diff --git a/pkg/handlers/controllers.go b/pkg/handlers/controllers.go index ec28fa8..a816888 100644 --- a/pkg/handlers/controllers.go +++ b/pkg/handlers/controllers.go @@ -2,6 +2,7 @@ package handlers import ( "context" + "github.com/port-labs/port-k8s-exporter/pkg/port/integration" "time" "github.com/port-labs/port-k8s-exporter/pkg/crd" @@ -74,8 +75,24 @@ func (c *ControllersHandler) Handle() { klog.Fatalf("Error while waiting for informer cache sync: %s", err.Error()) } } + + currentEntitiesSet := make([]map[string]interface{}, 0) + for _, controller := range c.controllers { + controllerEntitiesSet, rawDataExamples, err := controller.GetEntitiesSet() + if err != nil { + klog.Errorf("error getting controller entities set: %s", err.Error()) + } + currentEntitiesSet = append(currentEntitiesSet, controllerEntitiesSet) + if len(rawDataExamples) > 0 { + err = integration.PostIntegrationKindExample(c.portClient, c.stateKey, controller.Resource.Kind, rawDataExamples) + if err != nil { + klog.Warningf("failed to post integration kind example: %s", err.Error()) + } + } + } + klog.Info("Deleting stale entities") - c.RunDeleteStaleEntities() + c.RunDeleteStaleEntities(currentEntitiesSet) klog.Info("Starting controllers") for _, controller := range c.controllers { controller.Run(1, c.stopCh) @@ -91,16 +108,7 @@ func (c *ControllersHandler) Handle() { }() } -func (c *ControllersHandler) RunDeleteStaleEntities() { - currentEntitiesSet := make([]map[string]interface{}, 0) - for _, controller := range c.controllers { - controllerEntitiesSet, err := controller.GetEntitiesSet() - if err != nil { - klog.Errorf("error getting controller entities set: %s", err.Error()) - } - currentEntitiesSet = append(currentEntitiesSet, controllerEntitiesSet) - } - +func (c *ControllersHandler) RunDeleteStaleEntities(currentEntitiesSet []map[string]interface{}) { _, err := c.portClient.Authenticate(context.Background(), c.portClient.ClientID, c.portClient.ClientSecret) if err != nil { klog.Errorf("error authenticating with Port: %v", err) diff --git a/pkg/k8s/controller.go b/pkg/k8s/controller.go index 2cc39e2..83dc022 100644 --- a/pkg/k8s/controller.go +++ b/pkg/k8s/controller.go @@ -28,10 +28,11 @@ import ( type EventActionType string const ( - CreateAction EventActionType = "create" - UpdateAction EventActionType = "update" - DeleteAction EventActionType = "delete" - MaxNumRequeues int = 4 + CreateAction EventActionType = "create" + UpdateAction EventActionType = "update" + DeleteAction EventActionType = "delete" + MaxNumRequeues int = 4 + MaxRawDataExamplesToSend = 5 ) type EventItem struct { @@ -40,20 +41,22 @@ type EventItem struct { } type Controller struct { - resource port.AggregatedResource - portClient *cli.PortClient - informer cache.SharedIndexInformer - lister cache.GenericLister - workqueue workqueue.RateLimitingInterface + Resource port.AggregatedResource + portClient *cli.PortClient + integrationConfig *port.IntegrationAppConfig + informer cache.SharedIndexInformer + lister cache.GenericLister + workqueue workqueue.RateLimitingInterface } func NewController(resource port.AggregatedResource, portClient *cli.PortClient, informer informers.GenericInformer, integrationConfig *port.IntegrationAppConfig) *Controller { controller := &Controller{ - resource: resource, - portClient: portClient, - informer: informer.Informer(), - lister: informer.Lister(), - workqueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), + Resource: resource, + portClient: portClient, + integrationConfig: integrationConfig, + informer: informer.Informer(), + lister: informer.Lister(), + workqueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), } controller.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ @@ -96,13 +99,13 @@ func NewController(resource port.AggregatedResource, portClient *cli.PortClient, } func (c *Controller) Shutdown() { - klog.Infof("Shutting down controller for resource '%s'", c.resource.Kind) + klog.Infof("Shutting down controller for resource '%s'", c.Resource.Kind) c.workqueue.ShutDown() - klog.Infof("Closed controller for resource '%s'", c.resource.Kind) + klog.Infof("Closed controller for resource '%s'", c.Resource.Kind) } func (c *Controller) WaitForCacheSync(stopCh <-chan struct{}) error { - klog.Infof("Waiting for informer cache to sync for resource '%s'", c.resource.Kind) + klog.Infof("Waiting for informer cache to sync for resource '%s'", c.Resource.Kind) if ok := cache.WaitForCacheSync(stopCh, c.informer.HasSynced); !ok { return fmt.Errorf("failed to wait for caches to sync") } @@ -113,11 +116,11 @@ func (c *Controller) WaitForCacheSync(stopCh <-chan struct{}) error { func (c *Controller) Run(workers int, stopCh <-chan struct{}) { defer utilruntime.HandleCrash() - klog.Infof("Starting workers for resource '%s'", c.resource.Kind) + klog.Infof("Starting workers for resource '%s'", c.Resource.Kind) for i := 0; i < workers; i++ { go wait.Until(c.runWorker, time.Second, stopCh) } - klog.Infof("Started workers for resource '%s'", c.resource.Kind) + klog.Infof("Started workers for resource '%s'", c.Resource.Kind) } func (c *Controller) runWorker() { @@ -139,18 +142,18 @@ func (c *Controller) processNextWorkItem() bool { if !ok { c.workqueue.Forget(obj) - utilruntime.HandleError(fmt.Errorf("expected event item of resource '%s' in workqueue but got %#v", c.resource.Kind, obj)) + utilruntime.HandleError(fmt.Errorf("expected event item of resource '%s' in workqueue but got %#v", c.Resource.Kind, obj)) return nil } if err := c.syncHandler(item); err != nil { if c.workqueue.NumRequeues(obj) >= MaxNumRequeues { - utilruntime.HandleError(fmt.Errorf("error syncing '%s' of resource '%s': %s, give up after %d requeues", item.Key, c.resource.Kind, err.Error(), MaxNumRequeues)) + utilruntime.HandleError(fmt.Errorf("error syncing '%s' of resource '%s': %s, give up after %d requeues", item.Key, c.Resource.Kind, err.Error(), MaxNumRequeues)) return nil } c.workqueue.AddRateLimited(obj) - return fmt.Errorf("error syncing '%s' of resource '%s': %s, requeuing", item.Key, c.resource.Kind, err.Error()) + return fmt.Errorf("error syncing '%s' of resource '%s': %s, requeuing", item.Key, c.Resource.Kind, err.Error()) } c.workqueue.Forget(obj) @@ -190,8 +193,8 @@ func (c *Controller) objectHandler(obj interface{}, item EventItem) error { } errors := make([]error, 0) - for _, kindConfig := range c.resource.KindConfigs { - portEntities, err := c.getObjectEntities(obj, kindConfig.Selector, kindConfig.Port.Entity.Mappings, kindConfig.Port.ItemsToParse) + for _, kindConfig := range c.Resource.KindConfigs { + portEntities, _, err := c.getObjectEntities(obj, kindConfig.Selector, kindConfig.Port.Entity.Mappings, kindConfig.Port.ItemsToParse) if err != nil { utilruntime.HandleError(fmt.Errorf("error getting entities for object key '%s': %v", item.Key, err)) continue @@ -238,15 +241,15 @@ func mapEntities(obj interface{}, mappings []port.EntityMapping) ([]port.Entity, return entities, nil } -func (c *Controller) getObjectEntities(obj interface{}, selector port.Selector, mappings []port.EntityMapping, itemsToParse string) ([]port.Entity, error) { +func (c *Controller) getObjectEntities(obj interface{}, selector port.Selector, mappings []port.EntityMapping, itemsToParse string) ([]port.Entity, []interface{}, error) { unstructuredObj, ok := obj.(*unstructured.Unstructured) if !ok { - return nil, fmt.Errorf("error casting to unstructured") + return nil, nil, fmt.Errorf("error casting to unstructured") } var structuredObj interface{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredObj.Object, &structuredObj) if err != nil { - return nil, fmt.Errorf("error converting from unstructured: %v", err) + return nil, nil, fmt.Errorf("error converting from unstructured: %v", err) } entities := make([]port.Entity, 0, len(mappings)) @@ -257,12 +260,12 @@ func (c *Controller) getObjectEntities(obj interface{}, selector port.Selector, } else { items, parseItemsError := jq.ParseArray(itemsToParse, structuredObj) if parseItemsError != nil { - return nil, parseItemsError + return nil, nil, parseItemsError } mappedObject, ok := structuredObj.(map[string]interface{}) if !ok { - return nil, fmt.Errorf("error parsing object '%#v'", structuredObj) + return nil, nil, fmt.Errorf("error parsing object '%#v'", structuredObj) } for _, item := range items { @@ -275,24 +278,28 @@ func (c *Controller) getObjectEntities(obj interface{}, selector port.Selector, } } + rawDataExamples := make([]interface{}, 0) for _, objectToMap := range objectsToMap { selectorResult, err := isPassSelector(objectToMap, selector) if err != nil { - return nil, err + return nil, nil, err } if selectorResult { + if *c.integrationConfig.SendRawDataExamples && len(rawDataExamples) < MaxRawDataExamplesToSend { + rawDataExamples = append(rawDataExamples, objectToMap) + } currentEntities, err := mapEntities(objectToMap, mappings) if err != nil { - return nil, err + return nil, nil, err } entities = append(entities, currentEntities...) } } - return entities, nil + return entities, rawDataExamples, nil } func checkIfOwnEntity(entity port.Entity, portClient *cli.PortClient) (*bool, error) { @@ -362,15 +369,16 @@ func (c *Controller) entityHandler(portEntity port.Entity, action EventActionTyp return nil } -func (c *Controller) GetEntitiesSet() (map[string]interface{}, error) { +func (c *Controller) GetEntitiesSet() (map[string]interface{}, []interface{}, error) { k8sEntitiesSet := map[string]interface{}{} objects, err := c.lister.List(labels.Everything()) if err != nil { - return nil, fmt.Errorf("error listing K8s objects of resource '%s': %v", c.resource.Kind, err) + return nil, nil, fmt.Errorf("error listing K8s objects of resource '%s': %v", c.Resource.Kind, err) } + rawDataExamples := make([]interface{}, 0) for _, obj := range objects { - for _, kindConfig := range c.resource.KindConfigs { + for _, kindConfig := range c.Resource.KindConfigs { mappings := make([]port.EntityMapping, 0, len(kindConfig.Port.Entity.Mappings)) for _, m := range kindConfig.Port.Entity.Mappings { mappings = append(mappings, port.EntityMapping{ @@ -378,17 +386,18 @@ func (c *Controller) GetEntitiesSet() (map[string]interface{}, error) { Blueprint: m.Blueprint, }) } - entities, err := c.getObjectEntities(obj, kindConfig.Selector, mappings, kindConfig.Port.ItemsToParse) + entities, examples, err := c.getObjectEntities(obj, kindConfig.Selector, mappings, kindConfig.Port.ItemsToParse) if err != nil { - return nil, fmt.Errorf("error getting entities of object: %v", err) + return nil, nil, fmt.Errorf("error getting entities of object: %v", err) } for _, entity := range entities { k8sEntitiesSet[c.portClient.GetEntityIdentifierKey(&entity)] = nil } + rawDataExamples = append(rawDataExamples, examples[:min(len(examples), MaxRawDataExamplesToSend-len(rawDataExamples))]...) } } - return k8sEntitiesSet, nil + return k8sEntitiesSet, rawDataExamples, nil } func hashAllEntities(entities []port.Entity) (string, error) { @@ -411,13 +420,13 @@ func (c *Controller) shouldSendUpdateEvent(old interface{}, new interface{}, upd if updateEntityOnlyOnDiff == false { return true } - for _, kindConfig := range c.resource.KindConfigs { - oldEntities, err := c.getObjectEntities(old, kindConfig.Selector, kindConfig.Port.Entity.Mappings, kindConfig.Port.ItemsToParse) + for _, kindConfig := range c.Resource.KindConfigs { + oldEntities, _, err := c.getObjectEntities(old, kindConfig.Selector, kindConfig.Port.Entity.Mappings, kindConfig.Port.ItemsToParse) if err != nil { klog.Errorf("Error getting old entities: %v", err) return true } - newEntities, err := c.getObjectEntities(new, kindConfig.Selector, kindConfig.Port.Entity.Mappings, kindConfig.Port.ItemsToParse) + newEntities, _, err := c.getObjectEntities(new, kindConfig.Selector, kindConfig.Port.Entity.Mappings, kindConfig.Port.ItemsToParse) if err != nil { klog.Errorf("Error getting new entities: %v", err) return true diff --git a/pkg/k8s/controller_test.go b/pkg/k8s/controller_test.go index 9a9d996..4f56af9 100644 --- a/pkg/k8s/controller_test.go +++ b/pkg/k8s/controller_test.go @@ -36,33 +36,49 @@ type fixture struct { controller *Controller } -func newFixture(t *testing.T, portClientId string, portClientSecret string, userAgent string, resource port.Resource, objects []runtime.Object) *fixture { +type fixtureConfig struct { + portClientId string + portClientSecret string + userAgent string + sendRawDataExamples *bool + resource port.Resource + objects []runtime.Object +} + +func newFixture(t *testing.T, fixtureConfig *fixtureConfig) *fixture { + defaultTrue := true + sendRawDataExamples := &defaultTrue + if fixtureConfig.sendRawDataExamples != nil { + sendRawDataExamples = fixtureConfig.sendRawDataExamples + } + interationConfig := &port.IntegrationAppConfig{ DeleteDependents: true, CreateMissingRelatedEntities: true, - Resources: []port.Resource{resource}, + SendRawDataExamples: sendRawDataExamples, + Resources: []port.Resource{fixtureConfig.resource}, } kubeclient := k8sfake.NewSimpleDynamicClient(runtime.NewScheme()) - if portClientId == "" { - portClientId = config.ApplicationConfig.PortClientId + if fixtureConfig.portClientId == "" { + fixtureConfig.portClientId = config.ApplicationConfig.PortClientId } - if portClientSecret == "" { - portClientSecret = config.ApplicationConfig.PortClientSecret + if fixtureConfig.portClientSecret == "" { + fixtureConfig.portClientSecret = config.ApplicationConfig.PortClientSecret } - if userAgent == "" { - userAgent = "port-k8s-exporter/0.1" + if fixtureConfig.userAgent == "" { + fixtureConfig.userAgent = "port-k8s-exporter/0.1" } - portClient, err := cli.New(config.ApplicationConfig.PortBaseURL, cli.WithHeader("User-Agent", userAgent), - cli.WithClientID(portClientId), cli.WithClientSecret(portClientSecret)) + portClient, err := cli.New(config.ApplicationConfig.PortBaseURL, cli.WithHeader("User-Agent", fixtureConfig.userAgent), + cli.WithClientID(fixtureConfig.portClientId), cli.WithClientSecret(fixtureConfig.portClientSecret)) if err != nil { t.Errorf("Error building Port client: %s", err.Error()) } return &fixture{ t: t, - controller: newController(resource, objects, portClient, kubeclient, interationConfig), + controller: newController(fixtureConfig.resource, fixtureConfig.objects, portClient, kubeclient, interationConfig), } } @@ -177,8 +193,8 @@ func (f *fixture) runControllerSyncHandler(item EventItem, expectError bool) { } -func (f *fixture) runControllerGetEntitiesSet(expectedEntitiesSet map[string]interface{}, expectError bool) { - entitiesSet, err := f.controller.GetEntitiesSet() +func (f *fixture) runControllerGetEntitiesSet(expectedEntitiesSet map[string]interface{}, expectedExamples []interface{}, expectError bool) { + entitiesSet, examples, err := f.controller.GetEntitiesSet() if !expectError && err != nil { f.t.Errorf("error syncing item: %v", err) } else if expectError && err == nil { @@ -189,6 +205,11 @@ func (f *fixture) runControllerGetEntitiesSet(expectedEntitiesSet map[string]int if !eq { f.t.Errorf("expected entities set: %v, got: %v", expectedEntitiesSet, entitiesSet) } + + eq = reflect.DeepEqual(examples, expectedExamples) + if !eq { + f.t.Errorf("expected raw data examples: %v, got: %v", expectedExamples, examples) + } } func getKey(deployment *appsv1.Deployment, t *testing.T) string { @@ -223,7 +244,7 @@ func TestCreateDeployment(t *testing.T) { }) item := EventItem{Key: getKey(d, t), ActionType: CreateAction} - f := newFixture(t, "", "", "", resource, objects) + f := newFixture(t, &fixtureConfig{resource: resource, objects: objects}) f.runControllerSyncHandler(item, false) } @@ -302,7 +323,7 @@ func TestCreateDeploymentWithSearchRelation(t *testing.T) { }, }, }) - f := newFixture(t, "", "", "", resource, objects) + f := newFixture(t, &fixtureConfig{resource: resource, objects: objects}) f.runControllerSyncHandler(item, false) } @@ -329,7 +350,7 @@ func TestUpdateDeployment(t *testing.T) { }) item := EventItem{Key: getKey(d, t), ActionType: UpdateAction} - f := newFixture(t, "", "", "", resource, objects) + f := newFixture(t, &fixtureConfig{resource: resource, objects: objects}) f.runControllerSyncHandler(item, false) } @@ -345,7 +366,7 @@ func TestDeleteDeploymentSameOwner(t *testing.T) { createItem := EventItem{Key: getKey(d, t), ActionType: CreateAction} item := EventItem{Key: getKey(d, t), ActionType: DeleteAction} - f := newFixture(t, "", "", fmt.Sprintf("port-k8s-exporter/0.1 (statekey/%s)", config.ApplicationConfig.StateKey), resource, objects) + f := newFixture(t, &fixtureConfig{userAgent: fmt.Sprintf("port-k8s-exporter/0.1 (statekey/%s)", config.ApplicationConfig.StateKey), resource: resource, objects: objects}) f.runControllerSyncHandler(createItem, false) f.runControllerSyncHandler(item, false) @@ -367,7 +388,7 @@ func TestDeleteDeploymentDifferentOwner(t *testing.T) { createItem := EventItem{Key: getKey(d, t), ActionType: CreateAction} item := EventItem{Key: getKey(d, t), ActionType: DeleteAction} - f := newFixture(t, "", "", fmt.Sprintf("statekey/%s", "non_exist_statekey")+"port-k8s-exporter", resource, objects) + f := newFixture(t, &fixtureConfig{userAgent: fmt.Sprintf("statekey/%s", "non_exist_statekey") + "port-k8s-exporter", resource: resource, objects: objects}) f.runControllerSyncHandler(createItem, false) f.runControllerSyncHandler(item, false) @@ -388,7 +409,7 @@ func TestSelectorQueryFilterDeployment(t *testing.T) { }) item := EventItem{Key: getKey(d, t), ActionType: DeleteAction} - f := newFixture(t, "", "", "", resource, objects) + f := newFixture(t, &fixtureConfig{resource: resource, objects: objects}) f.runControllerSyncHandler(item, false) } @@ -403,7 +424,7 @@ func TestFailPortAuth(t *testing.T) { }) item := EventItem{Key: getKey(d, t), ActionType: CreateAction} - f := newFixture(t, "wrongclientid", "wrongclientsecret", "", resource, objects) + f := newFixture(t, &fixtureConfig{portClientId: "wrongclientid", portClientSecret: "wrongclientsecret", resource: resource, objects: objects}) f.runControllerSyncHandler(item, true) } @@ -418,13 +439,19 @@ func TestFailDeletePortEntity(t *testing.T) { }) item := EventItem{Key: getKey(d, t), ActionType: DeleteAction} - f := newFixture(t, "", "", "", resource, objects) + f := newFixture(t, &fixtureConfig{resource: resource, objects: objects}) f.runControllerSyncHandler(item, false) } func TestGetEntitiesSet(t *testing.T) { - d := newDeployment() - objects := []runtime.Object{newUnstructured(d)} + d := newUnstructured(newDeployment()) + var structuredObj interface{} + err := runtime.DefaultUnstructuredConverter.FromUnstructured(d.Object, &structuredObj) + if err != nil { + t.Errorf("Error from unstructured: %s", err.Error()) + } + + objects := []runtime.Object{d} resource := newResource("", []port.EntityMapping{ { Identifier: ".metadata.name", @@ -435,8 +462,12 @@ func TestGetEntitiesSet(t *testing.T) { "k8s-export-test-bp;port-k8s-exporter": nil, } - f := newFixture(t, "", "", "", resource, objects) - f.runControllerGetEntitiesSet(expectedEntitiesSet, false) + f := newFixture(t, &fixtureConfig{resource: resource, objects: objects}) + f.runControllerGetEntitiesSet(expectedEntitiesSet, []interface{}{structuredObj}, false) + + sendRawDataExamples := false + f = newFixture(t, &fixtureConfig{sendRawDataExamples: &sendRawDataExamples, resource: resource, objects: objects}) + f.runControllerGetEntitiesSet(expectedEntitiesSet, []interface{}{}, false) } func TestUpdateHandlerWithIndividualPropertyChanges(t *testing.T) { @@ -494,7 +525,7 @@ func TestUpdateHandlerWithIndividualPropertyChanges(t *testing.T) { for _, mapping := range fullMapping { - controllerWithFullMapping := newFixture(t, "", "", "", mapping, []runtime.Object{}).controller + controllerWithFullMapping := newFixture(t, &fixtureConfig{resource: mapping, objects: []runtime.Object{}}).controller // Test changes in each individual property properties := map[string]Property{ diff --git a/pkg/parsers/sensitive.go b/pkg/parsers/sensitive.go new file mode 100644 index 0000000..4e9f6a4 --- /dev/null +++ b/pkg/parsers/sensitive.go @@ -0,0 +1,64 @@ +package parsers + +import ( + "reflect" + "regexp" +) + +var secretPatterns = map[string]string{ + "Password in URL": `[a-zA-Z]{3,10}:\\/\\/[^\\/\\s:@]{3,20}:[^\\/\\s:@]{3,20}@.{1,100}["'\\s]`, + "Generic API Key": `[a|A][p|P][i|I][_]?k[e|E]y[Y].*[\'|"][0-9a-zA-Z]{32,45}[\'|"]`, + "Generic Secret": `[s|S][e|E][c|C][r|R][e|E][t|T].*[\'|"][0-9a-zA-Z]{32,45}[\'|"]`, + "Google API Key": `AIza[0-9A-Za-z\\-_]{35}`, + "Firebase URL": `.*firebaseio\\.com`, + "RSA private key": `-----BEGIN RSA PRIVATE KEY-----`, + "SSH (DSA) private key": `-----BEGIN DSA PRIVATE KEY-----`, + "SSH (EC) private key": `-----BEGIN EC PRIVATE KEY-----`, + "PGP private key block": `-----BEGIN PGP PRIVATE KEY BLOCK-----`, + "Amazon AWS Access Key ID": `AKIA[0-9A-Z]{16}`, + "Amazon MWS Auth Token": `amzn\\.mws\\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`, + "AWS API Key": `AKIA[0-9A-Z]{16}`, + "GitHub": `[g|G][i|I][t|T][h|H][u|U][b|B].*[\'|"][0-9a-zA-Z]{35,40}[\'|"]`, + "Google Cloud Platform API Key": `AIza[0-9A-Za-z\\-_]{35}`, + "Google Cloud Platform OAuth": `[0-9]+-[0-9A-Za-z_]{32}\\.apps\\.googleusercontent\\.com`, + "Google (GCP) Service-account": `"type": "service_account"`, + "Google OAuth Access Token": `ya29\\.[0-9A-Za-z\\-_]+`, + "Connection String": `[a-zA-Z]+:\\/\\/[^/\\s]+:[^/\\s]+@[^/\\s]+\\/[^/\\s]+`, +} + +var compiledPatterns []*regexp.Regexp + +func init() { + for _, pattern := range secretPatterns { + compiledPatterns = append(compiledPatterns, regexp.MustCompile(pattern)) + } +} + +func maskString(input string) string { + maskedString := input + for _, pattern := range compiledPatterns { + maskedString = pattern.ReplaceAllString(maskedString, "[REDACTED]") + } + return maskedString +} + +func ParseSensitiveData(data interface{}) interface{} { + switch v := reflect.ValueOf(data); v.Kind() { + case reflect.String: + return maskString(v.String()) + case reflect.Slice: + sliceCopy := reflect.MakeSlice(v.Type(), v.Len(), v.Len()) + for i := 0; i < v.Len(); i++ { + sliceCopy.Index(i).Set(reflect.ValueOf(ParseSensitiveData(v.Index(i).Interface()))) + } + return sliceCopy.Interface() + case reflect.Map: + mapCopy := reflect.MakeMap(v.Type()) + for _, key := range v.MapKeys() { + mapCopy.SetMapIndex(key, reflect.ValueOf(ParseSensitiveData(v.MapIndex(key).Interface()))) + } + return mapCopy.Interface() + default: + return data + } +} diff --git a/pkg/port/cli/integration.go b/pkg/port/cli/integration.go index 250ec18..3130a46 100644 --- a/pkg/port/cli/integration.go +++ b/pkg/port/cli/integration.go @@ -2,7 +2,9 @@ package cli import ( "fmt" + "github.com/port-labs/port-k8s-exporter/pkg/parsers" "github.com/port-labs/port-k8s-exporter/pkg/port" + "net/url" ) func parseIntegration(i *port.Integration) *port.Integration { @@ -77,3 +79,20 @@ func (c *PortClient) PatchIntegration(stateKey string, integration *port.Integra } return nil } + +func (c *PortClient) PostIntegrationKindExample(stateKey string, kind string, examples []interface{}) error { + pb := &port.ResponseBody{} + resp, err := c.Client.R(). + SetBody(map[string]interface{}{ + "examples": parsers.ParseSensitiveData(examples), + }). + SetResult(&pb). + Post(fmt.Sprintf("v1/integration/%s/kinds/%s/examples", url.QueryEscape(stateKey), url.QueryEscape(kind))) + if err != nil { + return err + } + if !pb.OK { + return fmt.Errorf("failed to post integration kind example, got: %s", resp.Body()) + } + return nil +} diff --git a/pkg/port/integration/integration.go b/pkg/port/integration/integration.go index 5af4ba1..63ab15e 100644 --- a/pkg/port/integration/integration.go +++ b/pkg/port/integration/integration.go @@ -40,6 +40,13 @@ func GetIntegration(portClient *cli.PortClient, stateKey string) (*port.Integrat return nil, fmt.Errorf("error getting Port integration: %v", err) } + if apiIntegration.Config != nil { + defaultTrue := true + if apiIntegration.Config.SendRawDataExamples == nil { + apiIntegration.Config.SendRawDataExamples = &defaultTrue + } + } + return apiIntegration, nil } @@ -68,3 +75,16 @@ func PatchIntegration(portClient *cli.PortClient, stateKey string, integration * } return nil } + +func PostIntegrationKindExample(portClient *cli.PortClient, stateKey string, kind string, examples []interface{}) error { + _, err := portClient.Authenticate(context.Background(), portClient.ClientID, portClient.ClientSecret) + if err != nil { + return fmt.Errorf("error authenticating with Port: %v", err) + } + + err = portClient.PostIntegrationKindExample(stateKey, kind, examples) + if err != nil { + return err + } + return nil +} diff --git a/pkg/port/models.go b/pkg/port/models.go index ab9bc4e..7ddfb0d 100644 --- a/pkg/port/models.go +++ b/pkg/port/models.go @@ -236,11 +236,11 @@ type EntityMappings struct { type Port struct { Entity EntityMappings `json:"entity" yaml:"entity"` - ItemsToParse string `json:"itemsToParse" yaml:"itemsToParse"` + ItemsToParse string `json:"itemsToParse,omitempty" yaml:"itemsToParse"` } type Selector struct { - Query string + Query string `json:"query,omitempty" yaml:"query"` } type Resource struct { @@ -270,6 +270,7 @@ type IntegrationAppConfig struct { CRDSToDiscover string `json:"crdsToDiscover,omitempty"` OverwriteCRDsActions bool `json:"overwriteCrdsActions,omitempty"` UpdateEntityOnlyOnDiff *bool `json:"updateEntityOnlyOnDiff,omitempty"` + SendRawDataExamples *bool `json:"sendRawDataExamples,omitempty"` } type Config struct {