Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port 6027 delete entities by owner #36

Merged
merged 5 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 52 additions & 3 deletions pkg/k8s/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package k8s
import (
"context"
"fmt"
"github.com/port-labs/port-k8s-exporter/pkg/config"
"github.com/port-labs/port-k8s-exporter/pkg/jq"
"github.com/port-labs/port-k8s-exporter/pkg/port"
"github.com/port-labs/port-k8s-exporter/pkg/port/cli"
Expand Down Expand Up @@ -239,6 +240,44 @@ func (c *Controller) getObjectEntities(obj interface{}, selector port.Selector,
return entities, nil
}

func checkIfOwnEntity(entity port.Entity, portClient *cli.PortClient) (*bool, error) {
portEntities, err := portClient.SearchEntities(context.Background(), port.SearchBody{
Rules: []port.Rule{
{
Property: "$datasource",
Operator: "contains",
Value: "port-k8s-exporter",
},
{
Property: "$identifier",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just forgot that you also must check the blueprint identifier, because entity identifier is not unique across blueprints

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Operator: "=",
Value: entity.Identifier,
},
{
Property: "$datasource",
Operator: "contains",
Value: fmt.Sprintf("statekey/%s", config.ApplicationConfig.StateKey),
},
{
Property: "$blueprint",
Operator: "=",
Value: entity.Blueprint,
},
},
Combinator: "and",
})
if err != nil {
return nil, err
}

if len(portEntities) > 0 {
result := true
return &result, nil
}
result := false
return &result, nil
}

func (c *Controller) entityHandler(portEntity port.Entity, action EventActionType) error {
switch action {
case CreateAction, UpdateAction:
Expand All @@ -248,11 +287,21 @@ func (c *Controller) entityHandler(portEntity port.Entity, action EventActionTyp
}
klog.V(0).Infof("Successfully upserted entity '%s' of blueprint '%s'", portEntity.Identifier, portEntity.Blueprint)
case DeleteAction:
err := c.portClient.DeleteEntity(context.Background(), portEntity.Identifier, portEntity.Blueprint, c.portClient.DeleteDependents)
result, err := checkIfOwnEntity(portEntity, c.portClient)
if err != nil {
return fmt.Errorf("error deleting Port entity '%s' of blueprint '%s': %v", portEntity.Identifier, portEntity.Blueprint, err)
return fmt.Errorf("error checking if entity '%s' of blueprint '%s' is owned by this exporter: %v", portEntity.Identifier, portEntity.Blueprint, err)
}

if *result {
err := c.portClient.DeleteEntity(context.Background(), portEntity.Identifier, portEntity.Blueprint, c.portClient.DeleteDependents)
if err != nil {
return fmt.Errorf("error deleting Port entity '%s' of blueprint '%s': %v", portEntity.Identifier, portEntity.Blueprint, err)
}
klog.V(0).Infof("Successfully deleted entity '%s' of blueprint '%s'", portEntity.Identifier, portEntity.Blueprint)
} else {
klog.Warningf("trying to delete entity but didn't find it in port with this exporter ownership, entity id: '%s', blueprint:'%s'", portEntity.Identifier, portEntity.Blueprint)
}
klog.V(0).Infof("Successfully deleted entity '%s' of blueprint '%s'", portEntity.Identifier, portEntity.Blueprint)

}

return nil
Expand Down
59 changes: 47 additions & 12 deletions pkg/k8s/controller_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package k8s

import (
"context"
"fmt"
"github.com/port-labs/port-k8s-exporter/pkg/config"
"github.com/port-labs/port-k8s-exporter/pkg/port"
"github.com/port-labs/port-k8s-exporter/pkg/port/cli"
Expand Down Expand Up @@ -30,7 +32,7 @@ type fixture struct {
controller *Controller
}

func newFixture(t *testing.T, portClientId string, portClientSecret string, resource port.Resource, objects []runtime.Object) *fixture {
func newFixture(t *testing.T, portClientId string, portClientSecret string, userAgent string, resource port.Resource, objects []runtime.Object) *fixture {
kubeclient := k8sfake.NewSimpleDynamicClient(runtime.NewScheme())

if portClientId == "" {
Expand All @@ -39,7 +41,11 @@ func newFixture(t *testing.T, portClientId string, portClientSecret string, reso
if portClientSecret == "" {
portClientSecret = config.ApplicationConfig.PortClientSecret
}
portClient, err := cli.New(config.ApplicationConfig.PortBaseURL, cli.WithHeader("User-Agent", "port-k8s-exporter/0.1"),
if userAgent == "" {
userAgent = "port-k8s-exporter/0.1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add also the stateKey and add a test for it

}

portClient, err := cli.New(config.ApplicationConfig.PortBaseURL, cli.WithHeader("User-Agent", userAgent),
cli.WithClientID(portClientId), cli.WithClientSecret(portClientSecret))
if err != nil {
t.Errorf("Error building Port client: %s", err.Error())
Expand Down Expand Up @@ -173,7 +179,7 @@ func TestCreateDeployment(t *testing.T) {
})
item := EventItem{Key: getKey(d, t), ActionType: CreateAction}

f := newFixture(t, "", "", resource, objects)
f := newFixture(t, "", "", "", resource, objects)
f.runControllerSyncHandler(item, false)
}

Expand All @@ -199,23 +205,52 @@ func TestUpdateDeployment(t *testing.T) {
})
item := EventItem{Key: getKey(d, t), ActionType: UpdateAction}

f := newFixture(t, "", "", resource, objects)
f := newFixture(t, "", "", "", resource, objects)
f.runControllerSyncHandler(item, false)
}

func TestDeleteDeployment(t *testing.T) {
func TestDeleteDeploymentSameOwner(t *testing.T) {
d := newDeployment()
objects := []runtime.Object{newUnstructured(d)}
resource := newResource("", []port.EntityMapping{
{
Identifier: ".metadata.name",
Identifier: "\"entityWithSameOwner\"",
Blueprint: "\"k8s-export-test-bp\"",
},
})
createItem := EventItem{Key: getKey(d, t), ActionType: CreateAction}
item := EventItem{Key: getKey(d, t), ActionType: DeleteAction}

f := newFixture(t, "", "", resource, objects)
f := newFixture(t, "", "", fmt.Sprintf("port-k8s-exporter/0.1 (statekey/%s)", config.ApplicationConfig.StateKey), resource, objects)
f.runControllerSyncHandler(createItem, false)

f.runControllerSyncHandler(item, false)
_, err := f.controller.portClient.ReadEntity(context.Background(), "entityWithSameOwner", "k8s-export-test-bp")
if !strings.Contains(err.Error(), "was not found") {
t.Errorf("expected entity to be deleted")
}
}

func TestDeleteDeploymentDifferentOwner(t *testing.T) {
d := newDeployment()
objects := []runtime.Object{newUnstructured(d)}
resource := newResource("", []port.EntityMapping{
{
Identifier: "\"entityWithDifferentOwner\"",
Blueprint: "\"k8s-export-test-bp\"",
},
})
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.runControllerSyncHandler(createItem, false)

f.runControllerSyncHandler(item, false)
_, err := f.controller.portClient.ReadEntity(context.Background(), "entityWithDifferentOwner", "k8s-export-test-bp")
if err != nil && strings.Contains(err.Error(), "was not found") {
t.Errorf("expected entity to exist")
}
}

func TestSelectorQueryFilterDeployment(t *testing.T) {
Expand All @@ -229,7 +264,7 @@ func TestSelectorQueryFilterDeployment(t *testing.T) {
})
item := EventItem{Key: getKey(d, t), ActionType: DeleteAction}

f := newFixture(t, "", "", resource, objects)
f := newFixture(t, "", "", "", resource, objects)
f.runControllerSyncHandler(item, false)
}

Expand All @@ -244,7 +279,7 @@ func TestFailPortAuth(t *testing.T) {
})
item := EventItem{Key: getKey(d, t), ActionType: CreateAction}

f := newFixture(t, "wrongclientid", "wrongclientsecret", resource, objects)
f := newFixture(t, "wrongclientid", "wrongclientsecret", "", resource, objects)
f.runControllerSyncHandler(item, true)
}

Expand All @@ -259,8 +294,8 @@ func TestFailDeletePortEntity(t *testing.T) {
})
item := EventItem{Key: getKey(d, t), ActionType: DeleteAction}

f := newFixture(t, "", "", resource, objects)
f.runControllerSyncHandler(item, true)
f := newFixture(t, "", "", "", resource, objects)
f.runControllerSyncHandler(item, false)
}

func TestGetEntitiesSet(t *testing.T) {
Expand All @@ -276,6 +311,6 @@ func TestGetEntitiesSet(t *testing.T) {
"k8s-export-test-bp;port-k8s-exporter": nil,
}

f := newFixture(t, "", "", resource, objects)
f := newFixture(t, "", "", "", resource, objects)
f.runControllerGetEntitiesSet(expectedEntitiesSet, false)
}