From 19d4736b26efe4acee94e654f50ed87613825ab7 Mon Sep 17 00:00:00 2001 From: Aditya Thebe Date: Tue, 5 Nov 2024 15:45:29 +0545 Subject: [PATCH] feat: add kubernetes connection in exec connections --- connection/environment.go | 61 +++++++++++++++++++++-------- connection/zz_generated.deepcopy.go | 5 +++ shell/shell.go | 8 +++- types/resource_selector.go | 1 + types/zz_generated.deepcopy.go | 21 ++++++++++ 5 files changed, 79 insertions(+), 17 deletions(-) diff --git a/connection/environment.go b/connection/environment.go index 2ae04195..b66b9931 100644 --- a/connection/environment.go +++ b/connection/environment.go @@ -9,15 +9,17 @@ import ( "github.com/flanksource/commons/logger" "github.com/flanksource/duty/context" + "github.com/flanksource/duty/types" textTemplate "text/template" ) // +kubebuilder:object:generate=true type ExecConnections struct { - AWS *AWSConnection `yaml:"aws,omitempty" json:"aws,omitempty"` - GCP *GCPConnection `yaml:"gcp,omitempty" json:"gcp,omitempty"` - Azure *AzureConnection `yaml:"azure,omitempty" json:"azure,omitempty"` + Kubernetes *types.EnvVar `yaml:"kubernetes,omitempty" json:"kubernetes,omitempty"` + AWS *AWSConnection `yaml:"aws,omitempty" json:"aws,omitempty"` + GCP *GCPConnection `yaml:"gcp,omitempty" json:"gcp,omitempty"` + Azure *AzureConnection `yaml:"azure,omitempty" json:"azure,omitempty"` } func saveConfig(configTemplate *textTemplate.Template, view any) (string, error) { @@ -43,8 +45,9 @@ func saveConfig(configTemplate *textTemplate.Template, view any) (string, error) } var ( - awsConfigTemplate *textTemplate.Template - gcloudConfigTemplate *textTemplate.Template + awsConfigTemplate *textTemplate.Template + kubernetesConfigTemplate *textTemplate.Template + gcloudConfigTemplate *textTemplate.Template ) func init() { @@ -55,20 +58,43 @@ aws_secret_access_key = {{.SecretKey.ValueStatic}} `)) gcloudConfigTemplate = textTemplate.Must(textTemplate.New("").Parse(`{{.Credentials}}`)) + + kubernetesConfigTemplate = textTemplate.Must(textTemplate.New("").Parse(`{{.ValueStatic}}`)) } // SetupCConnections creates the necessary credential files and injects env vars // into the cmd -func SetupConnection(ctx context.Context, connections ExecConnections, cmd *osExec.Cmd) error { +func SetupConnection(ctx context.Context, connections ExecConnections, cmd *osExec.Cmd) (func() error, error) { + var cleaner = func() error { + return nil + } + + if connections.Kubernetes != nil { + configPath, err := saveConfig(kubernetesConfigTemplate, connections.Kubernetes) + if err != nil { + return nil, fmt.Errorf("failed to store AWS credentials: %w", err) + } + + cleaner = func() error { + return os.RemoveAll(filepath.Dir(configPath)) + } + + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, fmt.Sprintf("KUBECONFIG=%s", configPath)) + } + if connections.AWS != nil { if err := connections.AWS.Populate(ctx); err != nil { - return fmt.Errorf("failed to hydrate aws connection: %w", err) + return nil, fmt.Errorf("failed to hydrate aws connection: %w", err) } configPath, err := saveConfig(awsConfigTemplate, connections.AWS) - defer os.RemoveAll(filepath.Dir(configPath)) if err != nil { - return fmt.Errorf("failed to store AWS credentials: %w", err) + return nil, fmt.Errorf("failed to store AWS credentials: %w", err) + } + + cleaner = func() error { + return os.RemoveAll(filepath.Dir(configPath)) } cmd.Env = os.Environ() @@ -81,37 +107,40 @@ func SetupConnection(ctx context.Context, connections ExecConnections, cmd *osEx if connections.Azure != nil { if err := connections.Azure.HydrateConnection(ctx); err != nil { - return fmt.Errorf("failed to hydrate connection %w", err) + return nil, fmt.Errorf("failed to hydrate connection %w", err) } // login with service principal runCmd := osExec.Command("az", "login", "--service-principal", "--username", connections.Azure.ClientID.ValueStatic, "--password", connections.Azure.ClientSecret.ValueStatic, "--tenant", connections.Azure.TenantID) if err := runCmd.Run(); err != nil { - return fmt.Errorf("failed to login: %w", err) + return nil, fmt.Errorf("failed to login: %w", err) } } if connections.GCP != nil { if err := connections.GCP.HydrateConnection(ctx); err != nil { - return fmt.Errorf("failed to hydrate connection %w", err) + return nil, fmt.Errorf("failed to hydrate connection %w", err) } configPath, err := saveConfig(gcloudConfigTemplate, connections.GCP) - defer os.RemoveAll(filepath.Dir(configPath)) if err != nil { - return fmt.Errorf("failed to store gcloud credentials: %w", err) + return nil, fmt.Errorf("failed to store gcloud credentials: %w", err) + } + + cleaner = func() error { + return os.RemoveAll(filepath.Dir(configPath)) } // to configure gcloud CLI to use the service account specified in GOOGLE_APPLICATION_CREDENTIALS, // we need to explicitly activate it runCmd := osExec.Command("gcloud", "auth", "activate-service-account", "--key-file", configPath) if err := runCmd.Run(); err != nil { - return fmt.Errorf("failed to activate GCP service account: %w", err) + return nil, fmt.Errorf("failed to activate GCP service account: %w", err) } cmd.Env = os.Environ() cmd.Env = append(cmd.Env, fmt.Sprintf("GOOGLE_APPLICATION_CREDENTIALS=%s", configPath)) } - return nil + return cleaner, nil } diff --git a/connection/zz_generated.deepcopy.go b/connection/zz_generated.deepcopy.go index d45d5f27..9ba9b553 100644 --- a/connection/zz_generated.deepcopy.go +++ b/connection/zz_generated.deepcopy.go @@ -54,6 +54,11 @@ func (in *AzureConnection) DeepCopy() *AzureConnection { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExecConnections) DeepCopyInto(out *ExecConnections) { *out = *in + if in.Kubernetes != nil { + in, out := &in.Kubernetes, &out.Kubernetes + *out = new(types.EnvVar) + (*in).DeepCopyInto(*out) + } if in.AWS != nil { in, out := &in.AWS, &out.AWS *out = new(AWSConnection) diff --git a/shell/shell.go b/shell/shell.go index 7b5ae3af..61cc18b8 100644 --- a/shell/shell.go +++ b/shell/shell.go @@ -107,8 +107,14 @@ func Run(ctx context.Context, exec Exec) (*ExecDetails, error) { cmd.Dir = envParams.mountPoint } - if err := connection.SetupConnection(ctx, exec.Connections, cmd); err != nil { + if cleanup, err := connection.SetupConnection(ctx, exec.Connections, cmd); err != nil { return nil, ctx.Oops().Wrap(err) + } else { + defer func() { + if err := cleanup(); err != nil { + logger.Errorf("failed to cleanup connection artifacts: %v", err) + } + }() } envParams.cmd = cmd diff --git a/types/resource_selector.go b/types/resource_selector.go index 8e1c90ad..b4b1bdd9 100644 --- a/types/resource_selector.go +++ b/types/resource_selector.go @@ -21,6 +21,7 @@ type ComponentConfigTraversalArgs struct { Direction string `yaml:"direction,omitempty" json:"direction,omitempty"` } +// +kubebuilder:object:generate=true type Functions struct { // It uses the config_id linked to the componentID to lookup up all the config relations and returns // a list of componentIDs that are linked to the found configIDs diff --git a/types/zz_generated.deepcopy.go b/types/zz_generated.deepcopy.go index 6a69e995..cdc84c39 100644 --- a/types/zz_generated.deepcopy.go +++ b/types/zz_generated.deepcopy.go @@ -172,6 +172,26 @@ func (in *EnvVarSource) DeepCopy() *EnvVarSource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Functions) DeepCopyInto(out *Functions) { + *out = *in + if in.ComponentConfigTraversal != nil { + in, out := &in.ComponentConfigTraversal, &out.ComponentConfigTraversal + *out = new(ComponentConfigTraversalArgs) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Functions. +func (in *Functions) DeepCopy() *Functions { + if in == nil { + return nil + } + out := new(Functions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPBasicAuth) DeepCopyInto(out *HTTPBasicAuth) { *out = *in @@ -324,6 +344,7 @@ func (in *Property) DeepCopy() *Property { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceSelector) DeepCopyInto(out *ResourceSelector) { *out = *in + in.Functions.DeepCopyInto(&out.Functions) if in.Types != nil { in, out := &in.Types, &out.Types *out = make(Items, len(*in))