diff --git a/.gitignore b/.gitignore index 9c731b6..d51d5c5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,10 @@ .idea +.vscode + +__debug_bin + config.yaml deployments/k8s diff --git a/main.go b/main.go index 9ca9965..f747939 100644 --- a/main.go +++ b/main.go @@ -3,13 +3,14 @@ package main import ( "flag" "fmt" + "os" + "github.com/port-labs/port-k8s-exporter/pkg/config" "github.com/port-labs/port-k8s-exporter/pkg/handlers" "github.com/port-labs/port-k8s-exporter/pkg/k8s" "github.com/port-labs/port-k8s-exporter/pkg/port/cli" "github.com/port-labs/port-k8s-exporter/pkg/signal" "k8s.io/klog/v2" - "os" ) var ( @@ -34,7 +35,17 @@ func main() { klog.Fatalf("Error building Port K8s Exporter config: %s", err.Error()) } - k8sClient, err := k8s.NewClient() + k8sConfig := k8s.NewKubeConfig() + if err != nil { + klog.Fatalf("Error building K8s config: %s", err.Error()) + } + + clientConfig, err := k8sConfig.ClientConfig() + if err != nil { + klog.Fatalf("Error getting K8s client config: %s", err.Error()) + } + + k8sClient, err := k8s.NewClient(clientConfig) if err != nil { klog.Fatalf("Error building K8s client: %s", err.Error()) } @@ -44,10 +55,13 @@ func main() { cli.WithClientID(portClientId), cli.WithClientSecret(portClientSecret), cli.WithDeleteDependents(deleteDependents), cli.WithCreateMissingRelatedEntities(createMissingRelatedEntities), ) + if err != nil { klog.Fatalf("Error building Port client: %s", err.Error()) } + k8s.NewIntegration(portClient, k8sConfig, stateKey) + klog.Info("Starting controllers handler") controllersHandler := handlers.NewControllersHandler(exporterConfig, k8sClient, portClient) controllersHandler.Handle(stopCh) diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go index 90fb23b..95ef6d1 100644 --- a/pkg/k8s/client.go +++ b/pkg/k8s/client.go @@ -6,7 +6,6 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" "k8s.io/client-go/restmapper" - "k8s.io/client-go/tools/clientcmd" ) type Client struct { @@ -15,28 +14,14 @@ type Client struct { DiscoveryMapper *restmapper.DeferredDiscoveryRESTMapper } -func getKubeConfig() (*rest.Config, error) { - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - configOverrides := &clientcmd.ConfigOverrides{} - config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides).ClientConfig() - if err != nil { - return nil, err - } - return config, nil -} - -func NewClient() (*Client, error) { - kubeConfig, err := getKubeConfig() - if err != nil { - return nil, err - } +func NewClient(config *rest.Config) (*Client, error) { - discoveryClient, err := discovery.NewDiscoveryClientForConfig(kubeConfig) + discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) if err != nil { return nil, err } - dynamicClient, err := dynamic.NewForConfig(kubeConfig) + dynamicClient, err := dynamic.NewForConfig(config) if err != nil { return nil, err } diff --git a/pkg/k8s/config.go b/pkg/k8s/config.go new file mode 100644 index 0000000..3e17cb7 --- /dev/null +++ b/pkg/k8s/config.go @@ -0,0 +1,9 @@ +package k8s + +import "k8s.io/client-go/tools/clientcmd" + +func NewKubeConfig() clientcmd.ClientConfig { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{} + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) +} diff --git a/pkg/k8s/integration.go b/pkg/k8s/integration.go new file mode 100644 index 0000000..22540a8 --- /dev/null +++ b/pkg/k8s/integration.go @@ -0,0 +1,35 @@ +package k8s + +import ( + "context" + + "github.com/port-labs/port-k8s-exporter/pkg/port" + "github.com/port-labs/port-k8s-exporter/pkg/port/cli" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/klog/v2" +) + +func NewIntegration(portClient *cli.PortClient, k8sConfig clientcmd.ClientConfig, stateKey string) { + + k8sRawConfig, err := k8sConfig.RawConfig() + if err != nil { + klog.Fatalf("Error getting K8s raw config: %s", err.Error()) + } + + clusterName := k8sRawConfig.Contexts[k8sRawConfig.CurrentContext].Cluster + + integration := &port.Integration{ + Title: clusterName, + InstallationAppType: "kubernetes", + InstallationId: stateKey, + } + _, err = portClient.Authenticate(context.Background(), portClient.ClientID, portClient.ClientSecret) + if err != nil { + klog.Errorf("error authenticating with Port: %v", err) + } + + _, err = portClient.CreateIntegration(integration) + if err != nil { + klog.Fatalf("Error creating Port integration: %s", err.Error()) + } +} diff --git a/pkg/port/cli/integration.go b/pkg/port/cli/integration.go new file mode 100644 index 0000000..df81acf --- /dev/null +++ b/pkg/port/cli/integration.go @@ -0,0 +1,23 @@ +package cli + +import ( + "fmt" + + "github.com/port-labs/port-k8s-exporter/pkg/port" +) + +func (c *PortClient) CreateIntegration(i *port.Integration) (*port.Integration, error) { + pb := &port.ResponseBody{} + resp, err := c.Client.R(). + SetBody(i). + SetResult(&pb). + SetQueryParam("upsert", "true"). + Post("v1/integration") + if err != nil { + return nil, err + } + if !pb.OK { + return nil, fmt.Errorf("failed to create integration, got: %s", resp.Body()) + } + return &pb.Integration, nil +} diff --git a/pkg/port/models.go b/pkg/port/models.go index 047af63..68c2a66 100644 --- a/pkg/port/models.go +++ b/pkg/port/models.go @@ -27,6 +27,13 @@ type ( Relations map[string]interface{} `json:"relations"` } + Integration struct { + InstallationId string `json:"installationId"` + Title string `json:"title,omitempty"` + Version string `json:"version,omitempty"` + InstallationAppType string `json:"installationAppType,omitempty"` + } + BlueprintProperty struct { Type string `json:"type,omitempty"` Title string `json:"title,omitempty"` @@ -115,11 +122,12 @@ type SearchBody struct { } type ResponseBody struct { - OK bool `json:"ok"` - Entity Entity `json:"entity"` - Blueprint Blueprint `json:"blueprint"` - Action Action `json:"action"` - Entities []Entity `json:"entities"` + OK bool `json:"ok"` + Entity Entity `json:"entity"` + Blueprint Blueprint `json:"blueprint"` + Action Action `json:"action"` + Entities []Entity `json:"entities"` + Integration Integration `json:"integration"` } type EntityMapping struct {