From 654e3b050d1e17aa9fb8486f342af65f240359f6 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Thu, 1 Aug 2024 17:16:29 +0200 Subject: [PATCH 01/39] #29 - restructuring code files, refactoring --- .../cluster_category.go => cmd/cluster.go | 98 +++++----- {commands => cmd}/commands.go | 92 ++++----- commands/config_category.go => cmd/config.go | 135 ++++++++------ .../container_registry.go | 14 +- {commands => cmd}/flags.go | 16 +- commands/k3d_category.go => cmd/k3d.go | 32 ++-- .../project_category.go => cmd/project.go | 74 ++++---- .../project_dependencies.go | 168 +++++------------ .../project_generation.go | 2 +- .../release_category.go => cmd/release.go | 56 +++--- cmd/rmk.go | 175 ++++++++++++++++++ commands/secret_category.go => cmd/secret.go | 100 +++++----- .../secret_generation.go | 6 +- commands/rmk_category.go | 89 --------- config/config.go | 22 +-- git_handler/git.go | 20 +- main.go | 8 +- .../aws_provider}/aws.go | 18 +- providers/azure_provider/azure.go | 1 + util/cli.go | 84 +++++++++ {system => util}/dictionary.go | 2 +- {go_getter => util}/getter.go | 2 +- {system => util}/system.go | 97 +--------- util/validate.go | 27 +++ 24 files changed, 690 insertions(+), 648 deletions(-) rename commands/cluster_category.go => cmd/cluster.go (81%) rename {commands => cmd}/commands.go (88%) rename commands/config_category.go => cmd/config.go (81%) rename commands/container_registry_category.go => cmd/container_registry.go (85%) rename {commands => cmd}/flags.go (97%) rename commands/k3d_category.go => cmd/k3d.go (78%) rename commands/project_category.go => cmd/project.go (77%) rename commands/download_packages.go => cmd/project_dependencies.go (75%) rename commands/project_generation_category.go => cmd/project_generation.go (99%) rename commands/release_category.go => cmd/release.go (92%) create mode 100644 cmd/rmk.go rename commands/secret_category.go => cmd/secret.go (74%) rename commands/secret_generation_category.go => cmd/secret_generation.go (96%) delete mode 100644 commands/rmk_category.go rename {aws_provider => providers/aws_provider}/aws.go (96%) create mode 100644 providers/azure_provider/azure.go create mode 100644 util/cli.go rename {system => util}/dictionary.go (99%) rename {go_getter => util}/getter.go (99%) rename {system => util}/system.go (73%) create mode 100644 util/validate.go diff --git a/commands/cluster_category.go b/cmd/cluster.go similarity index 81% rename from commands/cluster_category.go rename to cmd/cluster.go index 454ba39..80d35c4 100644 --- a/commands/cluster_category.go +++ b/cmd/cluster.go @@ -1,4 +1,4 @@ -package commands +package cmd import ( "fmt" @@ -10,7 +10,7 @@ import ( "go.uber.org/zap" "rmk/config" - "rmk/system" + "rmk/util" ) type StateRunner interface { @@ -22,7 +22,7 @@ type StateRunner interface { type ClusterCommands struct { Conf *config.Config Ctx *cli.Context - SpecCMDs []*system.SpecCMD + SpecCMDs []*util.SpecCMD PlanFile string WorkDir string } @@ -37,8 +37,8 @@ func (cc *ClusterCommands) clusterRootDir() (string, error) { return "", fmt.Errorf("destination path for cluster provider %s not found", cc.Conf.ClusterProvider) } -func (cc *ClusterCommands) awsEks() *system.SpecCMD { - return &system.SpecCMD{ +func (cc *ClusterCommands) awsEks() *util.SpecCMD { + return &util.SpecCMD{ Args: []string{"eks", "--region", cc.Conf.Region, "update-kubeconfig", @@ -93,7 +93,7 @@ func (cc *ClusterCommands) runBatchCMD() error { return nil } -func (cc *ClusterCommands) initialize() *system.SpecCMD { +func (cc *ClusterCommands) initialize() *util.SpecCMD { args := []string{ "init", "-backend=true", @@ -106,7 +106,7 @@ func (cc *ClusterCommands) initialize() *system.SpecCMD { } args = append(args, "-reconfigure") - return &system.SpecCMD{ + return &util.SpecCMD{ Args: args, Command: "terraform", Ctx: cc.Ctx.Context, @@ -115,8 +115,8 @@ func (cc *ClusterCommands) initialize() *system.SpecCMD { } } -func (cc *ClusterCommands) validate() *system.SpecCMD { - return &system.SpecCMD{ +func (cc *ClusterCommands) validate() *util.SpecCMD { + return &util.SpecCMD{ Args: []string{"validate"}, Command: "terraform", Ctx: cc.Ctx.Context, @@ -125,8 +125,8 @@ func (cc *ClusterCommands) validate() *system.SpecCMD { } } -func (cc *ClusterCommands) workspace(args ...string) *system.SpecCMD { - return &system.SpecCMD{ +func (cc *ClusterCommands) workspace(args ...string) *util.SpecCMD { + return &util.SpecCMD{ Args: append([]string{"workspace"}, args...), Command: "terraform", Ctx: cc.Ctx.Context, @@ -135,8 +135,8 @@ func (cc *ClusterCommands) workspace(args ...string) *system.SpecCMD { } } -func (cc *ClusterCommands) output(args ...string) *system.SpecCMD { - return &system.SpecCMD{ +func (cc *ClusterCommands) output(args ...string) *util.SpecCMD { + return &util.SpecCMD{ Args: append([]string{"output"}, args...), Command: "terraform", Ctx: cc.Ctx.Context, @@ -146,8 +146,8 @@ func (cc *ClusterCommands) output(args ...string) *system.SpecCMD { } } -func (cc *ClusterCommands) destroy() *system.SpecCMD { - return &system.SpecCMD{ +func (cc *ClusterCommands) destroy() *util.SpecCMD { + return &util.SpecCMD{ Args: []string{"destroy", "-auto-approve", "-var=aws_account_id=" + cc.Conf.AccountID, "-var=cloudflare_api_token=" + cc.Conf.CloudflareToken, @@ -164,8 +164,8 @@ func (cc *ClusterCommands) destroy() *system.SpecCMD { } } -func (cc *ClusterCommands) plan() *system.SpecCMD { - return &system.SpecCMD{ +func (cc *ClusterCommands) plan() *util.SpecCMD { + return &util.SpecCMD{ Args: []string{ "plan", "-out=" + cc.PlanFile, @@ -184,8 +184,8 @@ func (cc *ClusterCommands) plan() *system.SpecCMD { } } -func (cc *ClusterCommands) apply() *system.SpecCMD { - return &system.SpecCMD{ +func (cc *ClusterCommands) apply() *util.SpecCMD { + return &util.SpecCMD{ Args: []string{"apply", cc.PlanFile}, Command: "terraform", Ctx: cc.Ctx.Context, @@ -194,8 +194,8 @@ func (cc *ClusterCommands) apply() *system.SpecCMD { } } -func (cc *ClusterCommands) listResources() *system.SpecCMD { - return &system.SpecCMD{ +func (cc *ClusterCommands) listResources() *util.SpecCMD { + return &util.SpecCMD{ Args: []string{"state", "list"}, Command: "terraform", Ctx: cc.Ctx.Context, @@ -204,8 +204,8 @@ func (cc *ClusterCommands) listResources() *system.SpecCMD { } } -func (cc *ClusterCommands) refresh() *system.SpecCMD { - return &system.SpecCMD{ +func (cc *ClusterCommands) refresh() *util.SpecCMD { + return &util.SpecCMD{ Args: []string{"refresh", "-var=aws_account_id=" + cc.Conf.AccountID, "-var=cloudflare_api_token=" + cc.Conf.CloudflareToken, @@ -222,8 +222,8 @@ func (cc *ClusterCommands) refresh() *system.SpecCMD { } } -func (cc *ClusterCommands) state(args ...string) *system.SpecCMD { - return &system.SpecCMD{ +func (cc *ClusterCommands) state(args ...string) *util.SpecCMD { + return &util.SpecCMD{ Args: append([]string{"state"}, args...), Command: "terraform", Ctx: cc.Ctx.Context, @@ -251,9 +251,9 @@ func (cc *ClusterCommands) clusterDestroy() error { destroy := cc.destroy() - match, err := system.WalkMatch( - system.GetPwdPath(system.TenantValuesDIR, "clusters", system.AWSClusterProvider, cc.Conf.Environment), - "*."+system.TerraformVarsExt, + match, err := util.WalkMatch( + util.GetPwdPath(util.TenantValuesDIR, "clusters", util.AWSClusterProvider, cc.Conf.Environment), + "*."+util.TerraformVarsExt, ) if err != nil { return err @@ -267,7 +267,7 @@ func (cc *ClusterCommands) clusterDestroy() error { return err } - cc.SpecCMDs = append([]*system.SpecCMD{}, destroy, cc.workspace("select", "default"), + cc.SpecCMDs = append([]*util.SpecCMD{}, destroy, cc.workspace("select", "default"), cc.workspace("delete", cc.Conf.Name)) return cc.runBatchCMD() @@ -283,7 +283,7 @@ func (cc *ClusterCommands) clusterList() error { } func (cc *ClusterCommands) clusterProvision() error { - var workspace *system.SpecCMD + var workspace *util.SpecCMD if err := os.MkdirAll(filepath.Join(cc.WorkDir, "plans"), 0755); err != nil { zap.S().Fatal(err) @@ -302,9 +302,9 @@ func (cc *ClusterCommands) clusterProvision() error { plan := cc.plan() - match, err := system.WalkMatch( - system.GetPwdPath(system.TenantValuesDIR, "clusters", system.AWSClusterProvider, cc.Conf.Environment), - "*."+system.TerraformVarsExt, + match, err := util.WalkMatch( + util.GetPwdPath(util.TenantValuesDIR, "clusters", util.AWSClusterProvider, cc.Conf.Environment), + "*."+util.TerraformVarsExt, ) for _, val := range match { @@ -324,7 +324,7 @@ func (cc *ClusterCommands) clusterProvision() error { rc := &ReleaseCommands{ Conf: cc.Conf, Ctx: cc.Ctx, - WorkDir: system.GetPwdPath(""), + WorkDir: util.GetPwdPath(""), UpdateContext: true, } @@ -344,7 +344,7 @@ func (cc *ClusterCommands) clusterStateList() error { } func (cc *ClusterCommands) clusterStateRefresh() error { - var workspace *system.SpecCMD + var workspace *util.SpecCMD checkWorkspace, err := cc.Conf.BucketKeyExists("", cc.Conf.Terraform.BucketName, "env:/"+cc.Conf.Name+"/tf.tfstate") if err != nil { @@ -358,9 +358,9 @@ func (cc *ClusterCommands) clusterStateRefresh() error { } refresh := cc.refresh() - match, err := system.WalkMatch( - system.GetPwdPath(system.TenantValuesDIR, "clusters", system.AWSClusterProvider, cc.Conf.Environment), - "*."+system.TerraformVarsExt, + match, err := util.WalkMatch( + util.GetPwdPath(util.TenantValuesDIR, "clusters", util.AWSClusterProvider, cc.Conf.Environment), + "*."+util.TerraformVarsExt, ) if err != nil { return err @@ -377,11 +377,11 @@ func (cc *ClusterCommands) clusterStateRefresh() error { func clusterDestroyAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -408,11 +408,11 @@ func clusterDestroyAction(conf *config.Config) cli.ActionFunc { func clusterListAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -437,11 +437,11 @@ func clusterListAction(conf *config.Config) cli.ActionFunc { func clusterProvisionAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -476,11 +476,11 @@ func clusterProvisionAction(conf *config.Config) cli.ActionFunc { func clusterStateAction(conf *config.Config, action func(stateRunner StateRunner) error) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -505,11 +505,11 @@ func clusterStateAction(conf *config.Config, action func(stateRunner StateRunner func clusterSwitchAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -520,7 +520,7 @@ func clusterSwitchAction(conf *config.Config) cli.ActionFunc { rc := &ReleaseCommands{ Conf: conf, Ctx: c, - WorkDir: system.GetPwdPath(""), + WorkDir: util.GetPwdPath(""), UpdateContext: c.Bool("force"), } diff --git a/commands/commands.go b/cmd/commands.go similarity index 88% rename from commands/commands.go rename to cmd/commands.go index 5c7c1b9..cc1ccc0 100644 --- a/commands/commands.go +++ b/cmd/commands.go @@ -1,4 +1,4 @@ -package commands +package cmd import ( "sort" @@ -7,10 +7,10 @@ import ( "github.com/urfave/cli/v2/altsrc" "go.uber.org/zap" - "rmk/aws_provider" "rmk/config" "rmk/git_handler" - "rmk/system" + "rmk/providers/aws_provider" + "rmk/util" ) type Flags map[string][]cli.Flag @@ -57,7 +57,7 @@ func Commands() []*cli.Command { { Name: "zsh", Usage: "View Zsh completion scripts", - Description: system.CompletionZshDescription, + Description: util.CompletionZshDescription, Aliases: []string{"z"}, Category: "completion", Action: completionAction(), @@ -84,7 +84,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "config", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: configDeleteAction(conf), }, { @@ -93,7 +93,7 @@ func Commands() []*cli.Command { Aliases: []string{"l"}, Flags: flags["configList"], Category: "config", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: configListAction(conf, gitSpec), }, { @@ -103,7 +103,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "config", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: configViewAction(conf), }, }, @@ -124,7 +124,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["clusterCRLogin"]), Flags: flags["clusterCRLogin"], Category: "container-registry", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: containerRegistryAction(conf, DockerRunner.dockerLogin), }, { @@ -133,7 +133,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "container-registry", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: containerRegistryAction(conf, DockerRunner.dockerLogout), }, }, @@ -145,7 +145,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "cluster", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: clusterDestroyAction(conf), }, { @@ -155,7 +155,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "cluster", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: clusterListAction(conf), }, { @@ -171,7 +171,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["clusterK3DCreate"]), Flags: flags["clusterK3DCreate"], Category: "k3d", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: K3DCreateAction(conf), }, { @@ -181,7 +181,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "k3d", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: K3DAction(conf, K3DRunner.createDeleteK3DCluster), }, { @@ -191,7 +191,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["clusterK3DImport"]), Flags: flags["clusterK3DImport"], Category: "k3d", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: K3DAction(conf, K3DRunner.importImageToK3DCluster), }, { @@ -201,7 +201,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "k3d", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: K3DAction(conf, K3DRunner.listK3DClusters), }, { @@ -210,7 +210,7 @@ func Commands() []*cli.Command { Aliases: []string{"s"}, Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Category: "k3d", Action: K3DAction(conf, K3DRunner.startStopK3DCluster), }, @@ -220,7 +220,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "k3d", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: K3DAction(conf, K3DRunner.startStopK3DCluster), }, }, @@ -232,7 +232,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["clusterPlan"]), Flags: flags["clusterPlan"], Category: "cluster", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: clusterProvisionAction(conf), }, { @@ -248,7 +248,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["clusterStateDelete"]), Flags: flags["clusterStateDelete"], Category: "state", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: clusterStateAction(conf, StateRunner.clusterStateDelete), }, { @@ -258,7 +258,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "state", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: clusterStateAction(conf, StateRunner.clusterStateList), }, { @@ -268,7 +268,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "state", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: clusterStateAction(conf, StateRunner.clusterStateRefresh), }, }, @@ -280,7 +280,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["clusterSwitch"]), Flags: flags["clusterSwitch"], Category: "cluster", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: clusterSwitchAction(conf), }, }, @@ -309,7 +309,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["projectGenerate"]), Flags: flags["projectGenerate"], Category: "project", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: projectGenerateAction(conf, gitSpec), }, { @@ -319,7 +319,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["projectUpdate"]), Flags: flags["projectUpdate"], Category: "project", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: projectUpdateAction(conf, gitSpec), }, }, @@ -335,7 +335,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["releaseHelmfile"]), Flags: flags["releaseHelmfile"], Category: "release", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: releaseHelmfileAction(conf), }, { @@ -345,7 +345,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["releaseHelmfile"]), Flags: flags["releaseHelmfileWithOutput"], Category: "release", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: releaseHelmfileAction(conf), }, { @@ -355,7 +355,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["releaseHelmfileWithOutput"]), Flags: flags["releaseHelmfileWithOutput"], Category: "release", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: releaseHelmfileAction(conf), }, { @@ -365,7 +365,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["releaseRollback"]), Flags: flags["releaseRollback"], Category: "release", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: releaseRollbackAction(conf), }, { @@ -375,7 +375,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["releaseHelmfile"]), Flags: flags["releaseHelmfile"], Category: "release", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: releaseHelmfileAction(conf), }, { @@ -385,7 +385,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["releaseHelmfile"]), Flags: flags["releaseHelmfile"], Category: "release", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: releaseHelmfileAction(conf), }, { @@ -395,7 +395,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["releaseUpdate"]), Flags: flags["releaseUpdate"], Category: "release", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: releaseUpdateAction(conf, gitSpec), }, }, @@ -417,7 +417,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["secretManager"]), Flags: flags["secretManager"], Category: "manager", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: secretMgrEncryptDecryptAction(conf), }, { @@ -427,7 +427,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["secretManager"]), Flags: flags["secretManager"], Category: "manager", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: secretMgrEncryptDecryptAction(conf), }, { @@ -437,7 +437,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["secretGenerate"]), Flags: flags["secretGenerate"], Category: "manager", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: secretMgrGenerateAction(conf), }, }, @@ -455,7 +455,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "keys", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: secretKeysCreateAction(conf), }, { @@ -465,7 +465,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "keys", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: secretKeysDownloadAction(conf), }, { @@ -475,7 +475,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "keys", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: secretKeysUploadAction(conf), }, }, @@ -487,7 +487,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "secret", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: secretAction(conf, SecretRunner.helmSecretsEncrypt), }, { @@ -497,7 +497,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "secret", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: secretAction(conf, SecretRunner.helmSecretsDecrypt), }, { @@ -507,7 +507,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "secret", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: secretAction(conf, SecretRunner.helmSecretsView), }, { @@ -516,7 +516,7 @@ func Commands() []*cli.Command { Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], Category: "secret", - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: secretAction(conf, SecretRunner.helmSecretsEdit), }, }, @@ -525,7 +525,7 @@ func Commands() []*cli.Command { Name: "update", Usage: "Update RMK CLI to a new version", Flags: flags["update"], - BashComplete: system.ShellCompleteCustomOutput, + BashComplete: util.ShellCompleteCustomOutput, Action: updateAction(), }, } @@ -537,7 +537,7 @@ func initInputSourceWithContext(gitSpec *git_handler.GitSpec, flags []cli.Flag) return err } - return inputSourceContext(ctx, flags, system.GetHomePath(system.RMKDir, system.RMKConfig, gitSpec.ID+".yaml")) + return inputSourceContext(ctx, flags, util.GetHomePath(util.RMKDir, util.RMKConfig, gitSpec.ID+".yaml")) } } @@ -547,9 +547,9 @@ func readInputSourceWithContext(gitSpec *git_handler.GitSpec, conf *config.Confi return err } - configPath := system.GetHomePath(system.RMKDir, system.RMKConfig, gitSpec.ID+".yaml") + configPath := util.GetHomePath(util.RMKDir, util.RMKConfig, gitSpec.ID+".yaml") if err := conf.ReadConfigFile(configPath); err != nil { - zap.S().Errorf(system.ConfigNotInitializedErrorText) + zap.S().Errorf(util.ConfigNotInitializedErrorText) return err } @@ -568,7 +568,7 @@ func inputSourceContext(ctx *cli.Context, flags []cli.Flag, configPath string) e return nil, err } - if system.IsExists(configPath, true) { + if util.IsExists(configPath, true) { return altsrc.NewYamlSourceFromFile(configPath) } else { return &altsrc.MapInputSource{}, nil diff --git a/commands/config_category.go b/cmd/config.go similarity index 81% rename from commands/config_category.go rename to cmd/config.go index fd6b22c..2aba9ab 100644 --- a/commands/config_category.go +++ b/cmd/config.go @@ -1,9 +1,10 @@ -package commands +package cmd import ( "encoding/json" "fmt" "os" + "regexp" "strconv" "strings" "time" @@ -14,7 +15,7 @@ import ( "rmk/config" "rmk/git_handler" - "rmk/system" + "rmk/util" ) type ConfigCommands struct { @@ -25,8 +26,8 @@ func newConfigCommands(conf *config.Config, ctx *cli.Context, workDir string) *C return &ConfigCommands{&ReleaseCommands{Conf: conf, Ctx: ctx, WorkDir: workDir}} } -func (c *ConfigCommands) awsConfigure(profile string) *system.SpecCMD { - return &system.SpecCMD{ +func (c *ConfigCommands) awsConfigure(profile string) *util.SpecCMD { + return &util.SpecCMD{ Args: []string{"configure", "--profile", profile}, Envs: []string{ "AWS_CONFIG_FILE=" + strings.Join(c.Conf.AWSSharedConfigFile(profile), ""), @@ -39,8 +40,8 @@ func (c *ConfigCommands) awsConfigure(profile string) *system.SpecCMD { } } -func (c *ConfigCommands) helmPlugin() *system.SpecCMD { - return &system.SpecCMD{ +func (c *ConfigCommands) helmPlugin() *util.SpecCMD { + return &util.SpecCMD{ Args: []string{"plugin"}, Command: "helm", Dir: c.WorkDir, @@ -50,13 +51,13 @@ func (c *ConfigCommands) helmPlugin() *system.SpecCMD { } } -func (c *ConfigCommands) rmkConfigInit() *system.SpecCMD { +func (c *ConfigCommands) rmkConfigInit() *util.SpecCMD { exRMK, err := os.Executable() if err != nil { panic(err) } - return &system.SpecCMD{ + return &util.SpecCMD{ Args: []string{"config", "init"}, Command: exRMK, Dir: c.WorkDir, @@ -201,52 +202,64 @@ func (c *ConfigCommands) copyAWSProfile(profile string) error { return nil } -func (c *ConfigCommands) uninstallHelmPlugin(plugin config.Package) error { +func (c *ConfigCommands) installHelmPlugin(plugin config.Package, args ...string) error { c.SpecCMD = c.helmPlugin() - c.SpecCMD.Args = append(c.SpecCMD.Args, "list") - plSemVer, _ := semver.NewVersion(plugin.Version) - + c.SpecCMD.Args = append(c.SpecCMD.Args, args...) if err := runner(c).runCMD(); err != nil { - return fmt.Errorf("get Helm plugin list failed: %s", c.SpecCMD.StderrBuf.String()) + if !strings.Contains(c.SpecCMD.StderrBuf.String(), util.HelmPluginExist) { + return fmt.Errorf("Helm plugin %s installation failed: \n%s", plugin.Name, c.SpecCMD.StderrBuf.String()) + } } - for _, v := range strings.Split(c.SpecCMD.StdoutBuf.String(), "\n") { - if strings.Contains(v, plugin.Name) && !strings.Contains(v, plSemVer.String()) { - zap.S().Infof("Helm plugin %s detect new version %s from config", plugin.Name, plugin.Version) - c.SpecCMD = c.helmPlugin() - c.SpecCMD.Args = append(c.SpecCMD.Args, "uninstall", plugin.Name) - if err := runner(c).runCMD(); err != nil { - return fmt.Errorf("Helm plugin %s uninstallation failed: \n%s", - plugin.Name, c.SpecCMD.StderrBuf.String()) - } - - break - } + if !strings.Contains(c.SpecCMD.StderrBuf.String(), util.HelmPluginExist) { + zap.S().Infof("installing Helm plugin: %s", plugin.Name) } return nil } -func (c *ConfigCommands) installHelmPlugin(plugin config.Package, args ...string) error { +func (c *ConfigCommands) configHelmPlugins() error { + var ( + helmPluginsUpdate = make(map[string]*config.Package) + helmPluginsInstalled = make(map[string]*config.Package) + ) + c.SpecCMD = c.helmPlugin() - c.SpecCMD.Args = append(c.SpecCMD.Args, args...) + c.SpecCMD.Args = append(c.SpecCMD.Args, "list") + if err := runner(c).runCMD(); err != nil { - if !strings.Contains(c.SpecCMD.StderrBuf.String(), system.HelmPluginExist) { - return fmt.Errorf("Helm plugin %s installation failed: \n%s", plugin.Name, c.SpecCMD.StderrBuf.String()) - } + return fmt.Errorf("get Helm plugin list failed: %s", c.SpecCMD.StderrBuf.String()) } - if !strings.Contains(c.SpecCMD.StderrBuf.String(), system.HelmPluginExist) { - zap.S().Infof("installing Helm plugin: %s", plugin.Name) + for _, val := range strings.Split(c.SpecCMD.StdoutBuf.String(), "\n")[1:] { + reg, _ := regexp.Compile(`\s+`) + plugin := strings.Split(reg.ReplaceAllString(val, "|"), "|") + if len(plugin) > 1 { + helmPluginsInstalled[plugin[0]] = &config.Package{ + Name: plugin[0], + Version: plugin[1], + } + } } - return nil -} + for name, plugin := range c.Conf.HelmPlugins { + plSemVer, _ := semver.NewVersion(plugin.Version) + for _, pl := range helmPluginsInstalled { + plSV, _ := semver.NewVersion(pl.Version) + if pl.Name == plugin.Name && !plSemVer.Equal(plSV) { + helmPluginsUpdate[name] = plugin + break + } + } + } -func (c *ConfigCommands) configHelmPlugins() error { - for _, plugin := range c.Conf.HelmPlugins { - if err := c.uninstallHelmPlugin(*plugin); err != nil { - return err + for _, plugin := range helmPluginsUpdate { + zap.S().Infof("Helm plugin %s detect new version %s from %s", plugin.Name, plugin.Version, util.TenantProjectFile) + c.SpecCMD = c.helmPlugin() + c.SpecCMD.Args = append(c.SpecCMD.Args, "uninstall", plugin.Name) + if err := runner(c).runCMD(); err != nil { + return fmt.Errorf("Helm plugin %s uninstallation failed: \n%s", + plugin.Name, c.SpecCMD.StderrBuf.String()) } if err := c.installHelmPlugin(*plugin, "install", plugin.Url, "--version="+plugin.Version); err != nil { @@ -254,6 +267,14 @@ func (c *ConfigCommands) configHelmPlugins() error { } } + for name, plugin := range c.Conf.HelmPlugins { + if _, ok := helmPluginsInstalled[name]; !ok { + if err := c.installHelmPlugin(*plugin, "install", plugin.Url, "--version="+plugin.Version); err != nil { + return err + } + } + } + return nil } @@ -278,7 +299,7 @@ func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi } // Reconfigure regular AWS profile - if err := newConfigCommands(conf, c, system.GetPwdPath("")).configAws(); err != nil { + if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAws(); err != nil { return err } @@ -308,14 +329,14 @@ func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi profile = conf.Profile // Create new MFA profile - if err := newConfigCommands(conf, c, system.GetPwdPath("")).configAwsMFA(); err != nil { + if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAwsMFA(); err != nil { return err } } if ok, err := conf.AwsConfigure.GetAwsConfigure(profile); err != nil && ok { zap.S().Warnf("%s", err.Error()) - if err := newConfigCommands(conf, c, system.GetPwdPath("")).configAws(); err != nil { + if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAws(); err != nil { return err } @@ -323,11 +344,11 @@ func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi return err } - if err := newConfigCommands(conf, c, system.GetPwdPath("")).configAwsMFA(); err != nil { + if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAwsMFA(); err != nil { return err } } else if !c.Bool("aws-reconfigure") { - if err := newConfigCommands(conf, c, system.GetPwdPath("")).configAwsMFA(); err != nil { + if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAwsMFA(); err != nil { return err } } else if !ok && err != nil { @@ -339,7 +360,7 @@ func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_handler.GitSpec) error { if len(c.String("config-from-environment")) > 0 { - configPath := system.GetHomePath(system.RMKDir, system.RMKConfig, + configPath := util.GetHomePath(util.RMKDir, util.RMKConfig, gitSpec.RepoPrefixName+"-"+c.String("config-from-environment")+".yaml") if err := conf.ReadConfigFile(configPath); err != nil { @@ -369,7 +390,7 @@ func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_ // Copy MFA profile for current environment conf.AwsConfigure.Profile = regularProfile - if err := newConfigCommands(conf, c, system.GetPwdPath("")).copyAWSProfile(gitSpec.ID); err != nil { + if err := newConfigCommands(conf, c, util.GetPwdPath("")).copyAWSProfile(gitSpec.ID); err != nil { return err } } else { @@ -389,7 +410,7 @@ func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_ } // Copy regular profile for current environment - if err := newConfigCommands(conf, c, system.GetPwdPath("")).copyAWSProfile(gitSpec.ID); err != nil { + if err := newConfigCommands(conf, c, util.GetPwdPath("")).copyAWSProfile(gitSpec.ID); err != nil { return err } } @@ -410,11 +431,11 @@ func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_ return nil } - if err := system.ValidateArtifactModeDefault(c, "required parameter --github-token not set"); err != nil { + if err := util.ValidateArtifactModeDefault(c, "required parameter --github-token not set"); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -434,7 +455,7 @@ func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_ func configDeleteAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -498,7 +519,7 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act conf.ArtifactMode = c.String("artifact-mode") conf.ProgressBar = c.Bool("progress-bar") - conf.Terraform.BucketKey = system.TenantBucketKey + conf.Terraform.BucketKey = util.TenantBucketKey conf.ClusterProvisionerSL = c.Bool("cluster-provisioner-state-locking") conf.S3ChartsRepoRegion = c.String("s3-charts-repo-region") conf.ClusterProvider = c.String("cluster-provider") @@ -516,10 +537,10 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act //Formation of a unique bucket name, consisting of the prefix tenant of the repository, //constant and the first 3 and last 2 numbers AWS account id awsUID := conf.AccountID[0:3] + conf.AccountID[len(conf.AccountID)-2:] - conf.SopsAgeKeys = system.GetHomePath(system.RMKDir, system.SopsRootName, conf.Tenant+"-"+system.SopsRootName+"-"+awsUID) - conf.SopsBucketName = conf.Tenant + "-" + system.SopsRootName + "-" + awsUID - conf.Terraform.BucketName = conf.Tenant + "-" + system.TenantBucketName + "-" + awsUID - conf.Terraform.DDBTableName = system.TenantDDBTablePrefix + "-" + awsUID + conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+awsUID) + conf.SopsBucketName = conf.Tenant + "-" + util.SopsRootName + "-" + awsUID + conf.Terraform.BucketName = conf.Tenant + "-" + util.TenantBucketName + "-" + awsUID + conf.Terraform.DDBTableName = util.TenantDDBTablePrefix + "-" + awsUID if err := conf.InitConfig(true).SetRootDomain(c, gitSpec.ID); err != nil { return err @@ -529,7 +550,7 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act return err } - if conf.ClusterProvider == system.AWSClusterProvider { + if conf.ClusterProvider == util.AWSClusterProvider { if conf.ClusterProvisionerSL { // create dynamodb table for backend terraform if err := conf.CreateDynamoDBTable(conf.Terraform.DDBTableName); err != nil { @@ -560,7 +581,7 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act func configListAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -575,7 +596,7 @@ func configListAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act func configViewAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } diff --git a/commands/container_registry_category.go b/cmd/container_registry.go similarity index 85% rename from commands/container_registry_category.go rename to cmd/container_registry.go index 7e93127..143418a 100644 --- a/commands/container_registry_category.go +++ b/cmd/container_registry.go @@ -1,4 +1,4 @@ -package commands +package cmd import ( "fmt" @@ -8,7 +8,7 @@ import ( "go.uber.org/zap" "rmk/config" - "rmk/system" + "rmk/util" ) type DockerRunner interface { @@ -24,8 +24,8 @@ func newCRCommands(conf *config.Config, ctx *cli.Context, workDir string) *CRCom return &CRCommands{&ReleaseCommands{Conf: conf, Ctx: ctx, WorkDir: workDir}} } -func (cr *CRCommands) docker(args ...string) *system.SpecCMD { - return &system.SpecCMD{ +func (cr *CRCommands) docker(args ...string) *util.SpecCMD { + return &util.SpecCMD{ Args: append([]string{}, args...), Command: "docker", Dir: cr.WorkDir, @@ -78,14 +78,14 @@ func (cr *CRCommands) dockerLogout() error { func containerRegistryAction(conf *config.Config, action func(dockerRunner DockerRunner) error) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } - return action(newCRCommands(conf, c, system.GetPwdPath(""))) + return action(newCRCommands(conf, c, util.GetPwdPath(""))) } } diff --git a/commands/flags.go b/cmd/flags.go similarity index 97% rename from commands/flags.go rename to cmd/flags.go index e875706..08da2d7 100644 --- a/commands/flags.go +++ b/cmd/flags.go @@ -1,11 +1,11 @@ -package commands +package cmd import ( "github.com/urfave/cli/v2" "github.com/urfave/cli/v2/altsrc" "rmk/git_handler" - "rmk/system" + "rmk/util" ) func flagsConfig() []cli.Flag { @@ -16,7 +16,7 @@ func flagsConfig() []cli.Flag { Usage: "choice of artifact usage model, available: none, online, offline", Aliases: []string{"am"}, EnvVars: []string{"RMK_ARTIFACT_MODE"}, - Value: system.ArtifactModeDefault, + Value: util.ArtifactModeDefault, }, ), altsrc.NewStringFlag( @@ -25,7 +25,7 @@ func flagsConfig() []cli.Flag { Usage: "AWS ECR host", Aliases: []string{"aeh"}, EnvVars: []string{"RMK_AWS_ECR_HOST"}, - Value: system.AWSECRHost, + Value: util.AWSECRHost, }, ), altsrc.NewStringFlag( @@ -34,7 +34,7 @@ func flagsConfig() []cli.Flag { Usage: "AWS region for specific ECR host", Aliases: []string{"aer"}, EnvVars: []string{"RMK_AWS_ECR_REGION"}, - Value: system.AWSECRRegion, + Value: util.AWSECRRegion, }, ), altsrc.NewStringFlag( @@ -43,7 +43,7 @@ func flagsConfig() []cli.Flag { Usage: "AWS ECR user name", Aliases: []string{"aeun"}, EnvVars: []string{"RMK_AWS_ECR_USER_NAME"}, - Value: system.AWSECRUserName, + Value: util.AWSECRUserName, }, ), altsrc.NewStringFlag( @@ -131,7 +131,7 @@ func flagsConfig() []cli.Flag { Usage: "location constraint region of S3 charts repo", Aliases: []string{"scrr"}, EnvVars: []string{"RMK_S3_CHARTS_REPO_REGION"}, - Value: system.S3ChartsRepoRegion, + Value: util.S3ChartsRepoRegion, }, ), altsrc.NewStringFlag( @@ -140,7 +140,7 @@ func flagsConfig() []cli.Flag { Usage: "select cluster provider to provision clusters", Aliases: []string{"cp"}, EnvVars: []string{"RMK_CLUSTER_PROVIDER"}, - Value: system.AWSClusterProvider, + Value: util.AWSClusterProvider, }, ), altsrc.NewBoolFlag( diff --git a/commands/k3d_category.go b/cmd/k3d.go similarity index 78% rename from commands/k3d_category.go rename to cmd/k3d.go index 10a67ee..eb816ce 100644 --- a/commands/k3d_category.go +++ b/cmd/k3d.go @@ -1,4 +1,4 @@ -package commands +package cmd import ( "fmt" @@ -9,7 +9,7 @@ import ( "github.com/urfave/cli/v2" "rmk/config" - "rmk/system" + "rmk/util" ) type K3DRunner interface { @@ -27,8 +27,8 @@ func newK3DCommands(conf *config.Config, ctx *cli.Context, workDir string) *K3DC return &K3DCommands{&ReleaseCommands{Conf: conf, Ctx: ctx, WorkDir: workDir}} } -func (k *K3DCommands) k3d(args ...string) *system.SpecCMD { - return &system.SpecCMD{ +func (k *K3DCommands) k3d(args ...string) *util.SpecCMD { + return &util.SpecCMD{ Args: append([]string{}, args...), Command: "k3d", Dir: k.WorkDir, @@ -59,7 +59,7 @@ func (k *K3DCommands) prepareK3D(args ...string) error { return nil } - k.SpecCMD.Envs = append(k.SpecCMD.Envs, "K3D_VOLUME_HOST_PATH="+system.GetPwdPath("")) + k.SpecCMD.Envs = append(k.SpecCMD.Envs, "K3D_VOLUME_HOST_PATH="+util.GetPwdPath("")) return nil } @@ -73,23 +73,23 @@ func (k *K3DCommands) createDeleteK3DCluster() error { } for name, pkg := range k.Conf.Clusters { - if strings.HasPrefix(name, system.K3DConfigPrefix) { + if strings.HasPrefix(name, util.K3DConfigPrefix) { k3dDst = pkg.DstPath break } } if len(k3dDst) == 0 { - return fmt.Errorf("cluster provider with name %s not found", system.K3DConfigPrefix) + return fmt.Errorf("cluster provider with name %s not found", util.K3DConfigPrefix) } - match, err := system.WalkMatch(k3dDst, system.K3DConfigPrefix+".yaml") + match, err := util.WalkMatch(k3dDst, util.K3DConfigPrefix+".yaml") if err != nil { return err } if len(match) == 0 { - return fmt.Errorf("configuration file for %s not found", system.K3DConfigPrefix) + return fmt.Errorf("configuration file for %s not found", util.K3DConfigPrefix) } if err := k.prepareK3D("cluster", k.Ctx.Command.Name, "--config", match[0]); err != nil { @@ -97,7 +97,7 @@ func (k *K3DCommands) createDeleteK3DCluster() error { } // Creating specific dir for k3d registry configuration - k3dRegistryHostPath := filepath.Join(filepath.Dir(match[0]), system.K3DConfigPrefix) + k3dRegistryHostPath := filepath.Join(filepath.Dir(match[0]), util.K3DConfigPrefix) k.SpecCMD.Envs = append(k.SpecCMD.Envs, "K3D_REGISTRY_HOST_PATH="+k3dRegistryHostPath) if err := os.RemoveAll(k3dRegistryHostPath); err != nil { @@ -148,11 +148,11 @@ func (k *K3DCommands) startStopK3DCluster() error { func K3DCreateAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -160,20 +160,20 @@ func K3DCreateAction(conf *config.Config) cli.ActionFunc { return err } - return newK3DCommands(conf, c, system.GetPwdPath("")).createDeleteK3DCluster() + return newK3DCommands(conf, c, util.GetPwdPath("")).createDeleteK3DCluster() } } func K3DAction(conf *config.Config, action func(k3dRunner K3DRunner) error) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } - return action(newK3DCommands(conf, c, system.GetPwdPath(""))) + return action(newK3DCommands(conf, c, util.GetPwdPath(""))) } } diff --git a/commands/project_category.go b/cmd/project.go similarity index 77% rename from commands/project_category.go rename to cmd/project.go index 840cb69..cc13a93 100644 --- a/commands/project_category.go +++ b/cmd/project.go @@ -1,4 +1,4 @@ -package commands +package cmd import ( "bytes" @@ -18,7 +18,7 @@ import ( "rmk/config" "rmk/git_handler" "rmk/notification" - "rmk/system" + "rmk/util" ) type ProjectCommands struct { @@ -68,11 +68,11 @@ func newProjectCommand(conf *config.Config, ctx *cli.Context, workDir string) *P } func (p *ProjectCommands) readProjectFile() error { - if !system.IsExists(system.GetPwdPath(system.TenantProjectFile), true) { - return fmt.Errorf("%s file not found", system.GetPwdPath(system.TenantProjectFile)) + if !util.IsExists(util.GetPwdPath(util.TenantProjectFile), true) { + return fmt.Errorf("%s file not found", util.GetPwdPath(util.TenantProjectFile)) } - data, err := os.ReadFile(system.GetPwdPath(system.TenantProjectFile)) + data, err := os.ReadFile(util.GetPwdPath(util.TenantProjectFile)) if err != nil { return err } @@ -97,7 +97,7 @@ func (p *ProjectCommands) serializeProjectFile() ([]byte, error) { count++ zap.S().Infof("version changed for dependency %s, affected file: %s", pkg.Name, - system.GetPwdPath(system.TenantProjectFile)) + util.GetPwdPath(util.TenantProjectFile)) break } } @@ -137,13 +137,13 @@ func (p *ProjectCommands) updateProjectFile(gitSpec *git_handler.GitSpec) error } if data != nil { - if err := os.WriteFile(system.GetPwdPath(system.TenantProjectFile), data, 0644); err != nil { + if err := os.WriteFile(util.GetPwdPath(util.TenantProjectFile), data, 0644); err != nil { return err } if !p.Ctx.Bool("skip-commit") { if err := gitSpec.GitCommitPush( - system.GetPwdPath(system.TenantProjectFile), + util.GetPwdPath(util.TenantProjectFile), p.genMsgCommit(), p.Conf.GitHubToken); err != nil { return err } @@ -151,7 +151,7 @@ func (p *ProjectCommands) updateProjectFile(gitSpec *git_handler.GitSpec) error tmp := ¬ification.TmpUpdate{Config: p.Conf, Context: p.Ctx} tmp.ChangesList = append(tmp.ChangesList, p.Ctx.String("dependency")) - tmp.PathToFile = system.TenantProjectFile + tmp.PathToFile = util.TenantProjectFile if err := notification.SlackInit(tmp, notification.SlackTmp(tmp).TmpProjectUpdateMsg()).SlackDeclareNotify(); err != nil { return err @@ -162,7 +162,7 @@ func (p *ProjectCommands) updateProjectFile(gitSpec *git_handler.GitSpec) error } func (p *ProjectCommands) writeProjectFiles(path, data string) error { - if system.IsExists(path, true) { + if util.IsExists(path, true) { zap.S().Warnf("file %s already exists", path) return nil } @@ -188,7 +188,7 @@ func (p *ProjectCommands) generateReadme(gitSpec *git_handler.GitSpec) error { return err } - if err := p.writeProjectFiles(system.GetPwdPath(system.ReadmeFileName), readmeF); err != nil { + if err := p.writeProjectFiles(util.GetPwdPath(util.ReadmeFileName), readmeF); err != nil { return err } @@ -235,7 +235,7 @@ func (p *ProjectCommands) generateHelmfile() error { p.HelmfileParts = append(p.HelmfileParts, hReleases) - if err := p.writeProjectFiles(system.GetPwdPath(system.HelmfileGoTmplName), strings.Join(p.HelmfileParts, "\n")); err != nil { + if err := p.writeProjectFiles(util.GetPwdPath(util.HelmfileGoTmplName), strings.Join(p.HelmfileParts, "\n")); err != nil { return err } @@ -247,11 +247,11 @@ func (p *ProjectCommands) generateProjectFiles(gitSpec *git_handler.GitSpec) err for _, env := range sc.environments { switch sc.name { case "clusters": - if err := p.writeProjectFiles(filepath.Join(env.valuesPath, system.TerraformVarsFile), clusterVariables); err != nil { + if err := p.writeProjectFiles(filepath.Join(env.valuesPath, util.TerraformVarsFile), clusterVariables); err != nil { return err } - if err := p.writeProjectFiles(filepath.Join(env.valuesPath, system.TerraformWGFile), clusterWorkerGroups); err != nil { + if err := p.writeProjectFiles(filepath.Join(env.valuesPath, util.TerraformWGFile), clusterWorkerGroups); err != nil { return err } case p.TenantName: @@ -278,7 +278,7 @@ func (p *ProjectCommands) generateProjectFiles(gitSpec *git_handler.GitSpec) err return err } - if err := p.writeProjectFiles(filepath.Join(env.secretsPath, system.SecretSpecFile), tSecretSpec); err != nil { + if err := p.writeProjectFiles(filepath.Join(env.secretsPath, util.SecretSpecFile), tSecretSpec); err != nil { return err } @@ -294,24 +294,24 @@ func (p *ProjectCommands) generateProjectFiles(gitSpec *git_handler.GitSpec) err return err } - if err := p.writeProjectFiles(filepath.Join(env.secretsPath, system.SecretSpecFile), secretSpecFile); err != nil { + if err := p.writeProjectFiles(filepath.Join(env.secretsPath, util.SecretSpecFile), secretSpecFile); err != nil { return err } } if sc.name != "clusters" { - if err := p.writeProjectFiles(filepath.Join(env.secretsPath, system.SopsConfigFile), sopsConfigFile); err != nil { + if err := p.writeProjectFiles(filepath.Join(env.secretsPath, util.SopsConfigFile), sopsConfigFile); err != nil { return err } } } } - if err := p.writeProjectFiles(system.GetPwdPath(system.TenantProjectGitIgn), gitignore); err != nil { + if err := p.writeProjectFiles(util.GetPwdPath(util.TenantProjectGitIgn), gitignore); err != nil { return err } - if err := p.writeProjectFiles(system.GetPwdPath(system.TenantProjectCodeOwners), p.owners); err != nil { + if err := p.writeProjectFiles(util.GetPwdPath(util.TenantProjectCodeOwners), p.owners); err != nil { return err } @@ -342,16 +342,16 @@ func (p *ProjectCommands) generateProject(gitSpec *git_handler.GitSpec) error { } if reflect.ValueOf(p.projectFile.Spec).IsZero() { - return fmt.Errorf("'spec' option required in %s", system.TenantProjectFile) + return fmt.Errorf("'spec' option required in %s", util.TenantProjectFile) } switch { case len(p.projectFile.Spec.Scopes) == 0 && len(p.projectFile.Spec.Environments) > 0: - return fmt.Errorf("'scopes' option required, if 'environments' specified in %s", system.TenantProjectFile) + return fmt.Errorf("'scopes' option required, if 'environments' specified in %s", util.TenantProjectFile) case len(p.projectFile.Spec.Scopes) > 0 && len(p.projectFile.Spec.Environments) == 0: - return fmt.Errorf("'environments' option required, if 'scopes' specified in %s", system.TenantProjectFile) + return fmt.Errorf("'environments' option required, if 'scopes' specified in %s", util.TenantProjectFile) case len(p.projectFile.Spec.Scopes) == 0 && len(p.projectFile.Spec.Environments) == 0: - return fmt.Errorf("'scopes', 'environments' options required in %s", system.TenantProjectFile) + return fmt.Errorf("'scopes', 'environments' options required in %s", util.TenantProjectFile) } for sKey, sc := range p.projectFile.Spec.Scopes { @@ -360,18 +360,18 @@ func (p *ProjectCommands) generateProject(gitSpec *git_handler.GitSpec) error { for _, env := range p.projectFile.Spec.Environments { if sc == "clusters" { p.scopes[sKey].environments[env] = &environment{ - secretsPath: system.GetPwdPath(system.TenantValuesDIR, sc, p.Conf.ClusterProvider, env, "secrets"), - valuesPath: system.GetPwdPath(system.TenantValuesDIR, sc, p.Conf.ClusterProvider, env, "values"), + secretsPath: util.GetPwdPath(util.TenantValuesDIR, sc, p.Conf.ClusterProvider, env, "secrets"), + valuesPath: util.GetPwdPath(util.TenantValuesDIR, sc, p.Conf.ClusterProvider, env, "values"), } continue } p.scopes[sKey].environments[env] = &environment{ - globalsPath: system.GetPwdPath(system.TenantValuesDIR, sc, env, system.GlobalsFileName), - releasesPath: system.GetPwdPath(system.TenantValuesDIR, sc, env, system.ReleasesFileName), - secretsPath: system.GetPwdPath(system.TenantValuesDIR, sc, env, "secrets"), - valuesPath: system.GetPwdPath(system.TenantValuesDIR, sc, env, "values"), + globalsPath: util.GetPwdPath(util.TenantValuesDIR, sc, env, util.GlobalsFileName), + releasesPath: util.GetPwdPath(util.TenantValuesDIR, sc, env, util.ReleasesFileName), + secretsPath: util.GetPwdPath(util.TenantValuesDIR, sc, env, "secrets"), + valuesPath: util.GetPwdPath(util.TenantValuesDIR, sc, env, "values"), } } } @@ -388,7 +388,7 @@ func (p *ProjectCommands) generateProject(gitSpec *git_handler.GitSpec) error { } } - if err := os.MkdirAll(system.GetPwdPath("docs"), 0755); err != nil { + if err := os.MkdirAll(util.GetPwdPath("docs"), 0755); err != nil { return err } @@ -397,7 +397,7 @@ func (p *ProjectCommands) generateProject(gitSpec *git_handler.GitSpec) error { } if p.Ctx.Bool("create-sops-age-keys") { - if err := newSecretCommands(p.Conf, p.Ctx, system.GetPwdPath()).CreateKeys(); err != nil { + if err := newSecretCommands(p.Conf, p.Ctx, util.GetPwdPath()).CreateKeys(); err != nil { return err } } @@ -407,15 +407,15 @@ func (p *ProjectCommands) generateProject(gitSpec *git_handler.GitSpec) error { func projectGenerateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } - if err := newProjectCommand(conf, c, system.GetPwdPath()).generateProject(gitSpec); err != nil { + if err := newProjectCommand(conf, c, util.GetPwdPath()).generateProject(gitSpec); err != nil { return err } @@ -425,14 +425,14 @@ func projectGenerateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cl func projectUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } - return newProjectCommand(conf, c, system.GetPwdPath()).updateProjectFile(gitSpec) + return newProjectCommand(conf, c, util.GetPwdPath()).updateProjectFile(gitSpec) } } diff --git a/commands/download_packages.go b/cmd/project_dependencies.go similarity index 75% rename from commands/download_packages.go rename to cmd/project_dependencies.go index 3c253a4..d3a77e5 100644 --- a/commands/download_packages.go +++ b/cmd/project_dependencies.go @@ -1,25 +1,19 @@ -package commands +package cmd import ( - "encoding/json" "fmt" "net/http" "net/url" "os" - "path" "path/filepath" "strings" - "syscall" - "time" "github.com/Masterminds/semver" "github.com/urfave/cli/v2" "go.uber.org/zap" - "rmk/aws_provider" "rmk/config" - "rmk/go_getter" - "rmk/system" + "rmk/util" ) const ( @@ -28,8 +22,8 @@ const ( ) var ( - TenantPrDependenciesDir = filepath.Join(system.TenantProjectDIR, "dependencies") - TenantPrInventoryDir = filepath.Join(system.TenantProjectDIR, "inventory") + TenantPrDependenciesDir = filepath.Join(util.TenantProjectDIR, "dependencies") + TenantPrInventoryDir = filepath.Join(util.TenantProjectDIR, "inventory") TenantPrInvClustersDir = filepath.Join(TenantPrInventoryDir, "clusters") TenantPrInvHooksDir = filepath.Join(TenantPrInventoryDir, "hooks") ) @@ -55,19 +49,6 @@ type ArtifactSpec struct { Url string } -type RMKArtifactMetadata struct { - ProjectName string `json:"project_name"` - Tag string `json:"tag"` - PreviousTag string `json:"previous_tag"` - Version string `json:"version"` - Commit string `json:"commit"` - Date time.Time `json:"date"` - Runtime struct { - Goos string `json:"goos"` - Goarch string `json:"goarch"` - } `json:"runtime"` -} - type InventoryState struct { clustersState map[string]struct{} helmPluginsState map[string]struct{} @@ -124,7 +105,7 @@ func (s *SpecDownload) download(silent bool) error { } return s.downloadErrorHandler( - go_getter.DownloadArtifact(s.PkgUrl, s.PkgDst, s.PkgName, s.Header, silent, s.Conf.ProgressBar, s.Ctx.Context), + util.DownloadArtifact(s.PkgUrl, s.PkgDst, s.PkgName, s.Header, silent, s.Conf.ProgressBar, s.Ctx.Context), ) } @@ -207,7 +188,7 @@ func (s *SpecDownload) updateArtifact() error { s.Conf.AwsConfigure.Profile = licenseProfile if ok, err := s.Conf.GetAwsConfigure(licenseProfile); err != nil && ok { zap.S().Warnf("%s", err.Error()) - if err := newConfigCommands(s.Conf, s.Ctx, system.GetPwdPath("")).configAws(); err != nil { + if err := newConfigCommands(s.Conf, s.Ctx, util.GetPwdPath("")).configAws(); err != nil { return err } @@ -215,7 +196,7 @@ func (s *SpecDownload) updateArtifact() error { return err } } else if s.Ctx.Bool("aws-reconfigure-artifact-license") { - if err := newConfigCommands(s.Conf, s.Ctx, system.GetPwdPath("")).configAws(); err != nil { + if err := newConfigCommands(s.Conf, s.Ctx, util.GetPwdPath("")).configAws(); err != nil { return err } } @@ -228,33 +209,33 @@ func (s *SpecDownload) updateArtifact() error { return err } - if err := s.Conf.DownloadFromBucket(s.Artifact.Region, s.Artifact.BucketName, system.GetPwdPath(system.ArtifactDownloadDir), s.Artifact.Key); err != nil { + if err := s.Conf.DownloadFromBucket(s.Artifact.Region, s.Artifact.BucketName, util.GetPwdPath(util.ArtifactDownloadDir), s.Artifact.Key); err != nil { return err } s.Conf.AwsConfigure.Profile = currentProfile - if system.IsExists(s.PkgDst, false) { + if util.IsExists(s.PkgDst, false) { if err := os.MkdirAll(s.PkgDst, 0777); err != nil { return err } } - r, err := os.Open(filepath.Join(system.GetPwdPath(system.ArtifactDownloadDir), s.Artifact.Key)) + r, err := os.Open(filepath.Join(util.GetPwdPath(util.ArtifactDownloadDir), s.Artifact.Key)) if err != nil { return err } - if err := system.UnTar(s.PkgDst, "", r); err != nil { + if err := util.UnTar(s.PkgDst, "", r); err != nil { return err } - if system.IsExists(filepath.Join(s.PkgDst, system.TenantProjectDIR, "inventory"), false) { - if err := system.CopyDir(filepath.Join(s.PkgDst, system.TenantProjectDIR, "inventory"), system.GetPwdPath(system.TenantProjectDIR)); err != nil { + if util.IsExists(filepath.Join(s.PkgDst, util.TenantProjectDIR, "inventory"), false) { + if err := util.CopyDir(filepath.Join(s.PkgDst, util.TenantProjectDIR, "inventory"), util.GetPwdPath(util.TenantProjectDIR)); err != nil { return err } } - if err := os.RemoveAll(system.GetPwdPath(system.ArtifactDownloadDir)); err != nil { + if err := os.RemoveAll(util.GetPwdPath(util.ArtifactDownloadDir)); err != nil { return err } @@ -263,11 +244,11 @@ func (s *SpecDownload) updateArtifact() error { // depsGC - deleting old deps dirs with not actual versions func hooksGC(hooks []config.HookMapping) error { - if !system.IsExists(system.GetPwdPath(TenantPrInvHooksDir), false) { + if !util.IsExists(util.GetPwdPath(TenantPrInvHooksDir), false) { return nil } - allDirs, _, err := system.ListDir(system.GetPwdPath(TenantPrInvHooksDir), true) + allDirs, _, err := util.ListDir(util.GetPwdPath(TenantPrInvHooksDir), true) if err != nil { return err } @@ -464,8 +445,8 @@ func resolveDependencies(conf *config.Config, ctx *cli.Context, silent bool) err for _, val := range conf.Dependencies { projectFile := &config.ProjectFile{} - depsDir := system.FindDir(system.GetPwdPath(TenantPrDependenciesDir), val.Name) - if err := projectFile.ReadProjectFile(system.GetPwdPath(TenantPrDependenciesDir, depsDir, system.TenantProjectFile)); err != nil { + depsDir := util.FindDir(util.GetPwdPath(TenantPrDependenciesDir), val.Name) + if err := projectFile.ReadProjectFile(util.GetPwdPath(TenantPrDependenciesDir, depsDir, util.TenantProjectFile)); err != nil { return err } @@ -547,7 +528,7 @@ func resolveDependencies(conf *config.Config, ctx *cli.Context, silent bool) err return err } - if err := newConfigCommands(conf, ctx, system.GetPwdPath("")).configHelmPlugins(); err != nil { + if err := newConfigCommands(conf, ctx, util.GetPwdPath("")).configHelmPlugins(); err != nil { return err } @@ -559,11 +540,11 @@ func resolveDependencies(conf *config.Config, ctx *cli.Context, silent bool) err } func removeOldDir(pwd string, pkg config.Package) error { - if !system.IsExists(pwd, false) { + if !util.IsExists(pwd, false) { return nil } - oldDir := system.FindDir(pwd, pkg.Name) + oldDir := util.FindDir(pwd, pkg.Name) if len(strings.Split(oldDir, "-")) > 1 { oldVer := strings.SplitN(oldDir, "-", 2)[1] if oldVer != pkg.Version { @@ -580,23 +561,23 @@ func (s *SpecDownload) batchUpdate(pwd string, pkg config.Package, silent bool) s.PkgUrl = pkg.Url s.PkgName = pkg.Name + "-" + strings.ReplaceAll(pkg.Version, "/", "_") s.PkgDst = filepath.Join(s.PkgDst, s.PkgName) - pkgExists := system.IsExists(s.PkgDst, false) + pkgExists := util.IsExists(s.PkgDst, false) if !pkgExists { - if s.rmOldDir && s.Ctx.String("artifact-mode") != system.ArtifactModeOnline { + if s.rmOldDir && s.Ctx.String("artifact-mode") != util.ArtifactModeOnline { if err := removeOldDir(pwd, pkg); err != nil { return err } } switch { - case s.Ctx.String("artifact-mode") == system.ArtifactModeOnline && len(pkg.ArtifactUrl) > 0: + case s.Ctx.String("artifact-mode") == util.ArtifactModeOnline && len(pkg.ArtifactUrl) > 0: if err := s.updateArtifact(); err != nil { return err } - case s.Ctx.String("artifact-mode") == system.ArtifactModeOnline && len(pkg.ArtifactUrl) == 0: + case s.Ctx.String("artifact-mode") == util.ArtifactModeOnline && len(pkg.ArtifactUrl) == 0: zap.S().Warnf("overriding %s component in inventory section "+ "%s file is not allowed when using %s artifact mode", - s.PkgName, system.TenantProjectFile, system.ArtifactModeOnline) + s.PkgName, util.TenantProjectFile, util.ArtifactModeOnline) return nil default: if err := s.download(silent); err != nil { @@ -609,7 +590,7 @@ func (s *SpecDownload) batchUpdate(pwd string, pkg config.Package, silent bool) } func updateDependencies(conf *config.Config, ctx *cli.Context, silent bool) error { - pwd := system.GetPwdPath(TenantPrDependenciesDir) + pwd := util.GetPwdPath(TenantPrDependenciesDir) for key, val := range conf.Dependencies { spec := &SpecDownload{Conf: conf, Ctx: ctx, PkgDst: pwd, @@ -625,13 +606,13 @@ func updateDependencies(conf *config.Config, ctx *cli.Context, silent bool) erro } switch { - case system.IsExists(filepath.Join(spec.PkgDst, system.HelmfileFileName), true): - conf.Dependencies[key].DstPath = filepath.Join(spec.PkgDst, system.HelmfileFileName) - case system.IsExists(filepath.Join(spec.PkgDst, system.HelmfileGoTmplName), true): - conf.Dependencies[key].DstPath = filepath.Join(spec.PkgDst, system.HelmfileGoTmplName) + case util.IsExists(filepath.Join(spec.PkgDst, util.HelmfileFileName), true): + conf.Dependencies[key].DstPath = filepath.Join(spec.PkgDst, util.HelmfileFileName) + case util.IsExists(filepath.Join(spec.PkgDst, util.HelmfileGoTmplName), true): + conf.Dependencies[key].DstPath = filepath.Join(spec.PkgDst, util.HelmfileGoTmplName) default: return fmt.Errorf("%s or %s not found in dependent project %s", - system.HelmfileFileName, system.HelmfileGoTmplName, spec.PkgName) + util.HelmfileFileName, util.HelmfileGoTmplName, spec.PkgName) } } @@ -639,7 +620,7 @@ func updateDependencies(conf *config.Config, ctx *cli.Context, silent bool) erro } func updateClusters(conf *config.Config, ctx *cli.Context, silent bool) error { - pwd := system.GetPwdPath(TenantPrInvClustersDir) + pwd := util.GetPwdPath(TenantPrInvClustersDir) for key, val := range conf.Clusters { spec := &SpecDownload{Conf: conf, Ctx: ctx, PkgDst: pwd, Artifact: &ArtifactSpec{}, rmOldDir: true} @@ -654,7 +635,7 @@ func updateClusters(conf *config.Config, ctx *cli.Context, silent bool) error { } func updateHooks(conf *config.Config, ctx *cli.Context, silent bool) error { - pwd := system.GetPwdPath(TenantPrInvHooksDir) + pwd := util.GetPwdPath(TenantPrInvHooksDir) for key, val := range conf.HooksMapping { spec := &SpecDownload{Conf: conf, Ctx: ctx, PkgDst: pwd, Artifact: &ArtifactSpec{}} @@ -675,7 +656,7 @@ func match(dir string, patterns []string) ([]string, error) { ) for _, val := range patterns { - match, err := system.WalkMatch(dir, val) + match, err := util.WalkMatch(dir, val) if err != nil { return nil, err } @@ -703,7 +684,7 @@ func match(dir string, patterns []string) ([]string, error) { func overwriteFiles(path, pattern, name string) error { var data []byte - oldFilePath, err := system.WalkMatch(path, pattern) + oldFilePath, err := util.WalkMatch(path, pattern) if err != nil { return err } @@ -719,13 +700,13 @@ func updateTools(conf *config.Config, ctx *cli.Context, silent bool) error { spec := &SpecDownload{ Conf: conf, Ctx: ctx, - PkgDst: system.GetHomePath(system.RMKDir, system.RMKToolsDir, system.ToolsTmpDir), + PkgDst: util.GetHomePath(util.RMKDir, util.RMKToolsDir, util.ToolsTmpDir), Type: httpProvider, } - toolsVersionPath := system.GetHomePath(system.RMKDir, system.RMKToolsDir, system.ToolsVersionDir) - toolsTmpPath := system.GetHomePath(system.RMKDir, system.RMKToolsDir, system.ToolsTmpDir) - toolsBinPath := system.GetHomePath(".local", system.ToolsBinDir) + toolsVersionPath := util.GetHomePath(util.RMKDir, util.RMKToolsDir, util.ToolsVersionDir) + toolsTmpPath := util.GetHomePath(util.RMKDir, util.RMKToolsDir, util.ToolsTmpDir) + toolsBinPath := util.GetHomePath(".local", util.ToolsBinDir) if err := os.MkdirAll(toolsVersionPath, 0755); err != nil { return err @@ -740,7 +721,7 @@ func updateTools(conf *config.Config, ctx *cli.Context, silent bool) error { version, _ := semver.NewVersion(val.Version) spec.PkgUrl = val.Url spec.PkgName = val.Name + "-" + version.String() - if !system.IsExists(filepath.Join(toolsVersionPath, spec.PkgName), true) { + if !util.IsExists(filepath.Join(toolsVersionPath, spec.PkgName), true) { err := spec.download(silent) if err != nil { return err @@ -775,11 +756,11 @@ func updateTools(conf *config.Config, ctx *cli.Context, silent bool) error { for _, pkg := range conf.Tools { for _, pathArt := range pkg.Artifacts { if pkg.Rename { - if err := system.CopyFile(pathArt, filepath.Join(toolsBinPath, pkg.Name)); err != nil { + if err := util.CopyFile(pathArt, filepath.Join(toolsBinPath, pkg.Name)); err != nil { return err } } else { - if err := system.CopyFile(pathArt, filepath.Join(toolsBinPath, filepath.Base(pathArt))); err != nil { + if err := util.CopyFile(pathArt, filepath.Join(toolsBinPath, filepath.Base(pathArt))); err != nil { return err } } @@ -788,66 +769,3 @@ func updateTools(conf *config.Config, ctx *cli.Context, silent bool) error { return os.RemoveAll(toolsTmpPath) } - -func getRMKArtifactMetadata(keyPath string) (*RMKArtifactMetadata, error) { - rmkArtifactMetadata := &RMKArtifactMetadata{} - aws := &aws_provider.AwsConfigure{Region: system.RMKBucketRegion} - data, err := aws.GetFileData(system.RMKBucketName, system.RMKBin+"/"+keyPath+"/metadata.json") - if err != nil { - return nil, err - } - - if err := json.Unmarshal(data, &rmkArtifactMetadata); err != nil { - return nil, err - } - - return rmkArtifactMetadata, nil -} - -func rmkURLFormation(paths ...string) string { - u, err := url.Parse("https://" + system.RMKBucketName + ".s3." + system.RMKBucketRegion + ".amazonaws.com") - if err != nil { - zap.S().Fatal(err) - } - - p := append([]string{u.Path}, paths...) - u.Path = path.Join(p...) - return u.String() -} - -func updateRMK(pkgName, version string, silent, progressBar bool, ctx *cli.Context) error { - zap.S().Infof("starting package download: %s", pkgName) - pkgDst := system.GetHomePath(filepath.Join(".local", system.ToolsBinDir)) - if err := go_getter.DownloadArtifact( - rmkURLFormation(system.RMKBin, version, pkgName), - pkgDst, - pkgName, - &http.Header{}, - silent, - progressBar, - ctx.Context, - ); err != nil { - return err - } - - if err := os.Rename(filepath.Join(pkgDst, pkgName), filepath.Join(pkgDst, system.RMKBin)); err != nil { - return err - } - - if err := os.Chmod(filepath.Join(pkgDst, system.RMKBin), 0755); err != nil { - return err - } - - relPath := strings.ReplaceAll(system.RMKSymLinkPath, filepath.Base(system.RMKSymLinkPath), "") - if syscall.Access(relPath, uint32(2)) == nil { - if !system.IsExists(system.RMKSymLinkPath, true) { - return os.Symlink(filepath.Join(pkgDst, system.RMKBin), system.RMKSymLinkPath) - } - } else { - zap.S().Warnf("symlink was not created automatically due to permissions, "+ - "please complete installation by running command: \n"+ - "sudo ln -s %s %s", filepath.Join(pkgDst, system.RMKBin), system.RMKSymLinkPath) - } - - return nil -} diff --git a/commands/project_generation_category.go b/cmd/project_generation.go similarity index 99% rename from commands/project_generation_category.go rename to cmd/project_generation.go index 0b7ffee..30f6113 100644 --- a/commands/project_generation_category.go +++ b/cmd/project_generation.go @@ -1,4 +1,4 @@ -package commands +package cmd const ( clusterVariables = `# Kubernetes user list diff --git a/commands/release_category.go b/cmd/release.go similarity index 92% rename from commands/release_category.go rename to cmd/release.go index 79e3c08..4e9a7fc 100644 --- a/commands/release_category.go +++ b/cmd/release.go @@ -1,4 +1,4 @@ -package commands +package cmd import ( "bytes" @@ -20,7 +20,7 @@ import ( "rmk/config" "rmk/git_handler" "rmk/notification" - "rmk/system" + "rmk/util" ) type runner interface { @@ -30,7 +30,7 @@ type runner interface { type ReleaseCommands struct { Conf *config.Config Ctx *cli.Context - SpecCMD *system.SpecCMD + SpecCMD *util.SpecCMD Scope string WorkDir string ValuesPath string @@ -164,11 +164,11 @@ func (rc *ReleaseCommands) nestedHelmfiles(envs ...string) []string { return append(envs, hfVersion...) } -func (rc *ReleaseCommands) prepareHelmfile(args ...string) *system.SpecCMD { +func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { envs := append([]string{}, "NAME="+rc.Conf.Name, "TENANT="+rc.Conf.Tenant, - "SOPS_AGE_KEY_FILE="+filepath.Join(rc.Conf.SopsAgeKeys, system.SopsAgeKeyFile), + "SOPS_AGE_KEY_FILE="+filepath.Join(rc.Conf.SopsAgeKeys, util.SopsAgeKeyFile), "GITHUB_TOKEN="+rc.Conf.GitHubToken, "AWS_PROFILE="+rc.Conf.Profile, "AWS_CONFIG_FILE="+strings.Join(rc.Conf.AWSSharedConfigFile(rc.Conf.Profile), ""), @@ -203,11 +203,11 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *system.SpecCMD { // needed if not used artifact mode var sensKeyWords []string - if rc.Ctx.String("artifact-mode") == system.ArtifactModeDefault { + if rc.Ctx.String("artifact-mode") == util.ArtifactModeDefault { sensKeyWords = []string{rc.Conf.GitHubToken} } - return &system.SpecCMD{ + return &util.SpecCMD{ Args: append([]string{"--environment", rc.Conf.Environment, "--log-level", rc.Ctx.String("helmfile-log-level")}, args...), Command: "helmfile", @@ -219,8 +219,8 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *system.SpecCMD { } } -func (rc *ReleaseCommands) kubeConfig() *system.SpecCMD { - return &system.SpecCMD{ +func (rc *ReleaseCommands) kubeConfig() *util.SpecCMD { + return &util.SpecCMD{ Args: []string{"config"}, Command: "kubectl", Ctx: rc.Ctx.Context, @@ -231,20 +231,20 @@ func (rc *ReleaseCommands) kubeConfig() *system.SpecCMD { } func (rc *ReleaseCommands) releaseMiddleware() error { - if len(rc.Conf.Dependencies) == 0 && rc.Ctx.String("artifact-mode") == system.ArtifactModeDefault { + if len(rc.Conf.Dependencies) == 0 && rc.Ctx.String("artifact-mode") == util.ArtifactModeDefault { if err := os.RemoveAll(filepath.Join(rc.WorkDir, TenantPrDependenciesDir)); err != nil { return err } } - if err := system.MergeAgeKeys(rc.Conf.SopsAgeKeys); err != nil { + if err := util.MergeAgeKeys(rc.Conf.SopsAgeKeys); err != nil { return err } if _, currentContext, err := rc.getKubeContext(); err != nil { return err } else { - if strings.Contains(currentContext, system.K3DConfigPrefix) { + if strings.Contains(currentContext, util.K3DConfigPrefix) { rc.K3DCluster = true } } @@ -302,7 +302,7 @@ func (rc *ReleaseCommands) getKubeContext() (string, string, error) { contextName = "" } - if rc.K3DCluster && len(contextName) > 0 && !strings.Contains(contextName, system.K3DConfigPrefix) { + if rc.K3DCluster && len(contextName) > 0 && !strings.Contains(contextName, util.K3DConfigPrefix) { return "", "", fmt.Errorf("remote Kubernetes context already exists %s for this branch", contextName) } @@ -326,14 +326,14 @@ func (rc *ReleaseCommands) releaseKubeContext() error { return nil } - if strings.Contains(contextName, system.K3DConfigPrefix) && rc.UpdateContext { + if strings.Contains(contextName, util.K3DConfigPrefix) && rc.UpdateContext { return fmt.Errorf("current context %s already used for K3D cluster, --force flag cannot be used", contextName) } cc := &ClusterCommands{ Conf: rc.Conf, Ctx: rc.Ctx, - WorkDir: system.GetPwdPath(""), + WorkDir: util.GetPwdPath(""), } if err := cc.clusterContext(); err != nil { @@ -357,7 +357,7 @@ func (rc *ReleaseCommands) releaseKubeContext() error { } func (sr *SpecRelease) searchReleasesPath() error { - paths, err := system.WalkInDir(system.GetPwdPath(system.TenantValuesDIR), sr.Conf.Environment, system.ReleasesFileName) + paths, err := util.WalkInDir(util.GetPwdPath(util.TenantValuesDIR), sr.Conf.Environment, util.ReleasesFileName) if err != nil { return err } @@ -419,7 +419,7 @@ func (sr *SpecRelease) updateReleasesFile(g *git_handler.GitSpec) error { } if len(sr.ReleasesPaths) == 0 { - return fmt.Errorf("no files %s found", system.ReleasesFileName) + return fmt.Errorf("no files %s found", util.ReleasesFileName) } sr.Changes.List = make(map[string][]string) @@ -519,8 +519,8 @@ func (sr *SpecRelease) deployUpdatedReleases() error { return sr.runCMD() } -func (rc *ReleaseCommands) helmCommands(args ...string) *system.SpecCMD { - return &system.SpecCMD{ +func (rc *ReleaseCommands) helmCommands(args ...string) *util.SpecCMD { + return &util.SpecCMD{ Args: args, Command: "helm", Ctx: rc.Ctx.Context, @@ -650,11 +650,11 @@ func (sr *SpecRelease) checkStatusRelease() error { func releaseHelmfileAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -665,7 +665,7 @@ func releaseHelmfileAction(conf *config.Config) cli.ActionFunc { rc := &ReleaseCommands{ Conf: conf, Ctx: c, - WorkDir: system.GetPwdPath(""), + WorkDir: util.GetPwdPath(""), } if !c.Bool("skip-context-switch") { @@ -703,11 +703,11 @@ func releaseHelmfileAction(conf *config.Config) cli.ActionFunc { func releaseRollbackAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -721,7 +721,7 @@ func releaseRollbackAction(conf *config.Config) cli.ActionFunc { }{List: make(map[string][]string)}}} sr.Conf = conf sr.Ctx = c - sr.WorkDir = system.GetPwdPath("") + sr.WorkDir = util.GetPwdPath("") if !c.Bool("skip-context-switch") { if err := sr.releaseKubeContext(); err != nil { @@ -740,11 +740,11 @@ func releaseRollbackAction(conf *config.Config) cli.ActionFunc { func releaseUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -755,7 +755,7 @@ func releaseUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli. sr := &SpecRelease{} sr.Conf = conf sr.Ctx = c - sr.WorkDir = system.GetPwdPath("") + sr.WorkDir = util.GetPwdPath("") if !c.Bool("skip-context-switch") { if err := sr.releaseKubeContext(); err != nil { diff --git a/cmd/rmk.go b/cmd/rmk.go new file mode 100644 index 0000000..86016f7 --- /dev/null +++ b/cmd/rmk.go @@ -0,0 +1,175 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "os" + "path" + "path/filepath" + "strings" + "syscall" + "time" + + "github.com/Masterminds/semver" + "github.com/urfave/cli/v2" + "go.uber.org/zap" + + "rmk/providers/aws_provider" + "rmk/util" +) + +type RMKArtifactMetadata struct { + ProjectName string `json:"project_name"` + Tag string `json:"tag"` + PreviousTag string `json:"previous_tag"` + Version string `json:"version"` + Commit string `json:"commit"` + Date time.Time `json:"date"` + Runtime struct { + Goos string `json:"goos"` + Goarch string `json:"goarch"` + } `json:"runtime"` +} + +func completionAction() cli.ActionFunc { + return func(c *cli.Context) error { + if err := util.ValidateNArg(c, 0); err != nil { + return err + } + + fmt.Println(util.CompletionZshScript) + + return nil + } +} + +func docGenerateAction() cli.ActionFunc { + return func(c *cli.Context) error { + if err := util.ValidateNArg(c, 0); err != nil { + return err + } + + man, err := c.App.ToMarkdown() + if err != nil { + return nil + } + + fmt.Println(man) + + return nil + } +} + +func getRMKArtifactMetadata(keyPath string) (*RMKArtifactMetadata, error) { + rmkArtifactMetadata := &RMKArtifactMetadata{} + aws := &aws_provider.AwsConfigure{Region: util.RMKBucketRegion} + data, err := aws.GetFileData(util.RMKBucketName, util.RMKBin+"/"+keyPath+"/metadata.json") + if err != nil { + return nil, err + } + + if err := json.Unmarshal(data, &rmkArtifactMetadata); err != nil { + return nil, err + } + + return rmkArtifactMetadata, nil +} + +func rmkURLFormation(paths ...string) string { + u, err := url.Parse("https://" + util.RMKBucketName + ".s3." + util.RMKBucketRegion + ".amazonaws.com") + if err != nil { + zap.S().Fatal(err) + } + + p := append([]string{u.Path}, paths...) + u.Path = path.Join(p...) + return u.String() +} + +func updateRMK(pkgName, version string, silent, progressBar bool, ctx *cli.Context) error { + zap.S().Infof("starting package download: %s", pkgName) + pkgDst := util.GetHomePath(filepath.Join(".local", util.ToolsBinDir)) + if err := util.DownloadArtifact( + rmkURLFormation(util.RMKBin, version, pkgName), + pkgDst, + pkgName, + &http.Header{}, + silent, + progressBar, + ctx.Context, + ); err != nil { + return err + } + + if err := os.Rename(filepath.Join(pkgDst, pkgName), filepath.Join(pkgDst, util.RMKBin)); err != nil { + return err + } + + if err := os.Chmod(filepath.Join(pkgDst, util.RMKBin), 0755); err != nil { + return err + } + + relPath := strings.ReplaceAll(util.RMKSymLinkPath, filepath.Base(util.RMKSymLinkPath), "") + if syscall.Access(relPath, uint32(2)) == nil { + if !util.IsExists(util.RMKSymLinkPath, true) { + return os.Symlink(filepath.Join(pkgDst, util.RMKBin), util.RMKSymLinkPath) + } + } else { + zap.S().Warnf("symlink was not created automatically due to permissions, "+ + "please complete installation by running command: \n"+ + "sudo ln -s %s %s", filepath.Join(pkgDst, util.RMKBin), util.RMKSymLinkPath) + } + + return nil +} + +func updateAction() cli.ActionFunc { + return func(c *cli.Context) error { + if err := util.ValidateNArg(c, 0); err != nil { + return err + } + + var version string + latestPath := "latest" + + if c.Bool("release-candidate") { + latestPath = "latest-rc" + } + + metadata, err := getRMKArtifactMetadata(latestPath) + if err != nil { + return err + } + + if len(c.String("version")) > 0 { + v, err := semver.NewVersion(c.String("version")) + if err != nil { + return err + } + + version = v.Original() + } + + verCurrent, _ := semver.NewVersion(fmt.Sprintf("%v", c.App.Metadata["version"])) + verFound, _ := semver.NewVersion(metadata.Version) + binaryName := fmt.Sprintf("%s", c.App.Metadata["binaryName"]) + if verCurrent.LessThan(verFound) && len(version) == 0 { + zap.S().Infof("newer release version RMK available: %s", verFound.Original()) + if err := updateRMK(binaryName, latestPath, false, true, c); err != nil { + return err + } + } else if len(version) > 0 { + zap.S().Infof("update current RMK version from %s to %s", + c.App.Metadata["version"], version) + if err := updateRMK(binaryName, version, false, true, c); err != nil { + return err + } + } else { + zap.S().Infof("installed RMK version %s is up-to-date", verCurrent.Original()) + } + + return nil + } +} diff --git a/commands/secret_category.go b/cmd/secret.go similarity index 74% rename from commands/secret_category.go rename to cmd/secret.go index c42385d..6ed016a 100644 --- a/commands/secret_category.go +++ b/cmd/secret.go @@ -1,4 +1,4 @@ -package commands +package cmd import ( "bytes" @@ -13,7 +13,7 @@ import ( "gopkg.in/yaml.v3" "rmk/config" - "rmk/system" + "rmk/util" ) type SecretRunner interface { @@ -40,8 +40,8 @@ func newSecretCommands(conf *config.Config, ctx *cli.Context, workDir string) *S return &SecretCommands{&ReleaseCommands{Conf: conf, Ctx: ctx, WorkDir: workDir}} } -func (sc *SecretCommands) ageKeygen(args ...string) *system.SpecCMD { - return &system.SpecCMD{ +func (sc *SecretCommands) ageKeygen(args ...string) *util.SpecCMD { + return &util.SpecCMD{ Args: args, Command: "age-keygen", Ctx: sc.Ctx.Context, @@ -50,13 +50,13 @@ func (sc *SecretCommands) ageKeygen(args ...string) *system.SpecCMD { } } -func (sc *SecretCommands) helm() *system.SpecCMD { - return &system.SpecCMD{ +func (sc *SecretCommands) helm() *util.SpecCMD { + return &util.SpecCMD{ Args: []string{"secrets"}, Command: "helm", Ctx: sc.Ctx.Context, Dir: sc.WorkDir, - Envs: []string{"SOPS_AGE_KEY_FILE=" + filepath.Join(sc.Conf.SopsAgeKeys, system.SopsAgeKeyFile)}, + Envs: []string{"SOPS_AGE_KEY_FILE=" + filepath.Join(sc.Conf.SopsAgeKeys, util.SopsAgeKeyFile)}, Debug: true, DisableStdOut: true, } @@ -65,7 +65,7 @@ func (sc *SecretCommands) helm() *system.SpecCMD { func (sc *SecretCommands) createAgeKey(scope string) error { keyPath := filepath.Join(sc.Conf.SopsAgeKeys, sc.Conf.Tenant+"-"+scope+".txt") - if system.IsExists(keyPath, true) { + if util.IsExists(keyPath, true) { return fmt.Errorf("key for scope %s exists, if you want to recreate, delete this file %s "+ "and run the command again", scope, keyPath) } @@ -82,12 +82,12 @@ func (sc *SecretCommands) createAgeKey(scope string) error { } func (sc *SecretCommands) CreateKeys() error { - if !system.IsExists(system.GetPwdPath(system.TenantValuesDIR), false) { + if !util.IsExists(util.GetPwdPath(util.TenantValuesDIR), false) { return fmt.Errorf("'%s' directory not exist in project structure, please generate structure "+ - "by running command: 'rmk project generate'", system.TenantValuesDIR) + "by running command: 'rmk project generate'", util.TenantValuesDIR) } - scopes, err := os.ReadDir(system.GetPwdPath(system.TenantValuesDIR)) + scopes, err := os.ReadDir(util.GetPwdPath(util.TenantValuesDIR)) if err != nil { return err } @@ -104,22 +104,22 @@ func (sc *SecretCommands) CreateKeys() error { zap.S().Infof("generate age key for scope: %s", scope.Name()) - sopsConfigFiles, err := system.WalkInDir(system.GetPwdPath(system.TenantValuesDIR, scope.Name()), - "secrets", system.SopsConfigFile) + sopsConfigFiles, err := util.WalkInDir(util.GetPwdPath(util.TenantValuesDIR, scope.Name()), + "secrets", util.SopsConfigFile) if err != nil { return err } if len(sopsConfigFiles) == 0 { - secretSpecFiles, err := system.WalkInDir(system.GetPwdPath(system.TenantValuesDIR, scope.Name()), - "secrets", system.SecretSpecFile) + secretSpecFiles, err := util.WalkInDir(util.GetPwdPath(util.TenantValuesDIR, scope.Name()), + "secrets", util.SecretSpecFile) if err != nil { return err } for _, specFile := range secretSpecFiles { dirSpecFile, _ := filepath.Split(specFile) - sopsConfigFiles = append(sopsConfigFiles, filepath.Join(dirSpecFile, system.SopsConfigFile)) + sopsConfigFiles = append(sopsConfigFiles, filepath.Join(dirSpecFile, util.SopsConfigFile)) } } @@ -157,14 +157,14 @@ func (sc *SecretCommands) getOptionFiles(option string) ([]string, error) { switch { case !sc.Ctx.IsSet("scope") && !sc.Ctx.IsSet("environment"): var err error - optionFiles, err = system.WalkInDir(system.GetPwdPath(system.TenantValuesDIR, filepath.Join(optionPaths...)), + optionFiles, err = util.WalkInDir(util.GetPwdPath(util.TenantValuesDIR, filepath.Join(optionPaths...)), "secrets", option) if err != nil { return nil, err } case sc.Ctx.IsSet("scope") && !sc.Ctx.IsSet("environment"): for _, scope := range sc.Ctx.StringSlice("scope") { - sopsFiles, err := system.WalkInDir(system.GetPwdPath(system.TenantValuesDIR, scope), + sopsFiles, err := util.WalkInDir(util.GetPwdPath(util.TenantValuesDIR, scope), "secrets", option) if err != nil { return nil, err @@ -184,7 +184,7 @@ func (sc *SecretCommands) getOptionFiles(option string) ([]string, error) { return nil, fmt.Errorf("environment %s do not exist in project.spec.environments", environment) } - sopsFiles, err := system.WalkInDir(system.GetPwdPath(system.TenantValuesDIR), + sopsFiles, err := util.WalkInDir(util.GetPwdPath(util.TenantValuesDIR), environment, filepath.Join("secrets", option)) if err != nil { return nil, err @@ -196,7 +196,7 @@ func (sc *SecretCommands) getOptionFiles(option string) ([]string, error) { for _, scope := range sc.Ctx.StringSlice("scope") { for _, environment := range sc.Ctx.StringSlice("environment") { optionPaths = append(optionPaths, scope, environment) - sopsFiles, err := system.WalkInDir(system.GetPwdPath(system.TenantValuesDIR, filepath.Join(optionPaths...)), + sopsFiles, err := util.WalkInDir(util.GetPwdPath(util.TenantValuesDIR, filepath.Join(optionPaths...)), "secrets", option) if err != nil { return nil, err @@ -220,14 +220,14 @@ func (sc *SecretCommands) getSecretPaths(optionFiles []string) ([]string, error) } for _, tempPath := range tempPaths { - secrets, err := system.WalkMatch(tempPath, "*.yaml") + secrets, err := util.WalkMatch(tempPath, "*.yaml") if err != nil { return nil, err } for _, secretPath := range secrets { _, file := filepath.Split(secretPath) - if file != system.SopsConfigFile && file != system.SecretSpecFile { + if file != util.SopsConfigFile && file != util.SecretSpecFile { secretPaths = append(secretPaths, secretPath) } } @@ -255,7 +255,7 @@ func (sc *SecretCommands) SecretManager(option string) error { return err } - if err := system.MergeAgeKeys(sc.Conf.SopsAgeKeys); err != nil { + if err := util.MergeAgeKeys(sc.Conf.SopsAgeKeys); err != nil { return err } @@ -266,8 +266,8 @@ func (sc *SecretCommands) SecretManager(option string) error { case "decrypt": sc.SpecCMD.Args = append(sc.SpecCMD.Args, sc.Ctx.Command.Name, "-i", secret) if err := runner(sc).runCMD(); err != nil { - if strings.Contains(sc.SpecCMD.StderrBuf.String(), system.HelmSecretsIsNotEncrypted+secret) { - zap.S().Warnf(strings.ToLower(system.HelmSecretsIsNotEncrypted)+"%s", secret) + if strings.Contains(sc.SpecCMD.StderrBuf.String(), util.HelmSecretsIsNotEncrypted+secret) { + zap.S().Warnf(strings.ToLower(util.HelmSecretsIsNotEncrypted)+"%s", secret) continue } else { return fmt.Errorf(sc.SpecCMD.StderrBuf.String()) @@ -278,8 +278,8 @@ func (sc *SecretCommands) SecretManager(option string) error { case "encrypt": sc.SpecCMD.Args = append(sc.SpecCMD.Args, sc.Ctx.Command.Name, "-i", secret) if err := runner(sc).runCMD(); err != nil { - if strings.Contains(sc.SpecCMD.StderrBuf.String(), system.HelmSecretsAlreadyEncrypted+filepath.Base(secret)) { - zap.S().Warnf(strings.ToLower(system.HelmSecretsAlreadyEncrypted)+"%s", secret) + if strings.Contains(sc.SpecCMD.StderrBuf.String(), util.HelmSecretsAlreadyEncrypted+filepath.Base(secret)) { + zap.S().Warnf(strings.ToLower(util.HelmSecretsAlreadyEncrypted)+"%s", secret) continue } else { return fmt.Errorf(sc.SpecCMD.StderrBuf.String()) @@ -294,7 +294,7 @@ func (sc *SecretCommands) SecretManager(option string) error { } func (sc *SecretCommands) helmSecretsEncrypt() error { - if err := system.MergeAgeKeys(sc.Conf.SopsAgeKeys); err != nil { + if err := util.MergeAgeKeys(sc.Conf.SopsAgeKeys); err != nil { return err } @@ -305,7 +305,7 @@ func (sc *SecretCommands) helmSecretsEncrypt() error { } func (sc *SecretCommands) helmSecretsDecrypt() error { - if err := system.MergeAgeKeys(sc.Conf.SopsAgeKeys); err != nil { + if err := util.MergeAgeKeys(sc.Conf.SopsAgeKeys); err != nil { return err } @@ -316,7 +316,7 @@ func (sc *SecretCommands) helmSecretsDecrypt() error { } func (sc *SecretCommands) helmSecretsView() error { - if err := system.MergeAgeKeys(sc.Conf.SopsAgeKeys); err != nil { + if err := util.MergeAgeKeys(sc.Conf.SopsAgeKeys); err != nil { return err } @@ -333,7 +333,7 @@ func (sc *SecretCommands) helmSecretsView() error { } func (sc *SecretCommands) helmSecretsEdit() error { - if err := system.MergeAgeKeys(sc.Conf.SopsAgeKeys); err != nil { + if err := util.MergeAgeKeys(sc.Conf.SopsAgeKeys); err != nil { return err } @@ -345,7 +345,7 @@ func (sc *SecretCommands) helmSecretsEdit() error { } func (sc *SecretCommands) runHelmSecretsCMD(secretFilePath string, returnCMDError bool) error { - if !system.IsExists(secretFilePath, true) { + if !util.IsExists(secretFilePath, true) { return fmt.Errorf("file does not exist: %s", secretFilePath) } @@ -357,13 +357,13 @@ func (sc *SecretCommands) runHelmSecretsCMD(secretFilePath string, returnCMDErro out := sc.SpecCMD.StderrBuf.String() // suppress help message of helm secrets - if strings.Contains(out, system.HelpFlagFull) { - return fmt.Errorf(system.UnknownErrorText, "Helm secrets") + if strings.Contains(out, util.HelpFlagFull) { + return fmt.Errorf(util.UnknownErrorText, "Helm secrets") } // remove unneeded text from helm secrets - out = strings.ReplaceAll(out, system.HelmSecretsOutputPrefix, "") - out = strings.ReplaceAll(out, system.HelmSecretsError, "") + out = strings.ReplaceAll(out, util.HelmSecretsOutputPrefix, "") + out = strings.ReplaceAll(out, util.HelmSecretsError, "") out = strings.TrimSpace(out) // make the first letter lowercase @@ -379,11 +379,11 @@ func (sc *SecretCommands) runHelmSecretsCMD(secretFilePath string, returnCMDErro func secretMgrEncryptDecryptAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -391,26 +391,26 @@ func secretMgrEncryptDecryptAction(conf *config.Config) cli.ActionFunc { return err } - return newSecretCommands(conf, c, system.GetPwdPath("")).SecretManager(system.SopsConfigFile) + return newSecretCommands(conf, c, util.GetPwdPath("")).SecretManager(util.SopsConfigFile) } } func secretMgrGenerateAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } - return newSecretCommands(conf, c, system.GetPwdPath("")).SecretManager(system.SecretSpecFile) + return newSecretCommands(conf, c, util.GetPwdPath("")).SecretManager(util.SecretSpecFile) } } func secretKeysCreateAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -418,13 +418,13 @@ func secretKeysCreateAction(conf *config.Config) cli.ActionFunc { return err } - return newSecretCommands(conf, c, system.GetPwdPath("")).CreateKeys() + return newSecretCommands(conf, c, util.GetPwdPath("")).CreateKeys() } } func secretKeysDownloadAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -434,21 +434,21 @@ func secretKeysDownloadAction(conf *config.Config) cli.ActionFunc { func secretKeysUploadAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateNArg(c, 0); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } - return conf.UploadToBucket(conf.SopsBucketName, conf.SopsAgeKeys, "*"+system.SopsAgeKeyExt) + return conf.UploadToBucket(conf.SopsBucketName, conf.SopsAgeKeys, "*"+util.SopsAgeKeyExt) } } func secretAction(conf *config.Config, action func(secretRunner SecretRunner) error) cli.ActionFunc { return func(c *cli.Context) error { - if err := system.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateArtifactModeDefault(c, ""); err != nil { return err } - if err := system.ValidateNArg(c, 1); err != nil { + if err := util.ValidateNArg(c, 1); err != nil { return err } @@ -456,6 +456,6 @@ func secretAction(conf *config.Config, action func(secretRunner SecretRunner) er return err } - return action(newSecretCommands(conf, c, system.GetPwdPath(""))) + return action(newSecretCommands(conf, c, util.GetPwdPath(""))) } } diff --git a/commands/secret_generation_category.go b/cmd/secret_generation.go similarity index 96% rename from commands/secret_generation_category.go rename to cmd/secret_generation.go index 42c10fb..d9515a0 100644 --- a/commands/secret_generation_category.go +++ b/cmd/secret_generation.go @@ -1,4 +1,4 @@ -package commands +package cmd import ( "bytes" @@ -13,7 +13,7 @@ import ( "golang.org/x/crypto/ssh/terminal" "gopkg.in/yaml.v3" - "rmk/system" + "rmk/util" ) // Custom name function for parsing template @@ -99,7 +99,7 @@ func (gf *GenerationFuncMap) renderSpecTemplate(s string, data ...interface{}) e func (g *GenerationSpec) writeSpecSecrets(force bool) error { for _, rule := range g.GenerationRules { - if system.IsExists(filepath.Join(g.secretsDir, rule.Name+".yaml"), true) && !force { + if util.IsExists(filepath.Join(g.secretsDir, rule.Name+".yaml"), true) && !force { zap.S().Warnf("%s exists, new secret generation was skipped", filepath.Join(g.secretsDir, rule.Name+".yaml")) continue diff --git a/commands/rmk_category.go b/commands/rmk_category.go deleted file mode 100644 index a1269cb..0000000 --- a/commands/rmk_category.go +++ /dev/null @@ -1,89 +0,0 @@ -package commands - -import ( - "fmt" - - "github.com/Masterminds/semver" - "github.com/urfave/cli/v2" - "go.uber.org/zap" - - "rmk/system" -) - -func completionAction() cli.ActionFunc { - return func(c *cli.Context) error { - if err := system.ValidateNArg(c, 0); err != nil { - return err - } - - fmt.Println(system.CompletionZshScript) - - return nil - } -} - -func docGenerateAction() cli.ActionFunc { - return func(c *cli.Context) error { - if err := system.ValidateNArg(c, 0); err != nil { - return err - } - - man, err := c.App.ToMarkdown() - if err != nil { - return nil - } - - fmt.Println(man) - - return nil - } -} - -func updateAction() cli.ActionFunc { - return func(c *cli.Context) error { - if err := system.ValidateNArg(c, 0); err != nil { - return err - } - - var version string - latestPath := "latest" - - if c.Bool("release-candidate") { - latestPath = "latest-rc" - } - - metadata, err := getRMKArtifactMetadata(latestPath) - if err != nil { - return err - } - - if len(c.String("version")) > 0 { - v, err := semver.NewVersion(c.String("version")) - if err != nil { - return err - } - - version = v.Original() - } - - verCurrent, _ := semver.NewVersion(fmt.Sprintf("%v", c.App.Metadata["version"])) - verFound, _ := semver.NewVersion(metadata.Version) - binaryName := fmt.Sprintf("%s", c.App.Metadata["binaryName"]) - if verCurrent.LessThan(verFound) && len(version) == 0 { - zap.S().Infof("newer release version RMK available: %s", verFound.Original()) - if err := updateRMK(binaryName, latestPath, false, true, c); err != nil { - return err - } - } else if len(version) > 0 { - zap.S().Infof("update current RMK version from %s to %s", - c.App.Metadata["version"], version) - if err := updateRMK(binaryName, version, false, true, c); err != nil { - return err - } - } else { - zap.S().Infof("installed RMK version %s is up-to-date", verCurrent.Original()) - } - - return nil - } -} diff --git a/config/config.go b/config/config.go index b6b5a1b..312b032 100644 --- a/config/config.go +++ b/config/config.go @@ -17,8 +17,8 @@ import ( "go.uber.org/zap" "gopkg.in/yaml.v3" - "rmk/aws_provider" - "rmk/system" + "rmk/providers/aws_provider" + "rmk/util" ) type Config struct { @@ -109,7 +109,7 @@ type Terraform struct { func (conf *Config) InitConfig(terraformOutput bool) *Config { conf.ProjectFile = ProjectFile{} - if err := conf.ReadProjectFile(system.GetPwdPath(system.TenantProjectFile)); err != nil { + if err := conf.ReadProjectFile(util.GetPwdPath(util.TenantProjectFile)); err != nil { zap.S().Fatal(err) } @@ -143,7 +143,7 @@ func (conf *Config) SerializeJsonConfig() ([]byte, error) { func (conf *Config) GetConfigs(all bool) error { var tenantPattern string - configsPath := system.GetHomePath(system.RMKDir, system.RMKConfig) + configsPath := util.GetHomePath(util.RMKDir, util.RMKConfig) if all { tenantPattern = "" @@ -151,7 +151,7 @@ func (conf *Config) GetConfigs(all bool) error { tenantPattern = conf.Tenant } - match, err := system.WalkMatch(configsPath, tenantPattern+"*.yaml") + match, err := util.WalkMatch(configsPath, tenantPattern+"*.yaml") if err != nil { return err } @@ -164,14 +164,14 @@ func (conf *Config) GetConfigs(all bool) error { } func (conf *Config) SetRootDomain(c *cli.Context, gitSpecID string) error { - hostedZoneVar := system.TerraformVarsPrefix + system.TerraformVarHostedZoneName + hostedZoneVar := util.TerraformVarsPrefix + util.TerraformVarHostedZoneName if !c.IsSet("root-domain") { if hostedZoneName, ok := conf.TerraformOutput[hostedZoneVar]; ok && len(hostedZoneName.(string)) > 0 { if err := c.Set("root-domain", hostedZoneName.(string)); err != nil { return err } } else { - if err := c.Set("root-domain", gitSpecID+system.TenantDomainSuffix); err != nil { + if err := c.Set("root-domain", gitSpecID+util.TenantDomainSuffix); err != nil { return err } } @@ -221,12 +221,12 @@ func (conf *Config) GetTerraformOutputs() error { } for key := range outputs { - if strings.Contains(key, system.TerraformVarsPrefix) { + if strings.Contains(key, util.TerraformVarsPrefix) { if err := json.Unmarshal(*outputs[key], &getVar); err != nil { return err } - envKey := strings.ToUpper(strings.ReplaceAll(key, system.TerraformVarsPrefix, "")) + envKey := strings.ToUpper(strings.ReplaceAll(key, util.TerraformVarsPrefix, "")) switch { case reflect.TypeOf(getVar.Value).Kind() == reflect.String && getVar.Type == reflect.String.String(): @@ -370,7 +370,7 @@ func (conf *Config) ReadConfigFile(path string) error { } func (conf *Config) CreateConfigFile() error { - if err := os.MkdirAll(system.GetHomePath(system.RMKDir, system.RMKConfig), 0755); err != nil { + if err := os.MkdirAll(util.GetHomePath(util.RMKDir, util.RMKConfig), 0755); err != nil { return err } @@ -379,5 +379,5 @@ func (conf *Config) CreateConfigFile() error { return err } - return os.WriteFile(system.GetHomePath(system.RMKDir, system.RMKConfig, conf.Name+".yaml"), data, 0644) + return os.WriteFile(util.GetHomePath(util.RMKDir, util.RMKConfig, conf.Name+".yaml"), data, 0644) } diff --git a/git_handler/git.go b/git_handler/git.go index d318a9c..7379941 100644 --- a/git_handler/git.go +++ b/git_handler/git.go @@ -16,7 +16,7 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport/ssh" "go.uber.org/zap" - "rmk/system" + "rmk/util" ) const ( @@ -108,7 +108,7 @@ func (g *GitSpec) GetBranchName() error { DetectDotGit: true, } - repo, err := git.PlainOpenWithOptions(system.GetPwdPath(""), &openOptions) + repo, err := git.PlainOpenWithOptions(util.GetPwdPath(""), &openOptions) if err != nil { return err } @@ -130,7 +130,7 @@ func (g *GitSpec) GetRepoPrefix() error { DetectDotGit: true, } - repo, err := git.PlainOpenWithOptions(system.GetPwdPath(""), &openOptions) + repo, err := git.PlainOpenWithOptions(util.GetPwdPath(""), &openOptions) if err != nil { return err } @@ -174,11 +174,11 @@ func (g *GitSpec) GenerateID() error { func (g *GitSpec) GitCommitPush(pathRF, msg, token string) error { var err error - if g.repo, err = git.PlainOpen(system.GetPwdPath("")); err != nil { + if g.repo, err = git.PlainOpen(util.GetPwdPath("")); err != nil { return err } - if pathRF, err = filepath.Rel(system.GetPwdPath(""), pathRF); err != nil { + if pathRF, err = filepath.Rel(util.GetPwdPath(""), pathRF); err != nil { return err } @@ -209,19 +209,19 @@ func (g *GitSpec) GitCommitPush(pathRF, msg, token string) error { return err } - reset := &system.SpecCMD{ + reset := &util.SpecCMD{ Args: []string{"reset", "--hard", "origin/" + g.headRef.Name().Short()}, Command: "git", - Dir: system.GetPwdPath(""), + Dir: util.GetPwdPath(""), Ctx: context.TODO(), DisableStdOut: true, Debug: false, } - cherryPick := &system.SpecCMD{ + cherryPick := &util.SpecCMD{ Args: []string{"cherry-pick", hash.String()}, Command: "git", - Dir: system.GetPwdPath(""), + Dir: util.GetPwdPath(""), Ctx: context.TODO(), DisableStdOut: true, Debug: false, @@ -300,7 +300,7 @@ func (g *GitSpec) getAuthMethod(token string) (transport.AuthMethod, error) { return &http.BasicAuth{Username: "git", Password: token}, nil } - return ssh.NewPublicKeysFromFile("git", system.GetHomePath(system.GitSSHPrivateKey), "") + return ssh.NewPublicKeysFromFile("git", util.GetHomePath(util.GitSSHPrivateKey), "") } return nil, fmt.Errorf("failed to detect auth method") diff --git a/main.go b/main.go index 0335397..25284f0 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,7 @@ import ( "github.com/urfave/cli/v2" "go.uber.org/zap" - "rmk/commands" + "rmk/cmd" "rmk/logger" ) @@ -26,7 +26,7 @@ var ( func init() { var output string - for _, val := range commands.FlagsGlobal() { + for _, val := range cmd.FlagsGlobal() { output += fmt.Sprintf(" %s\n", val.String()) } @@ -56,7 +56,7 @@ func runCLI() *cli.App { "version": version, } - app.Flags = commands.FlagsGlobal() + app.Flags = cmd.FlagsGlobal() app.Before = func(c *cli.Context) error { logger.Init(c.String("log-format"), c.String("log-level")) return nil @@ -68,7 +68,7 @@ func runCLI() *cli.App { // Enable flag and command suggestions app.Suggest = true - app.Commands = commands.Commands() + app.Commands = cmd.Commands() sort.Sort(cli.CommandsByName(app.Commands)) return app diff --git a/aws_provider/aws.go b/providers/aws_provider/aws.go similarity index 96% rename from aws_provider/aws.go rename to providers/aws_provider/aws.go index 259f5af..7f74ffb 100644 --- a/aws_provider/aws.go +++ b/providers/aws_provider/aws.go @@ -28,7 +28,7 @@ import ( "github.com/aws/smithy-go" "go.uber.org/zap" - "rmk/system" + "rmk/util" ) type AwsConfigure struct { @@ -49,11 +49,11 @@ type MFAToken struct { } func (a *AwsConfigure) AWSSharedConfigFile(profile string) []string { - return []string{system.GetHomePath(".aws", "config_"+profile)} + return []string{util.GetHomePath(".aws", "config_"+profile)} } func (a *AwsConfigure) AWSSharedCredentialsFile(profile string) []string { - return []string{system.GetHomePath(".aws", "credentials_"+profile)} + return []string{util.GetHomePath(".aws", "credentials_"+profile)} } func (a *AwsConfigure) errorProxy(cfg aws.Config, err error) (aws.Config, error) { @@ -138,8 +138,8 @@ func (a *AwsConfigure) GetMFADevicesSerialNumbers() error { serialNumbers[strconv.Itoa(key+1)] = aws.ToString(val.SerialNumber) } - if _, ok := serialNumbers[system.ReadStdin("number SerialNumber")]; ok { - a.MFADeviceSerialNumber = serialNumbers[system.ReadStdin("number SerialNumber")] + if _, ok := serialNumbers[util.ReadStdin("number SerialNumber")]; ok { + a.MFADeviceSerialNumber = serialNumbers[util.ReadStdin("number SerialNumber")] } else { return fmt.Errorf("incorrectly specified number SerialNumber") } @@ -164,7 +164,7 @@ func (a *AwsConfigure) GetMFASessionToken() error { token, err := sts.NewFromConfig(cfg).GetSessionToken(ctx, &sts.GetSessionTokenInput{ DurationSeconds: aws.Int32(43200), SerialNumber: aws.String(a.MFADeviceSerialNumber), - TokenCode: aws.String(system.ReadStdin("TOTP")), + TokenCode: aws.String(util.ReadStdin("TOTP")), }) if err != nil { return err @@ -255,7 +255,7 @@ func (a *AwsConfigure) CreateBucket(bucketName string) error { client := s3.NewFromConfig(cfg) - if a.Region == system.RegionException { + if a.Region == util.RegionException { _, err := client.HeadBucket(ctx, &s3.HeadBucketInput{Bucket: aws.String(bucketName)}) if err != nil { if !errors.As(err, &bucketNotFound) { @@ -541,13 +541,13 @@ func (a *AwsConfigure) UploadToBucket(bucketName, localDir, pattern string) erro uploader := manager.NewUploader(s3.NewFromConfig(cfg)) - match, err := system.WalkMatch(localDir, pattern) + match, err := util.WalkMatch(localDir, pattern) if err != nil { return err } for _, path := range match { - if filepath.Base(path) != system.SopsAgeKeyFile { + if filepath.Base(path) != util.SopsAgeKeyFile { data, err := os.ReadFile(path) if err != nil { return err diff --git a/providers/azure_provider/azure.go b/providers/azure_provider/azure.go new file mode 100644 index 0000000..b5dc3a4 --- /dev/null +++ b/providers/azure_provider/azure.go @@ -0,0 +1 @@ +package azure_provider diff --git a/util/cli.go b/util/cli.go new file mode 100644 index 0000000..cbb55d5 --- /dev/null +++ b/util/cli.go @@ -0,0 +1,84 @@ +package util + +import ( + "fmt" + "io" + "os" + "strings" + "unicode/utf8" + + "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2/altsrc" +) + +func cliArgContains(flagName string) bool { + for _, name := range strings.Split(flagName, ",") { + name = strings.TrimSpace(name) + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + + flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + + for _, a := range os.Args { + if a == flag { + return true + } + } + } + + return false +} + +func printFlagSuggestions(lastArg string, flags []cli.Flag, writer io.Writer) { + cur := strings.TrimPrefix(lastArg, "-") + cur = strings.TrimPrefix(cur, "-") + for _, flag := range flags { + // skip hidden flags for bool type + if boolFlag, ok := flag.(*cli.BoolFlag); ok && boolFlag.Hidden { + continue + } + // skip hidden flags for altsrc bool type + if altsrcBoolFlag, ok := flag.(*altsrc.BoolFlag); ok && altsrcBoolFlag.Hidden { + continue + } + // skip hidden flags for string type + if stringFlag, ok := flag.(*cli.StringFlag); ok && stringFlag.Hidden { + continue + } + // skip hidden flags for altsrc string type + if altsrcStringFlag, ok := flag.(*altsrc.StringFlag); ok && altsrcStringFlag.Hidden { + continue + } + + for _, name := range flag.Names() { + name = strings.TrimSpace(name) + // this will get total count utf8 letters in flag name + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 // reuse this count to generate single - or -- in flag completion + } + // if flag name has more than one utf8 letter and last argument in cli has -- prefix then + // skip flag completion for short flags example -v or -x + if strings.HasPrefix(lastArg, "--") && count == 1 { + continue + } + // match if last argument matches this flag and it is not repeated + if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(name) { + flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + _, _ = fmt.Fprintln(writer, flagCompletion) + } + } + } +} + +func ShellCompleteCustomOutput(c *cli.Context) { + if len(os.Args) > 2 { + if os.Args[len(os.Args)-2] != "" && strings.HasPrefix(os.Args[len(os.Args)-2], "-") { + printFlagSuggestions(os.Args[len(os.Args)-2], c.Command.Flags, c.App.Writer) + + return + } + } +} diff --git a/system/dictionary.go b/util/dictionary.go similarity index 99% rename from system/dictionary.go rename to util/dictionary.go index b33e428..8a4b230 100644 --- a/system/dictionary.go +++ b/util/dictionary.go @@ -1,4 +1,4 @@ -package system +package util const ( AWSClusterProvider = "aws" diff --git a/go_getter/getter.go b/util/getter.go similarity index 99% rename from go_getter/getter.go rename to util/getter.go index 3e90261..b53f013 100644 --- a/go_getter/getter.go +++ b/util/getter.go @@ -1,4 +1,4 @@ -package go_getter +package util import ( "context" diff --git a/system/system.go b/util/system.go similarity index 73% rename from system/system.go rename to util/system.go index e91f73f..67301e6 100644 --- a/system/system.go +++ b/util/system.go @@ -1,4 +1,4 @@ -package system +package util import ( "archive/tar" @@ -14,10 +14,7 @@ import ( "regexp" "strings" "sync" - "unicode/utf8" - "github.com/urfave/cli/v2" - "github.com/urfave/cli/v2/altsrc" "go.uber.org/zap" ) @@ -400,95 +397,3 @@ func UnTar(dst, excludeRegexp string, r io.Reader) error { } } } - -func cliArgContains(flagName string) bool { - for _, name := range strings.Split(flagName, ",") { - name = strings.TrimSpace(name) - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 - } - - flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - - for _, a := range os.Args { - if a == flag { - return true - } - } - } - - return false -} - -func printFlagSuggestions(lastArg string, flags []cli.Flag, writer io.Writer) { - cur := strings.TrimPrefix(lastArg, "-") - cur = strings.TrimPrefix(cur, "-") - for _, flag := range flags { - // skip hidden flags for bool type - if boolFlag, ok := flag.(*cli.BoolFlag); ok && boolFlag.Hidden { - continue - } - // skip hidden flags for altsrc bool type - if altsrcBoolFlag, ok := flag.(*altsrc.BoolFlag); ok && altsrcBoolFlag.Hidden { - continue - } - // skip hidden flags for string type - if stringFlag, ok := flag.(*cli.StringFlag); ok && stringFlag.Hidden { - continue - } - // skip hidden flags for altsrc string type - if altsrcStringFlag, ok := flag.(*altsrc.StringFlag); ok && altsrcStringFlag.Hidden { - continue - } - - for _, name := range flag.Names() { - name = strings.TrimSpace(name) - // this will get total count utf8 letters in flag name - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 // reuse this count to generate single - or -- in flag completion - } - // if flag name has more than one utf8 letter and last argument in cli has -- prefix then - // skip flag completion for short flags example -v or -x - if strings.HasPrefix(lastArg, "--") && count == 1 { - continue - } - // match if last argument matches this flag and it is not repeated - if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(name) { - flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - _, _ = fmt.Fprintln(writer, flagCompletion) - } - } - } -} - -func ShellCompleteCustomOutput(c *cli.Context) { - if len(os.Args) > 2 { - if os.Args[len(os.Args)-2] != "" && strings.HasPrefix(os.Args[len(os.Args)-2], "-") { - printFlagSuggestions(os.Args[len(os.Args)-2], c.Command.Flags, c.App.Writer) - - return - } - } -} - -func ValidateArtifactModeDefault(c *cli.Context, errorMsg string) error { - if c.String("artifact-mode") == ArtifactModeDefault && !c.IsSet("github-token") { - if errorMsg == "" { - return fmt.Errorf(ConfigNotInitializedErrorText) - } else { - return fmt.Errorf(errorMsg) - } - } - - return nil -} - -func ValidateNArg(c *cli.Context, expectedNArg int) error { - if c.NArg() != expectedNArg { - return fmt.Errorf("exactly %d argument(s) required for '%s' command", expectedNArg, c.Command.Name) - } - - return nil -} diff --git a/util/validate.go b/util/validate.go new file mode 100644 index 0000000..f010df9 --- /dev/null +++ b/util/validate.go @@ -0,0 +1,27 @@ +package util + +import ( + "fmt" + + "github.com/urfave/cli/v2" +) + +func ValidateArtifactModeDefault(c *cli.Context, errorMsg string) error { + if c.String("artifact-mode") == ArtifactModeDefault && !c.IsSet("github-token") { + if errorMsg == "" { + return fmt.Errorf(ConfigNotInitializedErrorText) + } else { + return fmt.Errorf(errorMsg) + } + } + + return nil +} + +func ValidateNArg(c *cli.Context, expectedNArg int) error { + if c.NArg() != expectedNArg { + return fmt.Errorf("exactly %d argument(s) required for '%s' command", expectedNArg, c.Command.Name) + } + + return nil +} From ba97fb5cdec349e7e7b92de4da37eb30599a0f1a Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Mon, 5 Aug 2024 17:01:30 +0200 Subject: [PATCH 02/39] #29 - fix command "config list" output for adjacent tenant names --- cmd/flags.go | 9 +++++++++ config/config.go | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/cmd/flags.go b/cmd/flags.go index 08da2d7..65701de 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -10,6 +10,7 @@ import ( func flagsConfig() []cli.Flag { return []cli.Flag{ + // TODO: will be deprecated altsrc.NewStringFlag( &cli.StringFlag{ Name: "artifact-mode", @@ -19,6 +20,7 @@ func flagsConfig() []cli.Flag { Value: util.ArtifactModeDefault, }, ), + // TODO: will be transfer to cluster category for AWS provider altsrc.NewStringFlag( &cli.StringFlag{ Name: "aws-ecr-host", @@ -28,6 +30,7 @@ func flagsConfig() []cli.Flag { Value: util.AWSECRHost, }, ), + // TODO: will be transfer to cluster category for AWS provider altsrc.NewStringFlag( &cli.StringFlag{ Name: "aws-ecr-region", @@ -37,6 +40,7 @@ func flagsConfig() []cli.Flag { Value: util.AWSECRRegion, }, ), + // TODO: will be transfer to cluster category for AWS provider altsrc.NewStringFlag( &cli.StringFlag{ Name: "aws-ecr-user-name", @@ -63,11 +67,13 @@ func flagsConfig() []cli.Flag { Usage: "force AWS profile creation", Aliases: []string{"r"}, }, + // TODO: will be deprecated &cli.BoolFlag{ Name: "aws-reconfigure-artifact-license", Usage: "force AWS profile creation for artifact license, used only if RMK config option artifact-mode has values: online, offline", Aliases: []string{"l"}, }, + // TODO: will be transfer to cluster category for AWS provider altsrc.NewBoolFlag( &cli.BoolFlag{ Name: "cluster-provisioner-state-locking", @@ -101,6 +107,7 @@ func flagsConfig() []cli.Flag { EnvVars: []string{"RMK_GITHUB_TOKEN"}, }, ), + // TODO: will be transfer to cluster category altsrc.NewStringFlag( &cli.StringFlag{ Name: "cloudflare-token", @@ -109,6 +116,7 @@ func flagsConfig() []cli.Flag { EnvVars: []string{"RMK_CLOUDFLARE_TOKEN"}, }, ), + // TODO: will be transfer to cluster category for AWS provider altsrc.NewStringFlag( &cli.StringFlag{ Name: "root-domain", @@ -125,6 +133,7 @@ func flagsConfig() []cli.Flag { Value: true, }, ), + // TODO: will be deprecated altsrc.NewStringFlag( &cli.StringFlag{ Name: "s3-charts-repo-region", diff --git a/config/config.go b/config/config.go index 312b032..f585238 100644 --- a/config/config.go +++ b/config/config.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "reflect" + "regexp" "runtime" "strconv" "strings" @@ -17,6 +18,7 @@ import ( "go.uber.org/zap" "gopkg.in/yaml.v3" + "rmk/git_handler" "rmk/providers/aws_provider" "rmk/util" ) @@ -142,22 +144,46 @@ func (conf *Config) SerializeJsonConfig() ([]byte, error) { } func (conf *Config) GetConfigs(all bool) error { - var tenantPattern string + var ( + patternTenant string + patternTaskNum *regexp.Regexp + patternSemVer *regexp.Regexp + patternBranch *regexp.Regexp + ) + configsPath := util.GetHomePath(util.RMKDir, util.RMKConfig) if all { - tenantPattern = "" + patternTenant = "" } else { - tenantPattern = conf.Tenant + patternTenant = conf.Tenant + + patternBranch = regexp.MustCompile(`^` + patternTenant + + `-(` + git_handler.DefaultDevelop + `|` + git_handler.DefaultStaging + `|` + git_handler.DefaultProduction + `)$`) + patternSemVer = regexp.MustCompile(`^` + patternTenant + `-v\d+-\d+-\d+(-[a-z]+)?$`) + patternTaskNum = regexp.MustCompile(`^` + patternTenant + `-[a-z]+-\d+$`) } - match, err := util.WalkMatch(configsPath, tenantPattern+"*.yaml") + match, err := util.WalkMatch(configsPath, patternTenant+"*.yaml") if err != nil { return err } for _, val := range match { - fmt.Printf("- %s\n", strings.TrimSuffix(filepath.Base(val), filepath.Ext(filepath.Base(val)))) + rmkConfig := strings.TrimSuffix(filepath.Base(val), filepath.Ext(filepath.Base(val))) + + if all { + fmt.Printf("- %s\n", rmkConfig) + } else { + switch { + case patternBranch.MatchString(rmkConfig): + fmt.Printf("- %s\n", rmkConfig) + case patternSemVer.MatchString(rmkConfig): + fmt.Printf("- %s\n", rmkConfig) + case patternTaskNum.MatchString(rmkConfig): + fmt.Printf("- %s\n", rmkConfig) + } + } } return nil From b23a916d4e284006aab4034aec0c46f49effc2af Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 7 Aug 2024 14:24:01 +0200 Subject: [PATCH 03/39] #29 - delete artifact-mode functionality --- README.md | 3 - cmd/cluster.go | 10 +- cmd/config.go | 16 +- cmd/container_registry.go | 2 +- cmd/flags.go | 50 +----- cmd/k3d.go | 4 +- cmd/project.go | 4 +- cmd/project_dependencies.go | 167 +----------------- cmd/release.go | 18 +- cmd/secret.go | 6 +- config/config.go | 12 -- .../preparation-of-project-repository.md | 6 +- .../rmk-configuration-management.md | 42 ----- util/dictionary.go | 7 +- util/validate.go | 4 +- 15 files changed, 52 insertions(+), 299 deletions(-) diff --git a/README.md b/README.md index d58d6c9..206adda 100644 --- a/README.md +++ b/README.md @@ -159,8 +159,6 @@ rmk update --version vX.X.X to update release and service version declarations, automatically commit the changes to Git. - **[Project structure generation:](docs/configuration/project-management/preparation-of-project-repository.md#automatic-generation-of-the-project-structure-from-scratch)** Generate a complete Kubernetes-based project structure from scratch using RMK, following the best practices. - **[Documentation generation:](docs/commands.md#doc)** Generate the full command documentation in the Markdown format with one click. -- **[Support for different types of code sources:](docs/configuration/rmk-configuration-management.md#use-upstream-artifact-for-the-downstream-projects-repository)** Use Git when the _artifact-mode_ is _none_, S3 when the _artifact-mode_ is _online_, - switch to fully offline installations when the _artifact-mode_ is _offline_. ## Supported Kubernetes providers @@ -185,7 +183,6 @@ Among the providers are: - **Integration with Helmfile [vals](https://github.com/helmfile/vals)**: Integrate RMK with the **vals** tool for enhanced values and secret management. - **Major update of the AWS [EKS](https://aws.amazon.com/eks/) cluster provider:** Update the AWS EKS cluster provider to the latest versions to utilize all the supported features of the [Terraform](https://www.terraform.io/) CLI and modules. - **Implementation of additional RMK cluster providers:** Implement support for additional cluster providers for popular Kubernetes services such as [GKE](https://cloud.google.com/kubernetes-engine), [AKS](https://azure.microsoft.com/en-us/products/kubernetes-service/), etc. -- **Offline artifact mode:** Implement the **offline** artifact mode to install artifacts in fully isolated offline environments. - **Web documentation generator:** Add an HTML documentation generator based on the **.md** files. - **Automatic testing of RMK during the CI/CD pipeline:** Ensure that changes to the RMK codebase do not introduce errors or regressions during the CI/CD. diff --git a/cmd/cluster.go b/cmd/cluster.go index 80d35c4..2905bbc 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -377,7 +377,7 @@ func (cc *ClusterCommands) clusterStateRefresh() error { func clusterDestroyAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -408,7 +408,7 @@ func clusterDestroyAction(conf *config.Config) cli.ActionFunc { func clusterListAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -437,7 +437,7 @@ func clusterListAction(conf *config.Config) cli.ActionFunc { func clusterProvisionAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -476,7 +476,7 @@ func clusterProvisionAction(conf *config.Config) cli.ActionFunc { func clusterStateAction(conf *config.Config, action func(stateRunner StateRunner) error) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -505,7 +505,7 @@ func clusterStateAction(conf *config.Config, action func(stateRunner StateRunner func clusterSwitchAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } diff --git a/cmd/config.go b/cmd/config.go index 2aba9ab..af0a4e4 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -431,7 +431,7 @@ func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_ return nil } - if err := util.ValidateArtifactModeDefault(c, "required parameter --github-token not set"); err != nil { + if err := util.ValidateGitHubToken(c, "required parameter --github-token not set"); err != nil { return err } @@ -517,11 +517,9 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act } } - conf.ArtifactMode = c.String("artifact-mode") conf.ProgressBar = c.Bool("progress-bar") conf.Terraform.BucketKey = util.TenantBucketKey conf.ClusterProvisionerSL = c.Bool("cluster-provisioner-state-locking") - conf.S3ChartsRepoRegion = c.String("s3-charts-repo-region") conf.ClusterProvider = c.String("cluster-provider") conf.AWSMFAProfile = c.String("aws-mfa-profile") conf.AWSMFATokenExpiration = c.String("aws-mfa-token-expiration") @@ -571,6 +569,18 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act if err := conf.DownloadFromBucket("", conf.SopsBucketName, conf.SopsAgeKeys, conf.Tenant); err != nil { return err } + + if err := resolveDependencies(conf.InitConfig(true), c, false); err != nil { + return err + } + + zap.S().Infof("time spent on initialization: %.fs", time.Since(start).Seconds()) + + return nil + } + + if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { + return err } zap.S().Infof("time spent on initialization: %.fs", time.Since(start).Seconds()) diff --git a/cmd/container_registry.go b/cmd/container_registry.go index 143418a..f690dac 100644 --- a/cmd/container_registry.go +++ b/cmd/container_registry.go @@ -78,7 +78,7 @@ func (cr *CRCommands) dockerLogout() error { func containerRegistryAction(conf *config.Config, action func(dockerRunner DockerRunner) error) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } diff --git a/cmd/flags.go b/cmd/flags.go index 65701de..753abf0 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -10,16 +10,6 @@ import ( func flagsConfig() []cli.Flag { return []cli.Flag{ - // TODO: will be deprecated - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "artifact-mode", - Usage: "choice of artifact usage model, available: none, online, offline", - Aliases: []string{"am"}, - EnvVars: []string{"RMK_ARTIFACT_MODE"}, - Value: util.ArtifactModeDefault, - }, - ), // TODO: will be transfer to cluster category for AWS provider altsrc.NewStringFlag( &cli.StringFlag{ @@ -67,12 +57,6 @@ func flagsConfig() []cli.Flag { Usage: "force AWS profile creation", Aliases: []string{"r"}, }, - // TODO: will be deprecated - &cli.BoolFlag{ - Name: "aws-reconfigure-artifact-license", - Usage: "force AWS profile creation for artifact license, used only if RMK config option artifact-mode has values: online, offline", - Aliases: []string{"l"}, - }, // TODO: will be transfer to cluster category for AWS provider altsrc.NewBoolFlag( &cli.BoolFlag{ @@ -116,6 +100,15 @@ func flagsConfig() []cli.Flag { EnvVars: []string{"RMK_CLOUDFLARE_TOKEN"}, }, ), + altsrc.NewStringFlag( + &cli.StringFlag{ + Name: "cluster-provider", + Usage: "select cluster provider to provision clusters", + Aliases: []string{"cp"}, + EnvVars: []string{"RMK_CLUSTER_PROVIDER"}, + Value: util.AWSClusterProvider, + }, + ), // TODO: will be transfer to cluster category for AWS provider altsrc.NewStringFlag( &cli.StringFlag{ @@ -133,25 +126,6 @@ func flagsConfig() []cli.Flag { Value: true, }, ), - // TODO: will be deprecated - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "s3-charts-repo-region", - Usage: "location constraint region of S3 charts repo", - Aliases: []string{"scrr"}, - EnvVars: []string{"RMK_S3_CHARTS_REPO_REGION"}, - Value: util.S3ChartsRepoRegion, - }, - ), - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "cluster-provider", - Usage: "select cluster provider to provision clusters", - Aliases: []string{"cp"}, - EnvVars: []string{"RMK_CLUSTER_PROVIDER"}, - Value: util.AWSClusterProvider, - }, - ), altsrc.NewBoolFlag( &cli.BoolFlag{ Name: "slack-notifications", @@ -450,12 +424,6 @@ func flagsHidden() []cli.Flag { Name: "config", Hidden: true, }, - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "artifact-mode", - Hidden: true, - }, - ), altsrc.NewStringFlag( &cli.StringFlag{ Name: "github-token", diff --git a/cmd/k3d.go b/cmd/k3d.go index eb816ce..9dfc454 100644 --- a/cmd/k3d.go +++ b/cmd/k3d.go @@ -148,7 +148,7 @@ func (k *K3DCommands) startStopK3DCluster() error { func K3DCreateAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -166,7 +166,7 @@ func K3DCreateAction(conf *config.Config) cli.ActionFunc { func K3DAction(conf *config.Config, action func(k3dRunner K3DRunner) error) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } diff --git a/cmd/project.go b/cmd/project.go index cc13a93..156f065 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -407,7 +407,7 @@ func (p *ProjectCommands) generateProject(gitSpec *git_handler.GitSpec) error { func projectGenerateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -425,7 +425,7 @@ func projectGenerateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cl func projectUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } diff --git a/cmd/project_dependencies.go b/cmd/project_dependencies.go index d3a77e5..2ca696e 100644 --- a/cmd/project_dependencies.go +++ b/cmd/project_dependencies.go @@ -37,18 +37,9 @@ type SpecDownload struct { PkgName string Header *http.Header Type string - Artifact *ArtifactSpec rmOldDir bool } -type ArtifactSpec struct { - BucketName string - Key string - Region string - Version string - Url string -} - type InventoryState struct { clustersState map[string]struct{} helmPluginsState map[string]struct{} @@ -109,139 +100,6 @@ func (s *SpecDownload) download(silent bool) error { ) } -func (s *SpecDownload) parseArtifactUrl() error { - u, err := url.Parse(s.Artifact.Url) - if err != nil { - return err - } - - // This just check whether we are dealing with S3 or - // any other S3 compliant service. S3 has a predictable - // url as others do not - if strings.Contains(u.Host, "amazonaws.com") { - // Amazon S3 supports both virtual-hosted–style and path-style URLs to access a bucket, although path-style is deprecated - // In both cases few older regions supports dash-style region indication (s3-Region) even if AWS discourages their use. - // The same bucket could be reached with: - // bucket.s3.region.amazonaws.com/path - // bucket.s3-region.amazonaws.com/path - // s3.amazonaws.com/bucket/path - // s3-region.amazonaws.com/bucket/path - - hostParts := strings.Split(u.Host, ".") - switch len(hostParts) { - // path-style - case 3: - // Parse the region out of the first part of the host - s.Artifact.Region = strings.TrimPrefix(strings.TrimPrefix(hostParts[0], "s3-"), "s3") - if s.Artifact.Region == "" { - s.Artifact.Region = "us-east-1" - } - - pathParts := strings.SplitN(u.Path, "/", 3) - s.Artifact.BucketName = pathParts[1] - s.Artifact.Key = pathParts[2] - // vhost-style, dash region indication - case 4: - // Parse the region out of the first part of the host - s.Artifact.Region = strings.TrimPrefix(strings.TrimPrefix(hostParts[1], "s3-"), "s3") - if s.Artifact.Region == "" { - return fmt.Errorf("artifact URL is not valid S3 URL for dependency name: %s", s.PkgName) - } - - pathParts := strings.SplitN(u.Path, "/", 2) - s.Artifact.BucketName = hostParts[0] - s.Artifact.Key = pathParts[1] - //vhost-style, dot region indication - case 5: - s.Artifact.Region = hostParts[2] - pathParts := strings.SplitN(u.Path, "/", 2) - s.Artifact.BucketName = hostParts[0] - s.Artifact.Key = pathParts[1] - - } - - if len(hostParts) < 3 && len(hostParts) > 5 { - return fmt.Errorf("artifact URL is not valid S3 URL for dependency name: %s", s.PkgName) - } - - if len(strings.SplitN(s.Artifact.Key, "/", 2)) < 1 { - return fmt.Errorf("artifact URL is not valid S3 URL for dependency name %s, path does not contain SemVer2", s.PkgName) - } - - s.Artifact.Version = strings.SplitN(s.Artifact.Key, "/", 2)[0] - - _, err := semver.NewVersion(s.Artifact.Version) - if err != nil { - return fmt.Errorf("%s for dependency name: %s", err.Error(), s.PkgName) - } - - return nil - } else { - return fmt.Errorf("artifact URL is not valid S3 compliant URL for dependency name: %s", s.PkgName) - } -} - -func (s *SpecDownload) updateArtifact() error { - currentProfile := s.Conf.Profile - licenseProfile := s.Conf.Profile + "-license" - - s.Conf.AwsConfigure.Profile = licenseProfile - if ok, err := s.Conf.GetAwsConfigure(licenseProfile); err != nil && ok { - zap.S().Warnf("%s", err.Error()) - if err := newConfigCommands(s.Conf, s.Ctx, util.GetPwdPath("")).configAws(); err != nil { - return err - } - - if _, err := s.Conf.GetAwsConfigure(licenseProfile); err != nil { - return err - } - } else if s.Ctx.Bool("aws-reconfigure-artifact-license") { - if err := newConfigCommands(s.Conf, s.Ctx, util.GetPwdPath("")).configAws(); err != nil { - return err - } - } - - if err := s.parseArtifactUrl(); err != nil { - return err - } - - if artVerExist, err := s.Conf.BucketKeyExists(s.Artifact.Region, s.Artifact.BucketName, s.Artifact.Key); err != nil || !artVerExist { - return err - } - - if err := s.Conf.DownloadFromBucket(s.Artifact.Region, s.Artifact.BucketName, util.GetPwdPath(util.ArtifactDownloadDir), s.Artifact.Key); err != nil { - return err - } - - s.Conf.AwsConfigure.Profile = currentProfile - if util.IsExists(s.PkgDst, false) { - if err := os.MkdirAll(s.PkgDst, 0777); err != nil { - return err - } - } - - r, err := os.Open(filepath.Join(util.GetPwdPath(util.ArtifactDownloadDir), s.Artifact.Key)) - if err != nil { - return err - } - - if err := util.UnTar(s.PkgDst, "", r); err != nil { - return err - } - - if util.IsExists(filepath.Join(s.PkgDst, util.TenantProjectDIR, "inventory"), false) { - if err := util.CopyDir(filepath.Join(s.PkgDst, util.TenantProjectDIR, "inventory"), util.GetPwdPath(util.TenantProjectDIR)); err != nil { - return err - } - } - - if err := os.RemoveAll(util.GetPwdPath(util.ArtifactDownloadDir)); err != nil { - return err - } - - return nil -} - // depsGC - deleting old deps dirs with not actual versions func hooksGC(hooks []config.HookMapping) error { if !util.IsExists(util.GetPwdPath(TenantPrInvHooksDir), false) { @@ -563,26 +421,14 @@ func (s *SpecDownload) batchUpdate(pwd string, pkg config.Package, silent bool) s.PkgDst = filepath.Join(s.PkgDst, s.PkgName) pkgExists := util.IsExists(s.PkgDst, false) if !pkgExists { - if s.rmOldDir && s.Ctx.String("artifact-mode") != util.ArtifactModeOnline { + if s.rmOldDir { if err := removeOldDir(pwd, pkg); err != nil { return err } } - switch { - case s.Ctx.String("artifact-mode") == util.ArtifactModeOnline && len(pkg.ArtifactUrl) > 0: - if err := s.updateArtifact(); err != nil { - return err - } - case s.Ctx.String("artifact-mode") == util.ArtifactModeOnline && len(pkg.ArtifactUrl) == 0: - zap.S().Warnf("overriding %s component in inventory section "+ - "%s file is not allowed when using %s artifact mode", - s.PkgName, util.TenantProjectFile, util.ArtifactModeOnline) - return nil - default: - if err := s.download(silent); err != nil { - return err - } + if err := s.download(silent); err != nil { + return err } } @@ -593,8 +439,7 @@ func updateDependencies(conf *config.Config, ctx *cli.Context, silent bool) erro pwd := util.GetPwdPath(TenantPrDependenciesDir) for key, val := range conf.Dependencies { - spec := &SpecDownload{Conf: conf, Ctx: ctx, PkgDst: pwd, - Artifact: &ArtifactSpec{Url: val.ArtifactUrl}, rmOldDir: true} + spec := &SpecDownload{Conf: conf, Ctx: ctx, PkgDst: pwd, rmOldDir: true} if err := spec.batchUpdate(pwd, val, silent); err != nil { return err } @@ -623,7 +468,7 @@ func updateClusters(conf *config.Config, ctx *cli.Context, silent bool) error { pwd := util.GetPwdPath(TenantPrInvClustersDir) for key, val := range conf.Clusters { - spec := &SpecDownload{Conf: conf, Ctx: ctx, PkgDst: pwd, Artifact: &ArtifactSpec{}, rmOldDir: true} + spec := &SpecDownload{Conf: conf, Ctx: ctx, PkgDst: pwd, rmOldDir: true} if err := spec.batchUpdate(pwd, *val, silent); err != nil { return err } @@ -638,7 +483,7 @@ func updateHooks(conf *config.Config, ctx *cli.Context, silent bool) error { pwd := util.GetPwdPath(TenantPrInvHooksDir) for key, val := range conf.HooksMapping { - spec := &SpecDownload{Conf: conf, Ctx: ctx, PkgDst: pwd, Artifact: &ArtifactSpec{}} + spec := &SpecDownload{Conf: conf, Ctx: ctx, PkgDst: pwd} if err := spec.batchUpdate(pwd, *val.Package, silent); err != nil { return err } diff --git a/cmd/release.go b/cmd/release.go index 4e9a7fc..261ccec 100644 --- a/cmd/release.go +++ b/cmd/release.go @@ -175,7 +175,7 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(rc.Conf.AWSSharedCredentialsFile(rc.Conf.Profile), ""), // Needed to set the AWS region to force the connection session region for the helm S3 plugin, // if AWS_DEFAULT_REGION and AWS_REGION cannot be trusted. - "HELM_S3_REGION="+rc.Conf.S3ChartsRepoRegion, + //"HELM_S3_REGION="+rc.Conf.S3ChartsRepoRegion, ) if _, ok := rc.Conf.Env["ROOT_DOMAIN"]; ok { @@ -201,12 +201,6 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { envs = append(envs, "K3D_CLUSTER="+strconv.FormatBool(rc.K3DCluster)) } - // needed if not used artifact mode - var sensKeyWords []string - if rc.Ctx.String("artifact-mode") == util.ArtifactModeDefault { - sensKeyWords = []string{rc.Conf.GitHubToken} - } - return &util.SpecCMD{ Args: append([]string{"--environment", rc.Conf.Environment, "--log-level", rc.Ctx.String("helmfile-log-level")}, args...), @@ -215,7 +209,7 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { Dir: rc.WorkDir, Envs: envs, Debug: true, - SensKeyWords: sensKeyWords, + SensKeyWords: []string{rc.Conf.GitHubToken}, } } @@ -231,7 +225,7 @@ func (rc *ReleaseCommands) kubeConfig() *util.SpecCMD { } func (rc *ReleaseCommands) releaseMiddleware() error { - if len(rc.Conf.Dependencies) == 0 && rc.Ctx.String("artifact-mode") == util.ArtifactModeDefault { + if len(rc.Conf.Dependencies) == 0 { if err := os.RemoveAll(filepath.Join(rc.WorkDir, TenantPrDependenciesDir)); err != nil { return err } @@ -650,7 +644,7 @@ func (sr *SpecRelease) checkStatusRelease() error { func releaseHelmfileAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -703,7 +697,7 @@ func releaseHelmfileAction(conf *config.Config) cli.ActionFunc { func releaseRollbackAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -740,7 +734,7 @@ func releaseRollbackAction(conf *config.Config) cli.ActionFunc { func releaseUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } diff --git a/cmd/secret.go b/cmd/secret.go index 6ed016a..e25bc97 100644 --- a/cmd/secret.go +++ b/cmd/secret.go @@ -379,7 +379,7 @@ func (sc *SecretCommands) runHelmSecretsCMD(secretFilePath string, returnCMDErro func secretMgrEncryptDecryptAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -406,7 +406,7 @@ func secretMgrGenerateAction(conf *config.Config) cli.ActionFunc { } func secretKeysCreateAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -444,7 +444,7 @@ func secretKeysUploadAction(conf *config.Config) cli.ActionFunc { func secretAction(conf *config.Config, action func(secretRunner SecretRunner) error) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateArtifactModeDefault(c, ""); err != nil { + if err := util.ValidateGitHubToken(c, ""); err != nil { return err } diff --git a/config/config.go b/config/config.go index f585238..f7846f1 100644 --- a/config/config.go +++ b/config/config.go @@ -28,11 +28,9 @@ type Config struct { Tenant string `yaml:"tenant,omitempty"` Environment string `yaml:"environment,omitempty"` ConfigFrom string `yaml:"config-from,omitempty"` - ArtifactMode string `yaml:"artifact-mode,omitempty"` RootDomain string `yaml:"root-domain,omitempty"` CloudflareToken string `yaml:"cloudflare-token,omitempty"` GitHubToken string `yaml:"github-token,omitempty"` - S3ChartsRepoRegion string `yaml:"s3-charts-repo-region"` ClusterProvider string `yaml:"cluster-provider"` SlackNotifications bool `yaml:"slack-notifications"` SlackWebHook string `yaml:"slack-webhook,omitempty"` @@ -71,7 +69,6 @@ type Package struct { Url string `yaml:"url,omitempty"` Checksum string `yaml:"checksum,omitempty"` Artifacts []string `yaml:"-"` - ArtifactUrl string `yaml:"artifact-url,omitempty"` HelmfileTenant string `yaml:"-"` OsLinux string `yaml:"os-linux,omitempty"` OsMac string `yaml:"os-mac,omitempty"` @@ -308,15 +305,6 @@ func (pf *ProjectFile) parseProjectFileData() error { if err != nil { return err } - - if len(strings.Split(pf.Dependencies[key].Name, ".")) > 0 { - pf.Dependencies[key].HelmfileTenant = strings.Split(pf.Dependencies[key].Name, ".")[0] - } - - pf.Dependencies[key].ArtifactUrl, err = pf.ParseTemplate(template.New("Dependencies"), pf.Dependencies[key], dep.ArtifactUrl) - if err != nil { - return err - } } for key, provider := range pf.Clusters { diff --git a/docs/configuration/project-management/preparation-of-project-repository.md b/docs/configuration/project-management/preparation-of-project-repository.md index 7c4cc8b..fe93712 100644 --- a/docs/configuration/project-management/preparation-of-project-repository.md +++ b/docs/configuration/project-management/preparation-of-project-repository.md @@ -37,10 +37,7 @@ and contains the following main sections: # Required, dependencies upstream project's repository version in `SemVer2` format, also can be a branch name or a commit hash. version: # Required, dependencies upstream project's repository URL. - url: git::https://github.com//{{.Name}}.git?ref={{.Version}} - # Optional, if it is expected that the dependency will be downloaded as an artifact from another storage. - artifact-url: https://-{{.HelmfileTenant}}-artifacts-infra.s3..amazonaws.com/{{.Version}}/{{.HelmfileTenant}}-{{.Version}}.tar.gz - + url: git::https://github.com//{{.Name}}.git?ref={{.Version}} # Optional, needed if you want automatic generation of the project structure from scratch. spec: # Required, list of available environments of the project (Git branches). @@ -114,7 +111,6 @@ project: - name: deps.bootstrap.infra version: v2.17.0 url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} - artifact-url: https://edenlabllc-{{.HelmfileTenant}}-artifacts-infra.s3.eu-north-1.amazonaws.com/{{.Version}}/{{.HelmfileTenant}}-{{.Version}}.tar.gz spec: environments: - develop diff --git a/docs/configuration/rmk-configuration-management.md b/docs/configuration/rmk-configuration-management.md index deea571..10c01b5 100644 --- a/docs/configuration/rmk-configuration-management.md +++ b/docs/configuration/rmk-configuration-management.md @@ -103,45 +103,3 @@ rmk config delete ``` > When deleting the current RMK configuration, the respective AWS profile files will be deleted as well. - -## Use upstream artifact for the downstream project's repository - -RMK supports downloading an upstream project's artifact using additional "license" AWS credentials. -To switch RMK to the artifact usage mode, you need to use additional flags when initializing the RMK configuration -for the current project. Additionally, before starting the initialization, you need to install the required version -of the upstream project to which you want to update. -For example: - -```yaml -project: - dependencies: - - name: deps.bootstrap.infra - version: v2.17.0 - url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} - artifact-url: https://edenlabllc-{{.HelmfileTenant}}-artifacts-infra.s3.eu-north-1.amazonaws.com/{{.Version}}/{{.HelmfileTenant}}-{{.Version}}.tar.gz - # ... -``` - -> The `artifact-url` field is required and contains the artifact URL generation template which consists -> of the following [fields](project-management/preparation-of-project-repository.md#projectyaml). - -Set the `version` field to the version of the upstream project for the current project. For example: - -```shell -# artifact usage modes: none|online|offline (default: "none") -rmk config init --artifact-mode=online -``` - -> Currently, only two artifact modes are supported: -> - `none`: The standard mode of RMK which is used for development normally, the codebase will be downloaded from GitHub repositories. -> The mode does not require the presence of the special "license" credentials. -> - `online`: Switches RMK to work with artifacts. In this mode, RMK will not use any credentials for GitHub -> (e.g., personal access tokens), but will request additional license AWS credentials to download and unpack -> the artifact from a repository like AWS S3. -> The `offline` mode is currently unsupported and will be implemented in future releases. - -To change the "license" AWS credentials when in the online artifact mode, use the following command: - -```shell -rmk config init --aws-reconfigure-artifact-license -``` diff --git a/util/dictionary.go b/util/dictionary.go index 8a4b230..6b29a1b 100644 --- a/util/dictionary.go +++ b/util/dictionary.go @@ -2,19 +2,17 @@ package util const ( AWSClusterProvider = "aws" + AzureClusterProvider = "azure" AWSECRHost = "288509344804.dkr.ecr.eu-north-1.amazonaws.com" AWSECRRegion = "eu-north-1" AWSECRUserName = "AWS" - ArtifactDownloadDir = "dist" - ArtifactModeDefault = "none" - ArtifactModeOffline = "offline" - ArtifactModeOnline = "online" GitSSHPrivateKey = ".ssh/id_rsa" GlobalsFileName = "globals.yaml.gotmpl" HelmfileFileName = "helmfile.yaml" HelmfileGoTmplName = HelmfileFileName + ".gotmpl" HelpFlagFull = "--help" K3DConfigPrefix = "k3d" + LocalClusterProvider = K3DConfigPrefix RMKBin = "rmk" RMKBucketName = "edenlabllc-rmk" RMKBucketRegion = "eu-north-1" @@ -25,7 +23,6 @@ const ( ReadmeFileName = "README.md" RegionException = "us-east-1" ReleasesFileName = "releases.yaml" - S3ChartsRepoRegion = "eu-north-1" SecretSpecFile = ".spec.yaml.gotmpl" SopsAgeKeyExt = ".txt" SopsAgeKeyFile = ".keys.txt" diff --git a/util/validate.go b/util/validate.go index f010df9..d3668f3 100644 --- a/util/validate.go +++ b/util/validate.go @@ -6,8 +6,8 @@ import ( "github.com/urfave/cli/v2" ) -func ValidateArtifactModeDefault(c *cli.Context, errorMsg string) error { - if c.String("artifact-mode") == ArtifactModeDefault && !c.IsSet("github-token") { +func ValidateGitHubToken(c *cli.Context, errorMsg string) error { + if !c.IsSet("github-token") { if errorMsg == "" { return fmt.Errorf(ConfigNotInitializedErrorText) } else { From 8d4acd500e117b0ce47b509b0a737059c099eb87 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Thu, 8 Aug 2024 16:03:18 +0200 Subject: [PATCH 04/39] #29 - draft refactoring 'rmk config init' --- cmd/commands.go | 3 +- cmd/config.go | 192 ++++++++++++++++++++++++++--------------------- cmd/flags.go | 12 ++- config/config.go | 2 +- 4 files changed, 113 insertions(+), 96 deletions(-) diff --git a/cmd/commands.go b/cmd/commands.go index cc1ccc0..ad55098 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -9,14 +9,13 @@ import ( "rmk/config" "rmk/git_handler" - "rmk/providers/aws_provider" "rmk/util" ) type Flags map[string][]cli.Flag func Commands() []*cli.Command { - conf := &config.Config{AwsConfigure: &aws_provider.AwsConfigure{}} + conf := &config.Config{} gitSpec := &git_handler.GitSpec{ DefaultBranches: []string{ git_handler.DefaultDevelop, diff --git a/cmd/config.go b/cmd/config.go index af0a4e4..c640afd 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -15,6 +15,7 @@ import ( "rmk/config" "rmk/git_handler" + "rmk/providers/aws_provider" "rmk/util" ) @@ -320,7 +321,7 @@ func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi } // Reset ConfigFrom value for config for current environment - conf.ConfigFrom = gitSpec.ID + conf.ConfigNameFrom = gitSpec.ID // Reset AWSMFAProfile value for config for current environment conf.AWSMFAProfile = "" // Reset AWSMFATokenExpiration value for config for current environment @@ -359,74 +360,81 @@ func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi } func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_handler.GitSpec) error { - if len(c.String("config-from-environment")) > 0 { - configPath := util.GetHomePath(util.RMKDir, util.RMKConfig, - gitSpec.RepoPrefixName+"-"+c.String("config-from-environment")+".yaml") + // TODO: A possible solution is to check the current cluster provider with from and prohibit such inheritance + // currentClusterProvider := c.String("cluster-provider") - if err := conf.ReadConfigFile(configPath); err != nil { - zap.S().Errorf("RMK config %s.yaml not initialized, please checkout to branch %s "+ - "and run command 'rmk config init' with specific parameters", - c.String("config-from-environment"), c.String("config-from-environment")) - return err - } + if len(c.String("config-from")) > 0 { + configPath := util.GetHomePath(util.RMKDir, util.RMKConfig, c.String("config-from")+".yaml") - if err := c.Set("config-from", conf.Name); err != nil { - return err - } - - // Delete regular profile - if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(gitSpec.ID), "")); err != nil { + if err := conf.ReadConfigFile(configPath); err != nil { + zap.S().Errorf("RMK config %s.yaml not initialized, please check RMK configs of exists "+ + "via command 'rmk config list' and run command 'rmk config init' with specific parameters", + c.String("config-from")) return err } - if len(conf.AWSMFAProfile) > 0 && len(conf.AWSMFATokenExpiration) > 0 { - regularProfile := conf.Profile + if c.String("cluster-provider") == util.AWSClusterProvider { + conf.AwsConfigure = new(aws_provider.AwsConfigure) - // Get MFA profile credentials. - conf.AwsConfigure.Profile = conf.AWSMFAProfile - if err := conf.GetAWSCredentials(); err != nil { + if err := c.Set("config-name-from", conf.Name); err != nil { return err } - // Copy MFA profile for current environment - conf.AwsConfigure.Profile = regularProfile - if err := newConfigCommands(conf, c, util.GetPwdPath("")).copyAWSProfile(gitSpec.ID); err != nil { - return err - } - } else { - // Delete config MFA profile - if err := os.RemoveAll(strings.Join(conf.AWSSharedConfigFile(gitSpec.ID+"-mfa"), "")); err != nil { + // Delete regular profile + if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(gitSpec.ID), "")); err != nil { return err } - // Delete credentials MFA profile - if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(gitSpec.ID+"-mfa"), "")); err != nil { - return err + if len(conf.AWSMFAProfile) > 0 && len(conf.AWSMFATokenExpiration) > 0 { + regularProfile := conf.Profile + + // Get MFA profile credentials. + conf.AwsConfigure.Profile = conf.AWSMFAProfile + if err := conf.GetAWSCredentials(); err != nil { + return err + } + + // Copy MFA profile for current environment + conf.AwsConfigure.Profile = regularProfile + if err := newConfigCommands(conf, c, util.GetPwdPath("")).copyAWSProfile(gitSpec.ID); err != nil { + return err + } + } else { + // Delete config MFA profile + if err := os.RemoveAll(strings.Join(conf.AWSSharedConfigFile(gitSpec.ID+"-mfa"), "")); err != nil { + return err + } + + // Delete credentials MFA profile + if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(gitSpec.ID+"-mfa"), "")); err != nil { + return err + } + + // Get regular profile credentials. + if err := conf.GetAWSCredentials(); err != nil { + return err + } + + // Copy regular profile for current environment + if err := newConfigCommands(conf, c, util.GetPwdPath("")).copyAWSProfile(gitSpec.ID); err != nil { + return err + } } - // Get regular profile credentials. - if err := conf.GetAWSCredentials(); err != nil { + // Reset AWSMFAProfile value for config for current environment + if err := c.Set("aws-mfa-profile", ""); err != nil { return err } - // Copy regular profile for current environment - if err := newConfigCommands(conf, c, util.GetPwdPath("")).copyAWSProfile(gitSpec.ID); err != nil { + // Reset AWSMFATokenExpiration value for config for current environment + if err := c.Set("aws-mfa-token-expiration", ""); err != nil { return err } - } - // Reset AWSMFAProfile value for config for current environment - if err := c.Set("aws-mfa-profile", ""); err != nil { - return err + conf.AwsConfigure.Profile = gitSpec.ID } - // Reset AWSMFATokenExpiration value for config for current environment - if err := c.Set("aws-mfa-token-expiration", ""); err != nil { - return err - } - - conf.ConfigFrom = c.String("config-from") - conf.AwsConfigure.Profile = gitSpec.ID + conf.ConfigNameFrom = c.String("config-name-from") return nil } @@ -439,14 +447,17 @@ func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_ return err } - if !c.IsSet("config-from") { - if err := c.Set("config-from", gitSpec.ID); err != nil { + if !c.IsSet("config-name-from") { + if err := c.Set("config-name-from", gitSpec.ID); err != nil { return err } } - conf.ConfigFrom = c.String("config-from") - conf.AwsConfigure.Profile = gitSpec.ID + if c.String("cluster-provider") == util.AWSClusterProvider { + conf.AwsConfigure = new(aws_provider.AwsConfigure) + } + + conf.ConfigNameFrom = c.String("config-name-from") conf.CloudflareToken = c.String("cloudflare-token") conf.GitHubToken = c.String("github-token") @@ -459,25 +470,28 @@ func configDeleteAction(conf *config.Config) cli.ActionFunc { return err } - // Delete MFA profile - if len(conf.AWSMFAProfile) > 0 && len(conf.AWSMFATokenExpiration) > 0 { - if err := os.RemoveAll(strings.Join(conf.AWSSharedConfigFile(conf.AWSMFAProfile), "")); err != nil { - return err + // TODO: It is necessary to think about whether to delete unconditionally or check taking into account the AWS provider. + if c.String("cluster-provider") == util.AWSClusterProvider { + // Delete MFA profile + if len(conf.AWSMFAProfile) > 0 && len(conf.AWSMFATokenExpiration) > 0 { + if err := os.RemoveAll(strings.Join(conf.AWSSharedConfigFile(conf.AWSMFAProfile), "")); err != nil { + return err + } + + if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(conf.AWSMFAProfile), "")); err != nil { + return err + } } - if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(conf.AWSMFAProfile), "")); err != nil { + // Delete config MFA profile + if err := os.RemoveAll(strings.Join(conf.AWSSharedConfigFile(conf.Profile), "")); err != nil { return err } - } - // Delete config MFA profile - if err := os.RemoveAll(strings.Join(conf.AWSSharedConfigFile(conf.Profile), "")); err != nil { - return err - } - - // Delete credentials MFA profile - if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(conf.Profile), "")); err != nil { - return err + // Delete credentials MFA profile + if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(conf.Profile), "")); err != nil { + return err + } } if err := os.RemoveAll(c.String("config")); err != nil { @@ -503,6 +517,8 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act conf.Name = gitSpec.ID conf.Tenant = gitSpec.RepoPrefixName conf.Environment = gitSpec.DefaultBranch + conf.ClusterProvider = c.String("cluster-provider") + conf.ProgressBar = c.Bool("progress-bar") zap.S().Infof("RMK will use values for %s environment", conf.Environment) if c.Bool("slack-notifications") { @@ -517,28 +533,32 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act } } - conf.ProgressBar = c.Bool("progress-bar") - conf.Terraform.BucketKey = util.TenantBucketKey - conf.ClusterProvisionerSL = c.Bool("cluster-provisioner-state-locking") - conf.ClusterProvider = c.String("cluster-provider") - conf.AWSMFAProfile = c.String("aws-mfa-profile") - conf.AWSMFATokenExpiration = c.String("aws-mfa-token-expiration") - conf.AWSECRHost = c.String("aws-ecr-host") - conf.AWSECRRegion = c.String("aws-ecr-region") - conf.AWSECRUserName = c.String("aws-ecr-user-name") - - // AWS Profile init configuration with support MFA - if err := initAWSProfile(c, conf, gitSpec); err != nil { - return err - } + switch conf.ClusterProvider { + case util.AWSClusterProvider: + conf.Terraform.BucketKey = util.TenantBucketKey + conf.ClusterProvisionerSL = c.Bool("cluster-provisioner-state-locking") + conf.AwsConfigure.Profile = gitSpec.ID + conf.AWSMFAProfile = c.String("aws-mfa-profile") + conf.AWSMFATokenExpiration = c.String("aws-mfa-token-expiration") + conf.AWSECRHost = c.String("aws-ecr-host") + conf.AWSECRRegion = c.String("aws-ecr-region") + conf.AWSECRUserName = c.String("aws-ecr-user-name") - //Formation of a unique bucket name, consisting of the prefix tenant of the repository, - //constant and the first 3 and last 2 numbers AWS account id - awsUID := conf.AccountID[0:3] + conf.AccountID[len(conf.AccountID)-2:] - conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+awsUID) - conf.SopsBucketName = conf.Tenant + "-" + util.SopsRootName + "-" + awsUID - conf.Terraform.BucketName = conf.Tenant + "-" + util.TenantBucketName + "-" + awsUID - conf.Terraform.DDBTableName = util.TenantDDBTablePrefix + "-" + awsUID + // AWS Profile init configuration with support MFA + if err := initAWSProfile(c, conf, gitSpec); err != nil { + return err + } + + //Formation of a unique bucket name, consisting of the prefix tenant of the repository, + //constant and the first 3 and last 2 numbers AWS account id + awsUID := conf.AccountID[0:3] + conf.AccountID[len(conf.AccountID)-2:] + conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+awsUID) + conf.SopsBucketName = conf.Tenant + "-" + util.SopsRootName + "-" + awsUID + conf.Terraform.BucketName = conf.Tenant + "-" + util.TenantBucketName + "-" + awsUID + conf.Terraform.DDBTableName = util.TenantDDBTablePrefix + "-" + awsUID + case util.LocalClusterProvider: + conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+util.LocalClusterProvider) + } if err := conf.InitConfig(true).SetRootDomain(c, gitSpec.ID); err != nil { return err diff --git a/cmd/flags.go b/cmd/flags.go index 753abf0..a02b758 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -4,7 +4,6 @@ import ( "github.com/urfave/cli/v2" "github.com/urfave/cli/v2/altsrc" - "rmk/git_handler" "rmk/util" ) @@ -72,16 +71,15 @@ func flagsConfig() []cli.Flag { }, altsrc.NewStringFlag( &cli.StringFlag{ - Name: "config-from", + Name: "config-name-from", Hidden: true, }, ), &cli.StringFlag{ - Name: "config-from-environment", - Usage: "inheritance of RMK config credentials from environments: " + - git_handler.DefaultDevelop + ", " + git_handler.DefaultStaging + ", " + git_handler.DefaultProduction, - Aliases: []string{"cfe"}, - EnvVars: []string{"RMK_CONFIG_FROM_ENVIRONMENT"}, + Name: "config-from", + Usage: "inheritance of RMK config credentials from another RMK config", + Aliases: []string{"cf"}, + EnvVars: []string{"RMK_CONFIG_FROM"}, }, altsrc.NewStringFlag( &cli.StringFlag{ diff --git a/config/config.go b/config/config.go index f7846f1..800c8cc 100644 --- a/config/config.go +++ b/config/config.go @@ -27,7 +27,7 @@ type Config struct { Name string `yaml:"name,omitempty"` Tenant string `yaml:"tenant,omitempty"` Environment string `yaml:"environment,omitempty"` - ConfigFrom string `yaml:"config-from,omitempty"` + ConfigNameFrom string `yaml:"config-name-from,omitempty"` RootDomain string `yaml:"root-domain,omitempty"` CloudflareToken string `yaml:"cloudflare-token,omitempty"` GitHubToken string `yaml:"github-token,omitempty"` From 19ff5b4f4cf9e8b57ad9b35578aeefe9560f32fe Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 3 Sep 2024 16:58:58 +0000 Subject: [PATCH 05/39] [skip docs commands update] Update commands documentation --- docs/commands.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index 02a85e8..4a48db8 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -18,8 +18,8 @@ rmk Command line tool for reduced management of the provision of Kubernetes clusters in different environments and management of service releases. **BuiltBy:** goreleaser
-**Commit:** 3199141
-**Date:** 2024-09-03T16:36:53Z
+**Commit:** 0370089
+**Date:** 2024-09-03T16:58:55Z
**Target:** linux_amd64 **Usage**: From 37adca787d70df62b6e936465c35b39e32a76aaf Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 3 Sep 2024 17:13:47 +0000 Subject: [PATCH 06/39] [skip docs commands update] Update commands documentation --- docs/commands.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index 4a48db8..b9cdd31 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -18,8 +18,8 @@ rmk Command line tool for reduced management of the provision of Kubernetes clusters in different environments and management of service releases. **BuiltBy:** goreleaser
-**Commit:** 0370089
-**Date:** 2024-09-03T16:58:55Z
+**Commit:** b0175ad
+**Date:** 2024-09-03T17:13:45Z
**Target:** linux_amd64 **Usage**: From c160fea1fb7e95688713d046da449efec1cb0c48 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 4 Sep 2024 08:23:17 +0000 Subject: [PATCH 07/39] [skip docs commands update] Update commands documentation --- docs/commands.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index 2e29222..b26a331 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -18,8 +18,8 @@ rmk Command line tool for reduced management of the provision of Kubernetes clusters in different environments and management of service releases. **BuiltBy:** goreleaser
-**Commit:** 7a0f199
-**Date:** 2024-09-04T07:46:16Z
+**Commit:** 0ed359c
+**Date:** 2024-09-04T08:23:15Z
**Target:** linux_amd64 **Usage**: From 1c457c98adc8885c22bc696d3f54804dfffa65dc Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 4 Sep 2024 08:25:32 +0000 Subject: [PATCH 08/39] [skip docs commands update] Update commands documentation --- docs/commands.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index b26a331..4baab80 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -18,8 +18,8 @@ rmk Command line tool for reduced management of the provision of Kubernetes clusters in different environments and management of service releases. **BuiltBy:** goreleaser
-**Commit:** 0ed359c
-**Date:** 2024-09-04T08:23:15Z
+**Commit:** 6e50d9f
+**Date:** 2024-09-04T08:25:29Z
**Target:** linux_amd64 **Usage**: From 7da2c5f6f2afe8ba435dadebea182848a831e97f Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 25 Sep 2024 16:45:19 +0200 Subject: [PATCH 09/39] #29 - deprecate Terraform commands, add commands for managing CAPI --- cmd/cluster.go | 483 +------------------------------------- cmd/commands.go | 97 ++------ cmd/config.go | 29 +-- cmd/container_registry.go | 91 ------- cmd/flags.go | 102 ++------ cmd/k3d.go | 61 ++--- cmd/project.go | 2 +- cmd/release.go | 42 ++-- cmd/secret.go | 6 +- config/config.go | 98 +------- util/dictionary.go | 80 +++---- util/system.go | 15 ++ 12 files changed, 144 insertions(+), 962 deletions(-) delete mode 100644 cmd/container_registry.go diff --git a/cmd/cluster.go b/cmd/cluster.go index 2905bbc..5a53a63 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -1,44 +1,29 @@ package cmd import ( - "fmt" - "os" - "path/filepath" "strings" "github.com/urfave/cli/v2" - "go.uber.org/zap" "rmk/config" "rmk/util" ) -type StateRunner interface { - clusterStateDelete() error - clusterStateList() error - clusterStateRefresh() error -} - type ClusterCommands struct { - Conf *config.Config - Ctx *cli.Context - SpecCMDs []*util.SpecCMD - PlanFile string - WorkDir string + *ReleaseCommands } -func (cc *ClusterCommands) clusterRootDir() (string, error) { - for _, provider := range cc.Conf.Clusters { - if strings.HasPrefix(provider.Name, cc.Conf.ClusterProvider) { - return provider.DstPath, nil - } - } - - return "", fmt.Errorf("destination path for cluster provider %s not found", cc.Conf.ClusterProvider) +func newClusterCommands(conf *config.Config, ctx *cli.Context, workDir string) *ClusterCommands { + return &ClusterCommands{&ReleaseCommands{Conf: conf, Ctx: ctx, WorkDir: workDir}} } func (cc *ClusterCommands) awsEks() *util.SpecCMD { return &util.SpecCMD{ + Envs: []string{ + "AWS_PROFILE=" + cc.Conf.Profile, + "AWS_CONFIG_FILE=" + strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""), + "AWS_SHARED_CREDENTIALS_FILE=" + strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""), + }, Args: []string{"eks", "--region", cc.Conf.Region, "update-kubeconfig", @@ -54,453 +39,9 @@ func (cc *ClusterCommands) awsEks() *util.SpecCMD { } } -func (cc *ClusterCommands) runBatchCMD() error { - if err := os.Unsetenv("AWS_PROFILE"); err != nil { - return err - } - - for _, val := range cc.SpecCMDs { - val.Envs = []string{ - "AWS_PROFILE=" + cc.Conf.Profile, - "AWS_CONFIG_FILE=" + strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""), - "AWS_SHARED_CREDENTIALS_FILE=" + strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""), - } - if err := val.AddEnv(); err != nil { - return err - } - - if err := val.ExecCMD(); err != nil { - if val.Debug { - zap.S().Debugf("command: %s", val.CommandStr) - zap.S().Debugf("path: %s", val.Dir) - for _, v := range val.Envs { - zap.S().Debugf("env: %s", v) - } - } - - return err - } - - if val.Debug { - zap.S().Debugf("command: %s", val.CommandStr) - zap.S().Debugf("path: %s", val.Dir) - for _, v := range val.Envs { - zap.S().Debugf("env: %s", v) - } - } - } - - return nil -} - -func (cc *ClusterCommands) initialize() *util.SpecCMD { - args := []string{ - "init", - "-backend=true", - "-backend-config=region=" + cc.Conf.Region, - "-backend-config=bucket=" + cc.Conf.Terraform.BucketName, - "-backend-config=key=" + cc.Conf.Terraform.BucketKey, - } - if cc.Conf.ClusterProvisionerSL { - args = append(args, "-backend-config=dynamodb_table="+cc.Conf.Terraform.DDBTableName) - } - - args = append(args, "-reconfigure") - return &util.SpecCMD{ - Args: args, - Command: "terraform", - Ctx: cc.Ctx.Context, - Dir: cc.WorkDir, - Debug: true, - } -} - -func (cc *ClusterCommands) validate() *util.SpecCMD { - return &util.SpecCMD{ - Args: []string{"validate"}, - Command: "terraform", - Ctx: cc.Ctx.Context, - Dir: cc.WorkDir, - Debug: true, - } -} - -func (cc *ClusterCommands) workspace(args ...string) *util.SpecCMD { - return &util.SpecCMD{ - Args: append([]string{"workspace"}, args...), - Command: "terraform", - Ctx: cc.Ctx.Context, - Dir: cc.WorkDir, - Debug: true, - } -} - -func (cc *ClusterCommands) output(args ...string) *util.SpecCMD { - return &util.SpecCMD{ - Args: append([]string{"output"}, args...), - Command: "terraform", - Ctx: cc.Ctx.Context, - Dir: cc.WorkDir, - DisableStdOut: true, - Debug: false, - } -} - -func (cc *ClusterCommands) destroy() *util.SpecCMD { - return &util.SpecCMD{ - Args: []string{"destroy", "-auto-approve", - "-var=aws_account_id=" + cc.Conf.AccountID, - "-var=cloudflare_api_token=" + cc.Conf.CloudflareToken, - "-var=name=" + cc.Conf.Name, - "-var=region=" + cc.Conf.Region, - "-var=root_domain=" + cc.Conf.RootDomain, - "-var=terraform_bucket_key=" + cc.Conf.Terraform.BucketKey, - "-var=terraform_bucket_name=" + cc.Conf.Terraform.BucketName, - }, - Command: "terraform", - Ctx: cc.Ctx.Context, - Dir: cc.WorkDir, - Debug: true, - } -} - -func (cc *ClusterCommands) plan() *util.SpecCMD { - return &util.SpecCMD{ - Args: []string{ - "plan", - "-out=" + cc.PlanFile, - "-var=aws_account_id=" + cc.Conf.AccountID, - "-var=cloudflare_api_token=" + cc.Conf.CloudflareToken, - "-var=name=" + cc.Conf.Name, - "-var=region=" + cc.Conf.Region, - "-var=root_domain=" + cc.Conf.RootDomain, - "-var=terraform_bucket_key=" + cc.Conf.Terraform.BucketKey, - "-var=terraform_bucket_name=" + cc.Conf.Terraform.BucketName, - }, - Command: "terraform", - Ctx: cc.Ctx.Context, - Dir: cc.WorkDir, - Debug: true, - } -} - -func (cc *ClusterCommands) apply() *util.SpecCMD { - return &util.SpecCMD{ - Args: []string{"apply", cc.PlanFile}, - Command: "terraform", - Ctx: cc.Ctx.Context, - Dir: cc.WorkDir, - Debug: true, - } -} - -func (cc *ClusterCommands) listResources() *util.SpecCMD { - return &util.SpecCMD{ - Args: []string{"state", "list"}, - Command: "terraform", - Ctx: cc.Ctx.Context, - Dir: cc.WorkDir, - Debug: true, - } -} - -func (cc *ClusterCommands) refresh() *util.SpecCMD { - return &util.SpecCMD{ - Args: []string{"refresh", - "-var=aws_account_id=" + cc.Conf.AccountID, - "-var=cloudflare_api_token=" + cc.Conf.CloudflareToken, - "-var=name=" + cc.Conf.Name, - "-var=region=" + cc.Conf.Region, - "-var=root_domain=" + cc.Conf.RootDomain, - "-var=terraform_bucket_key=" + cc.Conf.Terraform.BucketKey, - "-var=terraform_bucket_name=" + cc.Conf.Terraform.BucketName, - }, - Command: "terraform", - Ctx: cc.Ctx.Context, - Dir: cc.WorkDir, - Debug: true, - } -} - -func (cc *ClusterCommands) state(args ...string) *util.SpecCMD { - return &util.SpecCMD{ - Args: append([]string{"state"}, args...), - Command: "terraform", - Ctx: cc.Ctx.Context, - Dir: cc.WorkDir, - Debug: true, - } -} - -func (cc *ClusterCommands) clusterContext() error { - cc.SpecCMDs = append(cc.SpecCMDs, cc.awsEks()) - return cc.runBatchCMD() -} - -func (cc *ClusterCommands) clusterDestroy() error { - checkWorkspace, err := cc.Conf.BucketKeyExists("", cc.Conf.Terraform.BucketName, "env:/"+cc.Conf.Name+"/tf.tfstate") - if err != nil { - return err - } - - if checkWorkspace { - cc.SpecCMDs = append(cc.SpecCMDs, cc.initialize(), cc.validate(), cc.workspace("select", cc.Conf.Name)) - if err := cc.runBatchCMD(); err != nil { - return err - } - - destroy := cc.destroy() - - match, err := util.WalkMatch( - util.GetPwdPath(util.TenantValuesDIR, "clusters", util.AWSClusterProvider, cc.Conf.Environment), - "*."+util.TerraformVarsExt, - ) - if err != nil { - return err - } - - for _, val := range match { - destroy.Args = append(destroy.Args, "-var-file="+val) - } - - if err := os.RemoveAll(cc.PlanFile); err != nil { - return err - } - - cc.SpecCMDs = append([]*util.SpecCMD{}, destroy, cc.workspace("select", "default"), - cc.workspace("delete", cc.Conf.Name)) - - return cc.runBatchCMD() - } else { - zap.S().Infof("Terraform cluster in workspace %s already deleted or not created", cc.Conf.Name) - return nil - } -} - -func (cc *ClusterCommands) clusterList() error { - cc.SpecCMDs = append(cc.SpecCMDs, cc.initialize(), cc.workspace("list")) - return cc.runBatchCMD() -} - -func (cc *ClusterCommands) clusterProvision() error { - var workspace *util.SpecCMD - - if err := os.MkdirAll(filepath.Join(cc.WorkDir, "plans"), 0755); err != nil { - zap.S().Fatal(err) - } - - checkWorkspace, err := cc.Conf.BucketKeyExists("", cc.Conf.Terraform.BucketName, "env:/"+cc.Conf.Name+"/tf.tfstate") - if err != nil { - zap.S().Fatal(err) - } - - if checkWorkspace { - workspace = cc.workspace("select", cc.Conf.Name) - } else { - workspace = cc.workspace("new", cc.Conf.Name) - } - - plan := cc.plan() - - match, err := util.WalkMatch( - util.GetPwdPath(util.TenantValuesDIR, "clusters", util.AWSClusterProvider, cc.Conf.Environment), - "*."+util.TerraformVarsExt, - ) - - for _, val := range match { - plan.Args = append(plan.Args, "-var-file="+val) - } - - if cc.Ctx.Bool("plan") { - cc.SpecCMDs = append(cc.SpecCMDs, cc.initialize(), cc.validate(), workspace, plan) - return cc.runBatchCMD() - } - - cc.SpecCMDs = append(cc.SpecCMDs, cc.initialize(), cc.validate(), workspace, plan, cc.apply()) - if err := cc.runBatchCMD(); err != nil { - return err - } - - rc := &ReleaseCommands{ - Conf: cc.Conf, - Ctx: cc.Ctx, - WorkDir: util.GetPwdPath(""), - UpdateContext: true, - } - - return rc.releaseKubeContext() -} - -func (cc *ClusterCommands) clusterStateDelete() error { - cc.SpecCMDs = append(cc.SpecCMDs, cc.state("rm", cc.Ctx.String("resource-address"))) - - return cc.runBatchCMD() -} - -func (cc *ClusterCommands) clusterStateList() error { - cc.SpecCMDs = append(cc.SpecCMDs, cc.state(cc.Ctx.Command.Name)) - - return cc.runBatchCMD() -} - -func (cc *ClusterCommands) clusterStateRefresh() error { - var workspace *util.SpecCMD - - checkWorkspace, err := cc.Conf.BucketKeyExists("", cc.Conf.Terraform.BucketName, "env:/"+cc.Conf.Name+"/tf.tfstate") - if err != nil { - zap.S().Fatal(err) - } - - if checkWorkspace { - workspace = cc.workspace("select", cc.Conf.Name) - } else { - workspace = cc.workspace("new", cc.Conf.Name) - } - - refresh := cc.refresh() - match, err := util.WalkMatch( - util.GetPwdPath(util.TenantValuesDIR, "clusters", util.AWSClusterProvider, cc.Conf.Environment), - "*."+util.TerraformVarsExt, - ) - if err != nil { - return err - } - - for _, val := range match { - refresh.Args = append(refresh.Args, "-var-file="+val) - } - - cc.SpecCMDs = append(cc.SpecCMDs, cc.initialize(), cc.validate(), workspace, refresh) - - return cc.runBatchCMD() -} - -func clusterDestroyAction(conf *config.Config) cli.ActionFunc { - return func(c *cli.Context) error { - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - - if err := util.ValidateNArg(c, 0); err != nil { - return err - } - - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { - return err - } - - cc := &ClusterCommands{ - Conf: conf, - Ctx: c, - } - - if pkgDst, err := cc.clusterRootDir(); err != nil { - return err - } else { - cc.WorkDir = filepath.Join(pkgDst, "terraform") - } - - cc.PlanFile = filepath.Join(cc.WorkDir, "plans", conf.Name+"__"+conf.Environment+".tfplan") - - return cc.clusterDestroy() - } -} - -func clusterListAction(conf *config.Config) cli.ActionFunc { - return func(c *cli.Context) error { - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - - if err := util.ValidateNArg(c, 0); err != nil { - return err - } - - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { - return err - } - - cc := &ClusterCommands{ - Conf: conf, - Ctx: c, - } - - if pkgDst, err := cc.clusterRootDir(); err != nil { - return err - } else { - cc.WorkDir = filepath.Join(pkgDst, "terraform") - } - - return cc.clusterList() - } -} - -func clusterProvisionAction(conf *config.Config) cli.ActionFunc { - return func(c *cli.Context) error { - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - - if err := util.ValidateNArg(c, 0); err != nil { - return err - } - - if err := resolveDependencies(conf.InitConfig(true), c, false); err != nil { - return err - } - - cc := &ClusterCommands{ - Conf: conf, - Ctx: c, - } - - if pkgDst, err := cc.clusterRootDir(); err != nil { - return err - } else { - cc.WorkDir = filepath.Join(pkgDst, "terraform") - } - - cc.PlanFile = filepath.Join(cc.WorkDir, "plans", conf.Name+"__"+conf.Environment+".tfplan") - - if err := cc.clusterProvision(); err != nil { - return err - } - - if err := conf.GetTerraformOutputs(); err != nil { - return err - } - - return conf.CreateConfigFile() - } -} - -func clusterStateAction(conf *config.Config, action func(stateRunner StateRunner) error) cli.ActionFunc { - return func(c *cli.Context) error { - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - - if err := util.ValidateNArg(c, 0); err != nil { - return err - } - - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { - return err - } - - cc := &ClusterCommands{ - Conf: conf, - Ctx: c, - } - - if pkgDst, err := cc.clusterRootDir(); err != nil { - return err - } else { - cc.WorkDir = filepath.Join(pkgDst, "terraform") - } - - return action(cc) - } +func (cc *ClusterCommands) awsClusterContext() error { + cc.SpecCMD = cc.awsEks() + return runner(cc).runCMD() } func clusterSwitchAction(conf *config.Config) cli.ActionFunc { @@ -513,7 +54,7 @@ func clusterSwitchAction(conf *config.Config) cli.ActionFunc { return err } - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { return err } diff --git a/cmd/commands.go b/cmd/commands.go index ad55098..db67807 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -24,11 +24,8 @@ func Commands() []*cli.Command { }, } flags := Flags{ - "clusterCRLogin": flagsClusterCRLogin(), "clusterK3DCreate": flagsClusterK3DCreate(), "clusterK3DImport": flagsClusterK3DImport(), - "clusterPlan": flagsClusterPlan(), - "clusterStateDelete": flagsClusterStateDelete(), "clusterSwitch": flagsClusterSwitch(), "config": flagsConfig(), "configList": flagsConfigList(), @@ -112,51 +109,33 @@ func Commands() []*cli.Command { Usage: "Cluster management", Subcommands: []*cli.Command{ { - Name: "container-registry", - Usage: "Container registry management", + Name: "capi", + Usage: "CAPI cluster management", Aliases: []string{"c"}, Category: "cluster", Subcommands: []*cli.Command{ { - Name: "login", - Usage: "Log in to container registry", - Before: readInputSourceWithContext(gitSpec, conf, flags["clusterCRLogin"]), - Flags: flags["clusterCRLogin"], - Category: "container-registry", + Name: "create", + Usage: "Create CAPI cluster", + Aliases: []string{"c"}, + Before: readInputSourceWithContext(gitSpec, conf, flags["clusterK3DCreate"]), + Flags: flags["clusterK3DCreate"], + Category: "capi", BashComplete: util.ShellCompleteCustomOutput, - Action: containerRegistryAction(conf, DockerRunner.dockerLogin), + Action: K3DCreateAction(conf), }, { - Name: "logout", - Usage: "Log out from container registry", + Name: "delete", + Usage: "Delete CAPI cluster", + Aliases: []string{"d"}, Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], - Category: "container-registry", + Category: "capi", BashComplete: util.ShellCompleteCustomOutput, - Action: containerRegistryAction(conf, DockerRunner.dockerLogout), + Action: K3DAction(conf, K3DRunner.createDeleteK3DCluster), }, }, }, - { - Name: "destroy", - Usage: "Destroy AWS cluster using Terraform", - Aliases: []string{"d"}, - Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), - Flags: flags["hidden"], - Category: "cluster", - BashComplete: util.ShellCompleteCustomOutput, - Action: clusterDestroyAction(conf), - }, - { - Name: "list", - Usage: "List all Terraform available workspaces", - Aliases: []string{"l"}, - Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), - Flags: flags["hidden"], - Category: "cluster", - BashComplete: util.ShellCompleteCustomOutput, - Action: clusterListAction(conf), - }, { Name: "k3d", Usage: "K3D cluster management", @@ -224,54 +203,6 @@ func Commands() []*cli.Command { }, }, }, - { - Name: "provision", - Usage: "Provision AWS cluster using Terraform", - Aliases: []string{"p"}, - Before: readInputSourceWithContext(gitSpec, conf, flags["clusterPlan"]), - Flags: flags["clusterPlan"], - Category: "cluster", - BashComplete: util.ShellCompleteCustomOutput, - Action: clusterProvisionAction(conf), - }, - { - Name: "state", - Usage: "State cluster management using Terraform", - Aliases: []string{"t"}, - Category: "cluster", - Subcommands: []*cli.Command{ - { - Name: "delete", - Usage: "Delete resource from Terraform state", - Aliases: []string{"d"}, - Before: readInputSourceWithContext(gitSpec, conf, flags["clusterStateDelete"]), - Flags: flags["clusterStateDelete"], - Category: "state", - BashComplete: util.ShellCompleteCustomOutput, - Action: clusterStateAction(conf, StateRunner.clusterStateDelete), - }, - { - Name: "list", - Usage: "List resources from Terraform state", - Aliases: []string{"l"}, - Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), - Flags: flags["hidden"], - Category: "state", - BashComplete: util.ShellCompleteCustomOutput, - Action: clusterStateAction(conf, StateRunner.clusterStateList), - }, - { - Name: "refresh", - Usage: "Update state file for AWS cluster using Terraform", - Aliases: []string{"r"}, - Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), - Flags: flags["hidden"], - Category: "state", - BashComplete: util.ShellCompleteCustomOutput, - Action: clusterStateAction(conf, StateRunner.clusterStateRefresh), - }, - }, - }, { Name: "switch", Usage: "Switch Kubernetes context for tenant cluster", diff --git a/cmd/config.go b/cmd/config.go index c640afd..5fb209f 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -374,8 +374,6 @@ func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_ } if c.String("cluster-provider") == util.AWSClusterProvider { - conf.AwsConfigure = new(aws_provider.AwsConfigure) - if err := c.Set("config-name-from", conf.Name); err != nil { return err } @@ -458,7 +456,6 @@ func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_ } conf.ConfigNameFrom = c.String("config-name-from") - conf.CloudflareToken = c.String("cloudflare-token") conf.GitHubToken = c.String("github-token") return nil @@ -535,14 +532,9 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act switch conf.ClusterProvider { case util.AWSClusterProvider: - conf.Terraform.BucketKey = util.TenantBucketKey - conf.ClusterProvisionerSL = c.Bool("cluster-provisioner-state-locking") conf.AwsConfigure.Profile = gitSpec.ID conf.AWSMFAProfile = c.String("aws-mfa-profile") conf.AWSMFATokenExpiration = c.String("aws-mfa-token-expiration") - conf.AWSECRHost = c.String("aws-ecr-host") - conf.AWSECRRegion = c.String("aws-ecr-region") - conf.AWSECRUserName = c.String("aws-ecr-user-name") // AWS Profile init configuration with support MFA if err := initAWSProfile(c, conf, gitSpec); err != nil { @@ -554,13 +546,12 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act awsUID := conf.AccountID[0:3] + conf.AccountID[len(conf.AccountID)-2:] conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+awsUID) conf.SopsBucketName = conf.Tenant + "-" + util.SopsRootName + "-" + awsUID - conf.Terraform.BucketName = conf.Tenant + "-" + util.TenantBucketName + "-" + awsUID - conf.Terraform.DDBTableName = util.TenantDDBTablePrefix + "-" + awsUID case util.LocalClusterProvider: + conf.AwsConfigure = &aws_provider.AwsConfigure{} conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+util.LocalClusterProvider) } - if err := conf.InitConfig(true).SetRootDomain(c, gitSpec.ID); err != nil { + if err := conf.InitConfig().SetRootDomain(c, gitSpec.ID); err != nil { return err } @@ -569,18 +560,6 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act } if conf.ClusterProvider == util.AWSClusterProvider { - if conf.ClusterProvisionerSL { - // create dynamodb table for backend terraform - if err := conf.CreateDynamoDBTable(conf.Terraform.DDBTableName); err != nil { - return err - } - } - - // create s3 bucket for backend terraform - if err := conf.CreateBucket(conf.Terraform.BucketName); err != nil { - return err - } - //create s3 bucket for sops age keys if err := conf.CreateBucket(conf.SopsBucketName); err != nil { return err @@ -590,7 +569,7 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act return err } - if err := resolveDependencies(conf.InitConfig(true), c, false); err != nil { + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { return err } @@ -599,7 +578,7 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act return nil } - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { return err } diff --git a/cmd/container_registry.go b/cmd/container_registry.go deleted file mode 100644 index f690dac..0000000 --- a/cmd/container_registry.go +++ /dev/null @@ -1,91 +0,0 @@ -package cmd - -import ( - "fmt" - "strings" - - "github.com/urfave/cli/v2" - "go.uber.org/zap" - - "rmk/config" - "rmk/util" -) - -type DockerRunner interface { - dockerLogin() error - dockerLogout() error -} - -type CRCommands struct { - *ReleaseCommands -} - -func newCRCommands(conf *config.Config, ctx *cli.Context, workDir string) *CRCommands { - return &CRCommands{&ReleaseCommands{Conf: conf, Ctx: ctx, WorkDir: workDir}} -} - -func (cr *CRCommands) docker(args ...string) *util.SpecCMD { - return &util.SpecCMD{ - Args: append([]string{}, args...), - Command: "docker", - Dir: cr.WorkDir, - Ctx: cr.Ctx.Context, - DisableStdOut: true, - Debug: false, - } -} - -func (cr *CRCommands) dockerLogin() error { - credentials, err := cr.Conf.AwsConfigure.GetECRCredentials(cr.Conf.AWSECRRegion) - if err != nil { - return err - } - - if token, ok := credentials[cr.Conf.AWSECRUserName]; !ok { - return fmt.Errorf("failed to get ECR token") - } else { - if cr.Ctx.Bool("get-token") { - fmt.Println(token) - return nil - } - - cr.SpecCMD = cr.docker("login", "--username", cr.Conf.AWSECRUserName, "--password", token, - cr.Conf.AWSECRHost) - if err := runner(cr).runCMD(); err != nil { - return err - } - - if !strings.Contains(cr.SpecCMD.StderrBuf.String(), "Using --password") { - return fmt.Errorf(strings.ReplaceAll(cr.SpecCMD.StderrBuf.String(), "\n", "")) - } - - zap.S().Info(strings.ReplaceAll(cr.SpecCMD.StdoutBuf.String(), "\n", "")) - } - - return nil -} - -func (cr *CRCommands) dockerLogout() error { - cr.SpecCMD = cr.docker("logout", cr.Conf.AWSECRHost) - if err := runner(cr).runCMD(); err != nil { - return err - } - - zap.S().Info(strings.ReplaceAll(cr.SpecCMD.StdoutBuf.String(), "\n", "")) - - return nil -} - -func containerRegistryAction(conf *config.Config, action func(dockerRunner DockerRunner) error) cli.ActionFunc { - return func(c *cli.Context) error { - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - - if err := util.ValidateNArg(c, 0); err != nil { - return err - } - - return action(newCRCommands(conf, c, util.GetPwdPath(""))) - } -} diff --git a/cmd/flags.go b/cmd/flags.go index a02b758..a1f7a4c 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -9,36 +9,6 @@ import ( func flagsConfig() []cli.Flag { return []cli.Flag{ - // TODO: will be transfer to cluster category for AWS provider - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "aws-ecr-host", - Usage: "AWS ECR host", - Aliases: []string{"aeh"}, - EnvVars: []string{"RMK_AWS_ECR_HOST"}, - Value: util.AWSECRHost, - }, - ), - // TODO: will be transfer to cluster category for AWS provider - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "aws-ecr-region", - Usage: "AWS region for specific ECR host", - Aliases: []string{"aer"}, - EnvVars: []string{"RMK_AWS_ECR_REGION"}, - Value: util.AWSECRRegion, - }, - ), - // TODO: will be transfer to cluster category for AWS provider - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "aws-ecr-user-name", - Usage: "AWS ECR user name", - Aliases: []string{"aeun"}, - EnvVars: []string{"RMK_AWS_ECR_USER_NAME"}, - Value: util.AWSECRUserName, - }, - ), altsrc.NewStringFlag( &cli.StringFlag{ Name: "aws-mfa-profile", @@ -56,15 +26,6 @@ func flagsConfig() []cli.Flag { Usage: "force AWS profile creation", Aliases: []string{"r"}, }, - // TODO: will be transfer to cluster category for AWS provider - altsrc.NewBoolFlag( - &cli.BoolFlag{ - Name: "cluster-provisioner-state-locking", - Usage: "disable or enable cluster provisioner state locking", - Aliases: []string{"c"}, - Value: true, - }, - ), &cli.StringFlag{ Name: "config", Hidden: true, @@ -89,15 +50,6 @@ func flagsConfig() []cli.Flag { EnvVars: []string{"RMK_GITHUB_TOKEN"}, }, ), - // TODO: will be transfer to cluster category - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "cloudflare-token", - Usage: "Cloudflare API token for provision NS records", - Aliases: []string{"cft"}, - EnvVars: []string{"RMK_CLOUDFLARE_TOKEN"}, - }, - ), altsrc.NewStringFlag( &cli.StringFlag{ Name: "cluster-provider", @@ -192,36 +144,26 @@ func flagsClusterK3DImport() []cli.Flag { ) } -func flagsClusterCRLogin() []cli.Flag { - return append(flagsHidden(), - &cli.BoolFlag{ - Name: "get-token", - Usage: "get ECR token for authentication", - Aliases: []string{"g"}, - }, - ) -} +//func flagsClusterPlan() []cli.Flag { +// return append(flagsHidden(), +// &cli.BoolFlag{ +// Name: "plan", +// Usage: "creates an execution Terraform plan", +// Aliases: []string{"p"}, +// }, +// ) +//} -func flagsClusterPlan() []cli.Flag { - return append(flagsHidden(), - &cli.BoolFlag{ - Name: "plan", - Usage: "creates an execution Terraform plan", - Aliases: []string{"p"}, - }, - ) -} - -func flagsClusterStateDelete() []cli.Flag { - return append(flagsHidden(), - &cli.StringFlag{ - Name: "resource-address", - Usage: "resource address for delete from Terraform state", - Aliases: []string{"ra"}, - EnvVars: []string{"RMK_CLUSTER_STATE_RESOURCE_ADDRESS"}, - }, - ) -} +//func flagsClusterStateDelete() []cli.Flag { +// return append(flagsHidden(), +// &cli.StringFlag{ +// Name: "resource-address", +// Usage: "resource address for delete from Terraform state", +// Aliases: []string{"ra"}, +// EnvVars: []string{"RMK_CLUSTER_STATE_RESOURCE_ADDRESS"}, +// }, +// ) +//} func flagsClusterSwitch() []cli.Flag { return append(flagsHidden(), @@ -428,12 +370,6 @@ func flagsHidden() []cli.Flag { Hidden: true, }, ), - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "cloudflare-token", - Hidden: true, - }, - ), altsrc.NewStringFlag( &cli.StringFlag{ Name: "cluster-provider", diff --git a/cmd/k3d.go b/cmd/k3d.go index 9dfc454..f7e9528 100644 --- a/cmd/k3d.go +++ b/cmd/k3d.go @@ -1,10 +1,7 @@ package cmd import ( - "fmt" "os" - "path/filepath" - "strings" "github.com/urfave/cli/v2" @@ -41,17 +38,12 @@ func (k *K3DCommands) k3d(args ...string) *util.SpecCMD { func (k *K3DCommands) prepareK3D(args ...string) error { k.SpecCMD = k.k3d(args...) k.SpecCMD.Debug = true - credentials, err := k.Conf.AwsConfigure.GetECRCredentials(k.Conf.AWSECRRegion) - if err != nil { - return err - } - k.SpecCMD.Envs = append(k.SpecCMD.Envs, "K3D_NAME="+k.Conf.Name) - - if token, ok := credentials[k.Conf.AWSECRUserName]; !ok { - return fmt.Errorf("failed to get ECR token") - } else { - k.SpecCMD.Envs = append(k.SpecCMD.Envs, "K3D_AWS_ECR_USER="+k.Conf.AWSECRUserName, "K3D_AWS_ECR_PASSWORD="+token) + switch { + case k.APICluster: + k.SpecCMD.Envs = append(k.SpecCMD.Envs, "K3D_NAME=capi") + case k.K3DCluster: + k.SpecCMD.Envs = append(k.SpecCMD.Envs, "K3D_NAME="+k.Conf.Name) } if len(k.Ctx.String("k3d-volume-host-path")) > 0 { @@ -65,50 +57,37 @@ func (k *K3DCommands) prepareK3D(args ...string) error { } func (k *K3DCommands) createDeleteK3DCluster() error { - var k3dDst string + switch k.Ctx.Command.Category { + case "capi": + k.APICluster = true + case "k3d": + k.K3DCluster = true + } - k.K3DCluster = true if _, _, err := k.getKubeContext(); err != nil { return err } - for name, pkg := range k.Conf.Clusters { - if strings.HasPrefix(name, util.K3DConfigPrefix) { - k3dDst = pkg.DstPath - break - } - } - - if len(k3dDst) == 0 { - return fmt.Errorf("cluster provider with name %s not found", util.K3DConfigPrefix) - } - - match, err := util.WalkMatch(k3dDst, util.K3DConfigPrefix+".yaml") - if err != nil { + k.SpecCMD = k.prepareHelmfile("-l", "app="+k.Ctx.Command.Category+"-cluster", "template") + k.SpecCMD.DisableStdOut = true + if err := runner(k).runCMD(); err != nil { return err } - if len(match) == 0 { - return fmt.Errorf("configuration file for %s not found", util.K3DConfigPrefix) - } - - if err := k.prepareK3D("cluster", k.Ctx.Command.Name, "--config", match[0]); err != nil { + k3dConfig, err := util.CreateTempYAMLFile("/tmp", k.Ctx.Command.Category+"-config", k.SpecCMD.StdoutBuf.Bytes()) + if err != nil { return err } - // Creating specific dir for k3d registry configuration - k3dRegistryHostPath := filepath.Join(filepath.Dir(match[0]), util.K3DConfigPrefix) - k.SpecCMD.Envs = append(k.SpecCMD.Envs, "K3D_REGISTRY_HOST_PATH="+k3dRegistryHostPath) - - if err := os.RemoveAll(k3dRegistryHostPath); err != nil { + if err := k.prepareK3D("cluster", k.Ctx.Command.Name, "--config", k3dConfig); err != nil { return err } - if err := os.MkdirAll(k3dRegistryHostPath, 0755); err != nil { + if err := runner(k).runCMD(); err != nil { return err } - return runner(k).runCMD() + return os.RemoveAll(k3dConfig) } func (k *K3DCommands) importImageToK3DCluster() error { @@ -156,7 +135,7 @@ func K3DCreateAction(conf *config.Config) cli.ActionFunc { return err } - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { return err } diff --git a/cmd/project.go b/cmd/project.go index 156f065..65f8829 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -419,7 +419,7 @@ func projectGenerateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cl return err } - return resolveDependencies(conf.InitConfig(false), c, false) + return resolveDependencies(conf.InitConfig(), c, false) } } diff --git a/cmd/release.go b/cmd/release.go index 261ccec..245ad88 100644 --- a/cmd/release.go +++ b/cmd/release.go @@ -35,6 +35,7 @@ type ReleaseCommands struct { WorkDir string ValuesPath string UpdateContext bool + APICluster bool K3DCluster bool } @@ -173,31 +174,22 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { "AWS_PROFILE="+rc.Conf.Profile, "AWS_CONFIG_FILE="+strings.Join(rc.Conf.AWSSharedConfigFile(rc.Conf.Profile), ""), "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(rc.Conf.AWSSharedCredentialsFile(rc.Conf.Profile), ""), - // Needed to set the AWS region to force the connection session region for the helm S3 plugin, - // if AWS_DEFAULT_REGION and AWS_REGION cannot be trusted. - //"HELM_S3_REGION="+rc.Conf.S3ChartsRepoRegion, ) - if _, ok := rc.Conf.Env["ROOT_DOMAIN"]; ok { - envs = append(envs, "ROOT_DOMAIN="+rc.Conf.Env["ROOT_DOMAIN"]) - delete(rc.Conf.Env, "ROOT_DOMAIN") - } else { - envs = append(envs, "ROOT_DOMAIN="+rc.Conf.RootDomain) - } + envs = append(envs, "ROOT_DOMAIN="+rc.Conf.RootDomain) for _, val := range rc.Conf.HooksMapping { keyTenantEnv := regexp.MustCompile(`[\-.]`).ReplaceAllString(val.Tenant, "_") envs = append(envs, "HELMFILE_"+strings.ToUpper(keyTenantEnv)+"_HOOKS_DIR="+val.DstPath) } - for key, val := range rc.Conf.Env { - envs = append(envs, key+"="+val) - } - // generating additional environment variables to nested helmfiles envs = rc.nestedHelmfiles(envs...) - if rc.K3DCluster { + switch { + case rc.APICluster: + envs = append(envs, "CAPI_CLUSTER="+strconv.FormatBool(rc.APICluster)) + case rc.K3DCluster: envs = append(envs, "K3D_CLUSTER="+strconv.FormatBool(rc.K3DCluster)) } @@ -324,13 +316,13 @@ func (rc *ReleaseCommands) releaseKubeContext() error { return fmt.Errorf("current context %s already used for K3D cluster, --force flag cannot be used", contextName) } - cc := &ClusterCommands{ - Conf: rc.Conf, - Ctx: rc.Ctx, - WorkDir: util.GetPwdPath(""), - } + //cc := &ClusterCommands{ + // Conf: rc.Conf, + // Ctx: rc.Ctx, + // WorkDir: util.GetPwdPath(""), + //} - if err := cc.clusterContext(); err != nil { + if err := newClusterCommands(rc.Conf, rc.Ctx, util.GetPwdPath("")).awsClusterContext(); err != nil { return err } @@ -342,8 +334,8 @@ func (rc *ReleaseCommands) releaseKubeContext() error { rc.SpecCMD = rc.kubeConfig() rc.SpecCMD.Args = append(rc.SpecCMD.Args, "set-credentials", currentContext, - "--exec-env", "AWS_CONFIG_FILE="+strings.Join(rc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""), - "--exec-env", "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(rc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""), + "--exec-env", "AWS_CONFIG_FILE="+strings.Join(rc.Conf.AWSSharedConfigFile(rc.Conf.Profile), ""), + "--exec-env", "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(rc.Conf.AWSSharedCredentialsFile(rc.Conf.Profile), ""), ) rc.SpecCMD.DisableStdOut = true rc.SpecCMD.Debug = true @@ -652,7 +644,7 @@ func releaseHelmfileAction(conf *config.Config) cli.ActionFunc { return err } - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { return err } @@ -705,7 +697,7 @@ func releaseRollbackAction(conf *config.Config) cli.ActionFunc { return err } - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { return err } @@ -742,7 +734,7 @@ func releaseUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli. return err } - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { return err } diff --git a/cmd/secret.go b/cmd/secret.go index e25bc97..93c9ec1 100644 --- a/cmd/secret.go +++ b/cmd/secret.go @@ -387,7 +387,7 @@ func secretMgrEncryptDecryptAction(conf *config.Config) cli.ActionFunc { return err } - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { return err } @@ -414,7 +414,7 @@ func secretKeysCreateAction(conf *config.Config) cli.ActionFunc { return err } - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { return err } @@ -452,7 +452,7 @@ func secretAction(conf *config.Config, action func(secretRunner SecretRunner) er return err } - if err := resolveDependencies(conf.InitConfig(false), c, false); err != nil { + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { return err } diff --git a/config/config.go b/config/config.go index 800c8cc..07cea85 100644 --- a/config/config.go +++ b/config/config.go @@ -6,10 +6,8 @@ import ( "fmt" "os" "path/filepath" - "reflect" "regexp" "runtime" - "strconv" "strings" "text/template" @@ -29,7 +27,6 @@ type Config struct { Environment string `yaml:"environment,omitempty"` ConfigNameFrom string `yaml:"config-name-from,omitempty"` RootDomain string `yaml:"root-domain,omitempty"` - CloudflareToken string `yaml:"cloudflare-token,omitempty"` GitHubToken string `yaml:"github-token,omitempty"` ClusterProvider string `yaml:"cluster-provider"` SlackNotifications bool `yaml:"slack-notifications"` @@ -38,15 +35,9 @@ type Config struct { SlackMsgDetails []string `yaml:"slack-message-details,omitempty"` SopsAgeKeys string `yaml:"sops-age-keys,omitempty"` SopsBucketName string `yaml:"sops-bucket-name,omitempty"` - AWSECRHost string `yaml:"aws-ecr-host,omitempty"` - AWSECRRegion string `yaml:"aws-ecr-region,omitempty"` - AWSECRUserName string `yaml:"aws-ecr-user-name,omitempty"` AWSMFAProfile string `yaml:"aws-mfa-profile,omitempty"` AWSMFATokenExpiration string `yaml:"aws-mfa-token-expiration,omitempty"` *aws_provider.AwsConfigure `yaml:"aws,omitempty"` - Terraform `yaml:"terraform,omitempty"` - ClusterProvisionerSL bool `yaml:"cluster-provisioner-state-locking"` - ExportedVars `yaml:"exported-vars,omitempty"` ProgressBar bool `yaml:"progress-bar"` ProjectFile `yaml:"project-file"` } @@ -106,25 +97,12 @@ type Terraform struct { DDBTableName string `yaml:"dynamodb-table-name,omitempty"` } -func (conf *Config) InitConfig(terraformOutput bool) *Config { +func (conf *Config) InitConfig() *Config { conf.ProjectFile = ProjectFile{} if err := conf.ReadProjectFile(util.GetPwdPath(util.TenantProjectFile)); err != nil { zap.S().Fatal(err) } - if !terraformOutput { - return conf - } - - conf.ExportedVars = ExportedVars{ - TerraformOutput: make(map[string]interface{}), - Env: make(map[string]string), - } - - if err := conf.GetTerraformOutputs(); err != nil { - zap.S().Fatal(err) - } - return conf } @@ -187,16 +165,9 @@ func (conf *Config) GetConfigs(all bool) error { } func (conf *Config) SetRootDomain(c *cli.Context, gitSpecID string) error { - hostedZoneVar := util.TerraformVarsPrefix + util.TerraformVarHostedZoneName if !c.IsSet("root-domain") { - if hostedZoneName, ok := conf.TerraformOutput[hostedZoneVar]; ok && len(hostedZoneName.(string)) > 0 { - if err := c.Set("root-domain", hostedZoneName.(string)); err != nil { - return err - } - } else { - if err := c.Set("root-domain", gitSpecID+util.TenantDomainSuffix); err != nil { - return err - } + if err := c.Set("root-domain", gitSpecID+util.TenantDomainSuffix); err != nil { + return err } } @@ -205,69 +176,6 @@ func (conf *Config) SetRootDomain(c *cli.Context, gitSpecID string) error { return nil } -func (conf *Config) GetTerraformOutputs() error { - type GetVar struct { - Type interface{} - Value interface{} - } - - var ( - raw map[string]*json.RawMessage - outputs map[string]*json.RawMessage - getVar *GetVar - ) - - checkWorkspace, err := conf.BucketKeyExists("", conf.Terraform.BucketName, "env:/"+conf.Name+"/tf.tfstate") - if err != nil { - return err - } - - if !checkWorkspace { - return nil - } - - data, err := conf.GetFileData(conf.Terraform.BucketName, "env:/"+conf.Name+"/tf.tfstate") - if err != nil { - return err - } - - if err := json.Unmarshal(data, &raw); err != nil { - return err - } - - if err := json.Unmarshal(*raw["outputs"], &outputs); err != nil { - return err - } - - if len(outputs) == 0 { - return nil - } - - for key := range outputs { - if strings.Contains(key, util.TerraformVarsPrefix) { - if err := json.Unmarshal(*outputs[key], &getVar); err != nil { - return err - } - - envKey := strings.ToUpper(strings.ReplaceAll(key, util.TerraformVarsPrefix, "")) - - switch { - case reflect.TypeOf(getVar.Value).Kind() == reflect.String && getVar.Type == reflect.String.String(): - conf.TerraformOutput[key] = getVar.Value - conf.Env[envKey] = getVar.Value.(string) - case reflect.TypeOf(getVar.Value).Kind() == reflect.Bool && getVar.Type == reflect.Bool.String(): - conf.TerraformOutput[key] = getVar.Value - conf.Env[envKey] = strconv.FormatBool(getVar.Value.(bool)) - default: - zap.S().Warnf("Terraform output variable %s will not be exported as environment variable, "+ - "does not match string or boolean types, current type: %s", key, getVar.Type) - } - } - } - - return nil -} - func (pf *ProjectFile) ReadProjectFile(path string) error { data, err := os.ReadFile(path) if err != nil { diff --git a/util/dictionary.go b/util/dictionary.go index 6b29a1b..baf086a 100644 --- a/util/dictionary.go +++ b/util/dictionary.go @@ -1,50 +1,42 @@ package util const ( - AWSClusterProvider = "aws" - AzureClusterProvider = "azure" - AWSECRHost = "288509344804.dkr.ecr.eu-north-1.amazonaws.com" - AWSECRRegion = "eu-north-1" - AWSECRUserName = "AWS" - GitSSHPrivateKey = ".ssh/id_rsa" - GlobalsFileName = "globals.yaml.gotmpl" - HelmfileFileName = "helmfile.yaml" - HelmfileGoTmplName = HelmfileFileName + ".gotmpl" - HelpFlagFull = "--help" - K3DConfigPrefix = "k3d" - LocalClusterProvider = K3DConfigPrefix - RMKBin = "rmk" - RMKBucketName = "edenlabllc-rmk" - RMKBucketRegion = "eu-north-1" - RMKConfig = "config" - RMKDir = ".rmk" - RMKSymLinkPath = "/usr/local/bin/rmk" - RMKToolsDir = "tools" - ReadmeFileName = "README.md" - RegionException = "us-east-1" - ReleasesFileName = "releases.yaml" - SecretSpecFile = ".spec.yaml.gotmpl" - SopsAgeKeyExt = ".txt" - SopsAgeKeyFile = ".keys.txt" - SopsRootName = "sops-age-keys" - SopsConfigFile = ".sops.yaml" - TenantBucketKey = "tf.tfstate" - TenantBucketName = "cloud-provisioner" - TenantDDBTablePrefix = "cloud-provisioner-state-locks" - TenantDomainSuffix = ".edenlab.dev" - TenantProjectCodeOwners = "docs/CODEOWNERS" - TenantProjectDIR = ".PROJECT" - TenantProjectFile = "project.yaml" - TenantProjectGitIgn = ".gitignore" - TenantValuesDIR = "etc" - TerraformVarHostedZoneName = "hosted_zone_name" - TerraformVarsExt = "tfvars" - TerraformVarsFile = "variables.auto." + TerraformVarsExt - TerraformVarsPrefix = "rmk_" - TerraformWGFile = "worker-groups.auto." + TerraformVarsExt - ToolsBinDir = "bin" - ToolsTmpDir = "tmp" - ToolsVersionDir = "version" + AWSClusterProvider = "aws" + AzureClusterProvider = "azure" + GitSSHPrivateKey = ".ssh/id_rsa" + GlobalsFileName = "globals.yaml.gotmpl" + HelmfileFileName = "helmfile.yaml" + HelmfileGoTmplName = HelmfileFileName + ".gotmpl" + HelpFlagFull = "--help" + K3DConfigPrefix = "k3d" + LocalClusterProvider = K3DConfigPrefix + RMKBin = "rmk" + RMKBucketName = "edenlabllc-rmk" + RMKBucketRegion = "eu-north-1" + RMKConfig = "config" + RMKDir = ".rmk" + RMKSymLinkPath = "/usr/local/bin/rmk" + RMKToolsDir = "tools" + ReadmeFileName = "README.md" + RegionException = "us-east-1" + ReleasesFileName = "releases.yaml" + SecretSpecFile = ".spec.yaml.gotmpl" + SopsAgeKeyExt = ".txt" + SopsAgeKeyFile = ".keys.txt" + SopsRootName = "sops-age-keys" + SopsConfigFile = ".sops.yaml" + TenantDomainSuffix = ".edenlab.dev" + TenantProjectCodeOwners = "docs/CODEOWNERS" + TenantProjectDIR = ".PROJECT" + TenantProjectFile = "project.yaml" + TenantProjectGitIgn = ".gitignore" + TenantValuesDIR = "etc" + TerraformVarsExt = "tfvars" + TerraformVarsFile = "variables.auto." + TerraformVarsExt + TerraformWGFile = "worker-groups.auto." + TerraformVarsExt + ToolsBinDir = "bin" + ToolsTmpDir = "tmp" + ToolsVersionDir = "version" ConfigNotInitializedErrorText = "RMK config not initialized, " + "please run command 'rmk config init' with specific parameters" diff --git a/util/system.go b/util/system.go index 67301e6..afa599c 100644 --- a/util/system.go +++ b/util/system.go @@ -397,3 +397,18 @@ func UnTar(dst, excludeRegexp string, r io.Reader) error { } } } + +func CreateTempYAMLFile(dirPath, fileName string, content []byte) (string, error) { + file, err := os.CreateTemp(dirPath, fileName+".*.yaml") + if err != nil { + return "", err + } + + if _, err := file.Write(content); err != nil { + return "", err + } + + defer file.Close() + + return file.Name(), nil +} From 5318f7913dbe3cfef89656109f6e5a943f1a27c4 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Thu, 26 Sep 2024 15:52:32 +0200 Subject: [PATCH 10/39] #29 - refactoring latest changes --- cmd/commands.go | 2 +- cmd/k3d.go | 12 +++++++----- cmd/release.go | 24 +++++++++++++----------- util/dictionary.go | 5 +++-- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/cmd/commands.go b/cmd/commands.go index db67807..9ff2362 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -273,7 +273,7 @@ func Commands() []*cli.Command { Usage: "Destroy releases", Aliases: []string{"d"}, Before: readInputSourceWithContext(gitSpec, conf, flags["releaseHelmfile"]), - Flags: flags["releaseHelmfileWithOutput"], + Flags: flags["releaseHelmfile"], Category: "release", BashComplete: util.ShellCompleteCustomOutput, Action: releaseHelmfileAction(conf), diff --git a/cmd/k3d.go b/cmd/k3d.go index f7e9528..83df182 100644 --- a/cmd/k3d.go +++ b/cmd/k3d.go @@ -1,6 +1,7 @@ package cmd import ( + "fmt" "os" "github.com/urfave/cli/v2" @@ -41,7 +42,7 @@ func (k *K3DCommands) prepareK3D(args ...string) error { switch { case k.APICluster: - k.SpecCMD.Envs = append(k.SpecCMD.Envs, "K3D_NAME=capi") + k.SpecCMD.Envs = append(k.SpecCMD.Envs, "K3D_NAME="+util.CAPI) case k.K3DCluster: k.SpecCMD.Envs = append(k.SpecCMD.Envs, "K3D_NAME="+k.Conf.Name) } @@ -58,9 +59,9 @@ func (k *K3DCommands) prepareK3D(args ...string) error { func (k *K3DCommands) createDeleteK3DCluster() error { switch k.Ctx.Command.Category { - case "capi": + case util.CAPI: k.APICluster = true - case "k3d": + case util.K3DPrefix: k.K3DCluster = true } @@ -68,10 +69,11 @@ func (k *K3DCommands) createDeleteK3DCluster() error { return err } - k.SpecCMD = k.prepareHelmfile("-l", "app="+k.Ctx.Command.Category+"-cluster", "template") + k.SpecCMD = k.prepareHelmfile("--log-level", "error", "-l", "cluster="+k.Ctx.Command.Category, "template") k.SpecCMD.DisableStdOut = true if err := runner(k).runCMD(); err != nil { - return err + return fmt.Errorf("Helmfile failed to render template by label release: cluster=%s\n%s", + k.Ctx.Command.Category, k.SpecCMD.StderrBuf.String()) } k3dConfig, err := util.CreateTempYAMLFile("/tmp", k.Ctx.Command.Category+"-config", k.SpecCMD.StdoutBuf.Bytes()) diff --git a/cmd/release.go b/cmd/release.go index 245ad88..bb9605d 100644 --- a/cmd/release.go +++ b/cmd/release.go @@ -166,6 +166,8 @@ func (rc *ReleaseCommands) nestedHelmfiles(envs ...string) []string { } func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { + defaultArgs := []string{"--environment", rc.Conf.Environment} + envs := append([]string{}, "NAME="+rc.Conf.Name, "TENANT="+rc.Conf.Tenant, @@ -193,9 +195,12 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { envs = append(envs, "K3D_CLUSTER="+strconv.FormatBool(rc.K3DCluster)) } + if len(rc.Ctx.String("helmfile-log-level")) > 0 { + defaultArgs = append(defaultArgs, "--log-level", rc.Ctx.String("helmfile-log-level")) + } + return &util.SpecCMD{ - Args: append([]string{"--environment", rc.Conf.Environment, "--log-level", - rc.Ctx.String("helmfile-log-level")}, args...), + Args: append(defaultArgs, args...), Command: "helmfile", Ctx: rc.Ctx.Context, Dir: rc.WorkDir, @@ -230,8 +235,11 @@ func (rc *ReleaseCommands) releaseMiddleware() error { if _, currentContext, err := rc.getKubeContext(); err != nil { return err } else { - if strings.Contains(currentContext, util.K3DConfigPrefix) { + switch { + case strings.Contains(currentContext, util.K3DPrefix) && !strings.Contains(currentContext, util.CAPI): rc.K3DCluster = true + case currentContext == util.K3DPrefix+"-"+util.CAPI: + rc.APICluster = true } } @@ -288,7 +296,7 @@ func (rc *ReleaseCommands) getKubeContext() (string, string, error) { contextName = "" } - if rc.K3DCluster && len(contextName) > 0 && !strings.Contains(contextName, util.K3DConfigPrefix) { + if rc.K3DCluster && len(contextName) > 0 && !strings.Contains(contextName, util.K3DPrefix) { return "", "", fmt.Errorf("remote Kubernetes context already exists %s for this branch", contextName) } @@ -312,16 +320,10 @@ func (rc *ReleaseCommands) releaseKubeContext() error { return nil } - if strings.Contains(contextName, util.K3DConfigPrefix) && rc.UpdateContext { + if strings.Contains(contextName, util.K3DPrefix) && rc.UpdateContext { return fmt.Errorf("current context %s already used for K3D cluster, --force flag cannot be used", contextName) } - //cc := &ClusterCommands{ - // Conf: rc.Conf, - // Ctx: rc.Ctx, - // WorkDir: util.GetPwdPath(""), - //} - if err := newClusterCommands(rc.Conf, rc.Ctx, util.GetPwdPath("")).awsClusterContext(); err != nil { return err } diff --git a/util/dictionary.go b/util/dictionary.go index baf086a..58cc221 100644 --- a/util/dictionary.go +++ b/util/dictionary.go @@ -3,13 +3,14 @@ package util const ( AWSClusterProvider = "aws" AzureClusterProvider = "azure" + CAPI = "capi" GitSSHPrivateKey = ".ssh/id_rsa" GlobalsFileName = "globals.yaml.gotmpl" HelmfileFileName = "helmfile.yaml" HelmfileGoTmplName = HelmfileFileName + ".gotmpl" HelpFlagFull = "--help" - K3DConfigPrefix = "k3d" - LocalClusterProvider = K3DConfigPrefix + K3DPrefix = "k3d" + LocalClusterProvider = K3DPrefix RMKBin = "rmk" RMKBucketName = "edenlabllc-rmk" RMKBucketRegion = "eu-north-1" From 12b176183baf12178757323c09d89b791a0628b1 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Mon, 30 Sep 2024 11:04:59 +0200 Subject: [PATCH 11/39] #29 - refactoring k3d list command --- cmd/commands.go | 10 ++++++++++ cmd/k3d.go | 20 +++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/cmd/commands.go b/cmd/commands.go index 9ff2362..850405a 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -134,6 +134,16 @@ func Commands() []*cli.Command { BashComplete: util.ShellCompleteCustomOutput, Action: K3DAction(conf, K3DRunner.createDeleteK3DCluster), }, + { + Name: "list", + Usage: "List CAPI clusters", + Aliases: []string{"l"}, + Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), + Flags: flags["hidden"], + Category: "capi", + BashComplete: util.ShellCompleteCustomOutput, + Action: K3DAction(conf, K3DRunner.listK3DClusters), + }, }, }, { diff --git a/cmd/k3d.go b/cmd/k3d.go index 83df182..02833c7 100644 --- a/cmd/k3d.go +++ b/cmd/k3d.go @@ -57,13 +57,17 @@ func (k *K3DCommands) prepareK3D(args ...string) error { return nil } -func (k *K3DCommands) createDeleteK3DCluster() error { +func (k *K3DCommands) selectCluster() { switch k.Ctx.Command.Category { case util.CAPI: k.APICluster = true case util.K3DPrefix: k.K3DCluster = true } +} + +func (k *K3DCommands) createDeleteK3DCluster() error { + k.selectCluster() if _, _, err := k.getKubeContext(); err != nil { return err @@ -102,11 +106,20 @@ func (k *K3DCommands) importImageToK3DCluster() error { } func (k *K3DCommands) listK3DClusters() error { - k.K3DCluster = true + k.selectCluster() + if _, _, err := k.getKubeContext(); err != nil { return err } + if k.APICluster { + if err := k.prepareK3D("cluster", k.Ctx.Command.Name, util.CAPI); err != nil { + return err + } + + return runner(k).runCMD() + } + if err := k.prepareK3D("cluster", k.Ctx.Command.Name); err != nil { return err } @@ -115,7 +128,8 @@ func (k *K3DCommands) listK3DClusters() error { } func (k *K3DCommands) startStopK3DCluster() error { - k.K3DCluster = true + k.selectCluster() + if _, _, err := k.getKubeContext(); err != nil { return err } From f1fd76b5fa289cdd9b2269d488c87bbbc77664b3 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Tue, 1 Oct 2024 18:00:32 +0200 Subject: [PATCH 12/39] #29 - change root-domain configuration --- cmd/flags.go | 45 ++++++------------ cmd/project.go | 95 +++++++++++++++++++++++++------------ cmd/project_dependencies.go | 50 ------------------- cmd/project_generation.go | 92 ++++++++++++++++------------------- cmd/secret.go | 2 +- config/config.go | 49 ++++++++----------- util/dictionary.go | 4 -- 7 files changed, 142 insertions(+), 195 deletions(-) diff --git a/cmd/flags.go b/cmd/flags.go index a1f7a4c..1a8439a 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -59,15 +59,6 @@ func flagsConfig() []cli.Flag { Value: util.AWSClusterProvider, }, ), - // TODO: will be transfer to cluster category for AWS provider - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "root-domain", - Usage: "domain name for external access to app services via ingress controller", - Aliases: []string{"rd"}, - EnvVars: []string{"RMK_ROOT_DOMAIN"}, - }, - ), altsrc.NewBoolFlag( &cli.BoolFlag{ Name: "progress-bar", @@ -144,27 +135,6 @@ func flagsClusterK3DImport() []cli.Flag { ) } -//func flagsClusterPlan() []cli.Flag { -// return append(flagsHidden(), -// &cli.BoolFlag{ -// Name: "plan", -// Usage: "creates an execution Terraform plan", -// Aliases: []string{"p"}, -// }, -// ) -//} - -//func flagsClusterStateDelete() []cli.Flag { -// return append(flagsHidden(), -// &cli.StringFlag{ -// Name: "resource-address", -// Usage: "resource address for delete from Terraform state", -// Aliases: []string{"ra"}, -// EnvVars: []string{"RMK_CLUSTER_STATE_RESOURCE_ADDRESS"}, -// }, -// ) -//} - func flagsClusterSwitch() []cli.Flag { return append(flagsHidden(), &cli.BoolFlag{ @@ -182,6 +152,21 @@ func flagsProjectGenerate() []cli.Flag { Usage: "create SOPS age keys for generated project structure", Aliases: []string{"c"}, }, + &cli.StringSliceFlag{ + Name: "environments", + Usage: "list of project environments. Root domain can take form of .root-domain=", + Aliases: []string{"e"}, + }, + &cli.StringSliceFlag{ + Name: "owners", + Usage: "list of project owners", + Aliases: []string{"o"}, + }, + &cli.StringSliceFlag{ + Name: "scopes", + Usage: "list of project scopes", + Aliases: []string{"s"}, + }, ) } diff --git a/cmd/project.go b/cmd/project.go index 65f8829..953feb9 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -67,9 +67,61 @@ func newProjectCommand(conf *config.Config, ctx *cli.Context, workDir string) *P } } +func (p *ProjectCommands) createProjectFile() error { + var buf bytes.Buffer + + if !p.Ctx.IsSet("environments") || !p.Ctx.IsSet("scopes") { + return fmt.Errorf("%s file not found or values not set for flags: %s, %s", + util.GetPwdPath(util.TenantProjectFile), "environments", "scopes") + } + + if p.Ctx.IsSet("environments") { + p.projectFile.Spec.Environments = make(map[string]*config.ProjectRootDomain) + for _, val := range p.Ctx.StringSlice("environments") { + if len(val) > 0 { + matchRootDomain := regexp.MustCompile(`^.+\.root-domain=.+$`).MatchString(val) + splitRootDomain := strings.SplitN(val, ".", 2) + + if !matchRootDomain && len(splitRootDomain) == 2 { + return fmt.Errorf("option %s for environment %s set not correctly", + splitRootDomain[1], splitRootDomain[0]) + } + + if matchRootDomain && len(splitRootDomain) == 2 { + p.projectFile.Spec.Environments[splitRootDomain[0]] = &config.ProjectRootDomain{ + RootDomain: strings.TrimPrefix(splitRootDomain[1], "root-domain="), + } + } + + if !matchRootDomain || len(splitRootDomain) == 1 { + p.projectFile.Spec.Environments[splitRootDomain[0]] = &config.ProjectRootDomain{} + } + } + } + } + + if p.Ctx.IsSet("owners") { + p.projectFile.Spec.Owners = p.Ctx.StringSlice("owners") + } + + if p.Ctx.IsSet("scopes") { + p.projectFile.Spec.Scopes = p.Ctx.StringSlice("scopes") + } + + encoder := yaml.NewEncoder(&buf) + encoder.SetIndent(2) + if err := encoder.Encode(&p.projectFile); err != nil { + return err + } + + return os.WriteFile(util.GetPwdPath(util.TenantProjectFile), buf.Bytes(), 0644) +} + func (p *ProjectCommands) readProjectFile() error { if !util.IsExists(util.GetPwdPath(util.TenantProjectFile), true) { - return fmt.Errorf("%s file not found", util.GetPwdPath(util.TenantProjectFile)) + if err := p.createProjectFile(); err != nil { + return err + } } data, err := os.ReadFile(util.GetPwdPath(util.TenantProjectFile)) @@ -196,9 +248,15 @@ func (p *ProjectCommands) generateReadme(gitSpec *git_handler.GitSpec) error { } func (p *ProjectCommands) generateHelmfile() error { - sort.Strings(p.projectFile.Spec.Environments) + var environmentKeys = make([]string, 0, len(p.projectFile.Spec.Environments)) + + for key := range p.projectFile.Spec.Environments { + environmentKeys = append(environmentKeys, key) + } + + sort.Strings(environmentKeys) - for key, name := range p.projectFile.Spec.Environments { + for key, name := range environmentKeys { p.EnvironmentName = name hEnvironments, err := p.Conf.ParseTemplate(template.New("Helmfile"), &p.parseContent, helmfileEnvironments) if err != nil { @@ -246,14 +304,6 @@ func (p *ProjectCommands) generateProjectFiles(gitSpec *git_handler.GitSpec) err for _, sc := range p.scopes { for _, env := range sc.environments { switch sc.name { - case "clusters": - if err := p.writeProjectFiles(filepath.Join(env.valuesPath, util.TerraformVarsFile), clusterVariables); err != nil { - return err - } - - if err := p.writeProjectFiles(filepath.Join(env.valuesPath, util.TerraformWGFile), clusterWorkerGroups); err != nil { - return err - } case p.TenantName: tGlobals, err := p.Conf.ParseTemplate(template.New("TenantGlobals"), &p.parseContent, tenantGlobals) if err != nil { @@ -299,10 +349,8 @@ func (p *ProjectCommands) generateProjectFiles(gitSpec *git_handler.GitSpec) err } } - if sc.name != "clusters" { - if err := p.writeProjectFiles(filepath.Join(env.secretsPath, util.SopsConfigFile), sopsConfigFile); err != nil { - return err - } + if err := p.writeProjectFiles(filepath.Join(env.secretsPath, util.SopsConfigFile), sopsConfigFile); err != nil { + return err } } } @@ -357,16 +405,7 @@ func (p *ProjectCommands) generateProject(gitSpec *git_handler.GitSpec) error { for sKey, sc := range p.projectFile.Spec.Scopes { p.Scopes = append(p.Scopes, sc) p.scopes = append(p.scopes, scope{name: sc, environments: make(map[string]*environment)}) - for _, env := range p.projectFile.Spec.Environments { - if sc == "clusters" { - p.scopes[sKey].environments[env] = &environment{ - secretsPath: util.GetPwdPath(util.TenantValuesDIR, sc, p.Conf.ClusterProvider, env, "secrets"), - valuesPath: util.GetPwdPath(util.TenantValuesDIR, sc, p.Conf.ClusterProvider, env, "values"), - } - - continue - } - + for env := range p.projectFile.Spec.Environments { p.scopes[sKey].environments[env] = &environment{ globalsPath: util.GetPwdPath(util.TenantValuesDIR, sc, env, util.GlobalsFileName), releasesPath: util.GetPwdPath(util.TenantValuesDIR, sc, env, util.ReleasesFileName), @@ -415,11 +454,7 @@ func projectGenerateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cl return err } - if err := newProjectCommand(conf, c, util.GetPwdPath()).generateProject(gitSpec); err != nil { - return err - } - - return resolveDependencies(conf.InitConfig(), c, false) + return newProjectCommand(conf, c, util.GetPwdPath()).generateProject(gitSpec) } } diff --git a/cmd/project_dependencies.go b/cmd/project_dependencies.go index 2ca696e..39b0b1c 100644 --- a/cmd/project_dependencies.go +++ b/cmd/project_dependencies.go @@ -24,7 +24,6 @@ const ( var ( TenantPrDependenciesDir = filepath.Join(util.TenantProjectDIR, "dependencies") TenantPrInventoryDir = filepath.Join(util.TenantProjectDIR, "inventory") - TenantPrInvClustersDir = filepath.Join(TenantPrInventoryDir, "clusters") TenantPrInvHooksDir = filepath.Join(TenantPrInventoryDir, "hooks") ) @@ -206,11 +205,6 @@ func uniqueHooksMapping(hooks []config.HookMapping) []config.HookMapping { } func (is *InventoryState) saveState(inv config.Inventory) { - is.clustersState = make(map[string]struct{}) - for key := range inv.Clusters { - is.clustersState[key] = struct{}{} - } - is.helmPluginsState = make(map[string]struct{}) for key := range inv.HelmPlugins { is.helmPluginsState[key] = struct{}{} @@ -222,26 +216,6 @@ func (is *InventoryState) saveState(inv config.Inventory) { } } -func (is *InventoryState) resolveClusters(invPkg map[string]*config.Package, conf *config.Config) (map[string]*config.Package, error) { - if len(conf.Clusters) == 0 { - conf.Clusters = make(map[string]*config.Package) - } - - for key, pkg := range invPkg { - vPkg, _ := semver.NewVersion(pkg.Version) - if _, ok := conf.Clusters[key]; !ok { - conf.Clusters[key] = pkg - } else if _, found := is.clustersState[key]; !found { - vP, _ := semver.NewVersion(conf.Clusters[key].Version) - if vPkg.GreaterThan(vP) { - conf.Clusters[key] = pkg - } - } - } - - return conf.Clusters, nil -} - func (is *InventoryState) resolveHelmPlugins(invPkg map[string]*config.Package, conf *config.Config) (map[string]*config.Package, error) { if len(conf.HelmPlugins) == 0 { conf.HelmPlugins = make(map[string]*config.Package) @@ -308,11 +282,6 @@ func resolveDependencies(conf *config.Config, ctx *cli.Context, silent bool) err return err } - // Resolve and recursively download repositories containing clusters - if conf.Clusters, invErr = invState.resolveClusters(projectFile.Clusters, conf); invErr != nil { - return invErr - } - // Resolve and recursively download repositories containing helm plugins if conf.HelmPlugins, invErr = invState.resolveHelmPlugins(projectFile.HelmPlugins, conf); invErr != nil { return invErr @@ -366,10 +335,6 @@ func resolveDependencies(conf *config.Config, ctx *cli.Context, silent bool) err return err } - if err := updateClusters(conf, ctx, silent); err != nil { - return err - } - // Finding unique versions of hooks in HooksMapping conf.HooksMapping = uniqueHooksMapping(conf.HooksMapping) @@ -464,21 +429,6 @@ func updateDependencies(conf *config.Config, ctx *cli.Context, silent bool) erro return nil } -func updateClusters(conf *config.Config, ctx *cli.Context, silent bool) error { - pwd := util.GetPwdPath(TenantPrInvClustersDir) - - for key, val := range conf.Clusters { - spec := &SpecDownload{Conf: conf, Ctx: ctx, PkgDst: pwd, rmOldDir: true} - if err := spec.batchUpdate(pwd, *val, silent); err != nil { - return err - } - - conf.Clusters[key].DstPath = spec.PkgDst - } - - return nil -} - func updateHooks(conf *config.Config, ctx *cli.Context, silent bool) error { pwd := util.GetPwdPath(TenantPrInvHooksDir) diff --git a/cmd/project_generation.go b/cmd/project_generation.go index 30f6113..6f1ae77 100644 --- a/cmd/project_generation.go +++ b/cmd/project_generation.go @@ -1,50 +1,6 @@ package cmd const ( - clusterVariables = `# Kubernetes user list -k8s_master_usernames = [] -k8s_cluster_version = "" # Actual EKS Kubernetes version -# Bastion -bastion_enabled = false -# IAM Roles -aws_lb_controller_role_enabled = true -external_dns_role_enabled = true -ebs_csi_controller_roles_enabled = true -` - - clusterWorkerGroups = `# For example: -# worker_groups = [ -# { -# name = "" -# instance_type = "t3.medium" -# additional_userdata = "t3.medium" -# asg_desired_capacity = 3 -# asg_max_size = 3 -# asg_min_size = 3 -# ami_id = "ami-" -# kubelet_extra_args = "--node-labels=key1=value1" -# root_volume_size = "10" -# root_volume_type = "gp3" -# enable_monitoring = false -# }, -# { -# name = "" -# instance_type = "t3.medium" -# additional_userdata = "t3.medium" -# asg_desired_capacity = 3 -# asg_max_size = 3 -# asg_min_size = 3 -# ami_id = "ami-" -# kubelet_extra_args = "--node-labels=key2=value2 --register-with-taints=key2=value2:NoSchedule" -# root_volume_size = "10" -# root_volume_type = "gp3" -# enable_monitoring = false -# }, -# ] - -worker_groups = [] -` - codeOwners = `# These owners will be the default owners for everything in # the repo and will be requested for review when someone opens a pull request. ` @@ -146,9 +102,33 @@ helmfiles: ` + escapeOpen + `{{ env "HELMFILE_` + escapeClose + `{{ .TenantNameE ` helmfileReleases = `releases: - # TODO: It is recommended to adapt this example considering security, performance and configuration management - # TODO: requirements specific to your application or infrastructure. + # TODO: Releases from group 1 are needed to deploy Cluster API or K3D clusters. + # TODO: If you do not inherit upstream repositories, you can leave these releases as is, + # TODO: or make sure that upstream repositories do not have the same releases to avoid conflicts. # Group 1 + - name: capi-cluster + namespace: kube-system + chart: "{{"{{` + escape + `{{ .Release.Labels.repo }}` + escape + `}}"}}/k3d-cluster" + version: 0.1.0 + labels: + cluster: capi + installed: ` + escapeOpen + `{{ eq (env "CAPI_CLUSTER" | default "false") "true" }}` + escapeClose + ` + inherit: + - template: release + + - name: k3d-cluster + namespace: kube-system + chart: "{{"{{` + escape + `{{ .Release.Labels.repo }}` + escape + `}}"}}/k3d-cluster" + version: 0.1.0 + labels: + cluster: k3d + installed: ` + escapeOpen + `{{ eq (env "K3D_CLUSTER" | default "false") "true" }}` + escapeClose + ` + inherit: + - template: release + + # TODO: It is recommended to adapt this example considering security, performance and configuration management + # TODO: requirements specific to your application or infrastructure. + # Group 2 - name: {{ .TenantName }}-app namespace: {{ .TenantName }} chart: "{{"{{` + escape + `{{ .Release.Labels.repo }}` + escape + `}}"}}/app" @@ -187,7 +167,7 @@ Detailed information about requirements and installation instructions can be fou * hooks - Note: K3D v5.x.x requires at least Docker v20.10.5 (runc >= v1.0.0-rc93) to work properly - Python >= 3.9 -- [RMK CLI](https://github.com/edenlabllc/rmk?tab=readme-ov-file#rmk-cli---reduced-management-for-kubernetes) >= v.0.42.4 +- [RMK CLI](https://edenlabllc.github.io/rmk/latest) ### GitLab flow strategy @@ -230,17 +210,27 @@ This example shows how the following options are configured and interact with ea {{- end }} ### Basic RMK commands for project management +#### Project generate + +` + "```" + `shell +rmk project generate \ + --environment=develop.root-domain=*.example.com \ + --environment=staging.root-domain=test.example.com \ + --environment=production \ + --owners=user \ + --scopes=rmt-test +` + "```" + ` + #### Initialization configuration ` + "```" + `shell rmk config init ` + "```" + ` -#### Cluster provision +#### Create Cluster API cluster ` + "```" + `shell -rmk cluster provision --plan -rmk cluster provision +rmk cluster capi create ` + "```" + ` #### Release sync @@ -249,7 +239,7 @@ rmk cluster provision rmk release sync ` + "```" + ` -> Note: A complete list of RMK commands and capabilities can be found at the [link](https://github.com/edenlabllc/rmk?tab=readme-ov-file#rmk-cli---reduced-management-for-kubernetes) +> Note: A complete list of RMK commands and capabilities can be found at the [link](https://edenlabllc.github.io/rmk/latest) ` releasesFile = `# This file defines the release list, is located in the environment directory diff --git a/cmd/secret.go b/cmd/secret.go index 93c9ec1..eeb6db0 100644 --- a/cmd/secret.go +++ b/cmd/secret.go @@ -174,7 +174,7 @@ func (sc *SecretCommands) getOptionFiles(option string) ([]string, error) { } case !sc.Ctx.IsSet("scope") && sc.Ctx.IsSet("environment"): for _, environment := range sc.Ctx.StringSlice("environment") { - for _, env := range sc.Conf.Project.Spec.Environments { + for env := range sc.Conf.Project.Spec.Environments { if environment == env { check++ } diff --git a/config/config.go b/config/config.go index 07cea85..96824f2 100644 --- a/config/config.go +++ b/config/config.go @@ -42,11 +42,6 @@ type Config struct { ProjectFile `yaml:"project-file"` } -type ExportedVars struct { - TerraformOutput map[string]interface{} `yaml:"terraform-output,omitempty"` - Env map[string]string `yaml:"env,omitempty"` -} - type HookMapping struct { Tenant string `yaml:"tenant,omitempty"` Exists bool `yaml:"-"` @@ -70,7 +65,6 @@ type Package struct { } type Inventory struct { - Clusters map[string]*Package `yaml:"clusters,omitempty"` HelmPlugins map[string]*Package `yaml:"helm-plugins,omitempty"` Hooks map[string]*Package `yaml:"hooks,omitempty"` Tools map[string]*Package `yaml:"tools,omitempty"` @@ -80,9 +74,9 @@ type Project struct { Dependencies []Package `yaml:"dependencies,omitempty"` HooksMapping []HookMapping `yaml:"hooks-mapping,omitempty"` Spec struct { - Environments []string `yaml:"environments,omitempty"` - Owners []string `yaml:"owners,omitempty"` - Scopes []string `yaml:"scopes,omitempty"` + Environments map[string]*ProjectRootDomain `yaml:"environments,omitempty"` + Owners []string `yaml:"owners,omitempty"` + Scopes []string `yaml:"scopes,omitempty"` } `yaml:"spec,omitempty"` } @@ -91,10 +85,8 @@ type ProjectFile struct { Inventory `yaml:"inventory,omitempty"` } -type Terraform struct { - BucketName string `yaml:"bucket-name,omitempty"` - BucketKey string `yaml:"bucket-key,omitempty"` - DDBTableName string `yaml:"dynamodb-table-name,omitempty"` +type ProjectRootDomain struct { + RootDomain string `yaml:"root-domain,omitempty"` } func (conf *Config) InitConfig() *Config { @@ -165,13 +157,24 @@ func (conf *Config) GetConfigs(all bool) error { } func (conf *Config) SetRootDomain(c *cli.Context, gitSpecID string) error { - if !c.IsSet("root-domain") { - if err := c.Set("root-domain", gitSpecID+util.TenantDomainSuffix); err != nil { - return err + for env, val := range conf.Spec.Environments { + check := strings.Split(val.RootDomain, "*.") + if len(check) > 2 { + return fmt.Errorf("root-domain not set correctly for environment %s", env) } } - conf.RootDomain = c.String("root-domain") + for env, val := range conf.Spec.Environments { + if env == conf.Environment { + if regexp.MustCompile(`^\*\.`).MatchString(val.RootDomain) { + conf.RootDomain = strings.ReplaceAll(val.RootDomain, "*", gitSpecID) + } else if len(val.RootDomain) > 0 { + conf.RootDomain = val.RootDomain + } else { + conf.RootDomain = "localhost" + } + } + } return nil } @@ -215,18 +218,6 @@ func (pf *ProjectFile) parseProjectFileData() error { } } - for key, provider := range pf.Clusters { - if _, err := semver.NewVersion(provider.Version); err != nil { - return fmt.Errorf("%s %s for section inventory.clusters", strings.ToLower(err.Error()), provider.Version) - } - - pf.Clusters[key].Name = key - pf.Clusters[key].Url, err = pf.ParseTemplate(template.New("Clusters"), pf.Clusters[key], provider.Url) - if err != nil { - return err - } - } - for key, plugin := range pf.HelmPlugins { if _, err := semver.NewVersion(plugin.Version); err != nil { return fmt.Errorf("%s %s for section inventory.helm-plugins", strings.ToLower(err.Error()), plugin.Version) diff --git a/util/dictionary.go b/util/dictionary.go index 58cc221..760acd9 100644 --- a/util/dictionary.go +++ b/util/dictionary.go @@ -26,15 +26,11 @@ const ( SopsAgeKeyFile = ".keys.txt" SopsRootName = "sops-age-keys" SopsConfigFile = ".sops.yaml" - TenantDomainSuffix = ".edenlab.dev" TenantProjectCodeOwners = "docs/CODEOWNERS" TenantProjectDIR = ".PROJECT" TenantProjectFile = "project.yaml" TenantProjectGitIgn = ".gitignore" TenantValuesDIR = "etc" - TerraformVarsExt = "tfvars" - TerraformVarsFile = "variables.auto." + TerraformVarsExt - TerraformWGFile = "worker-groups.auto." + TerraformVarsExt ToolsBinDir = "bin" ToolsTmpDir = "tmp" ToolsVersionDir = "version" From 5ae37e441f6edf4043779800390c9fc148d9fcf8 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Tue, 1 Oct 2024 18:06:39 +0200 Subject: [PATCH 13/39] #29 - refactoring --- cmd/project_generation.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/project_generation.go b/cmd/project_generation.go index 6f1ae77..7938cdc 100644 --- a/cmd/project_generation.go +++ b/cmd/project_generation.go @@ -227,10 +227,10 @@ rmk project generate \ rmk config init ` + "```" + ` -#### Create Cluster API cluster +#### Create K3D cluster ` + "```" + `shell -rmk cluster capi create +rmk cluster k3d create ` + "```" + ` #### Release sync From 116bd15d0d85fa1880dc6a026db6145daa3994cb Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 2 Oct 2024 09:55:18 +0200 Subject: [PATCH 14/39] #29 - refactoring --- cmd/flags.go | 6 +++--- cmd/project.go | 2 +- cmd/project_generation.go | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd/flags.go b/cmd/flags.go index 1a8439a..d16c531 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -154,17 +154,17 @@ func flagsProjectGenerate() []cli.Flag { }, &cli.StringSliceFlag{ Name: "environments", - Usage: "list of project environments. Root domain can take form of .root-domain=", + Usage: "list project environments. Root domain can take form of .root-domain=", Aliases: []string{"e"}, }, &cli.StringSliceFlag{ Name: "owners", - Usage: "list of project owners", + Usage: "list project owners", Aliases: []string{"o"}, }, &cli.StringSliceFlag{ Name: "scopes", - Usage: "list of project scopes", + Usage: "list project scopes", Aliases: []string{"s"}, }, ) diff --git a/cmd/project.go b/cmd/project.go index 953feb9..df334fc 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -83,7 +83,7 @@ func (p *ProjectCommands) createProjectFile() error { splitRootDomain := strings.SplitN(val, ".", 2) if !matchRootDomain && len(splitRootDomain) == 2 { - return fmt.Errorf("option %s for environment %s set not correctly", + return fmt.Errorf("option %s for environment %s not set correctly", splitRootDomain[1], splitRootDomain[0]) } diff --git a/cmd/project_generation.go b/cmd/project_generation.go index 7938cdc..5fa94c2 100644 --- a/cmd/project_generation.go +++ b/cmd/project_generation.go @@ -214,9 +214,7 @@ This example shows how the following options are configured and interact with ea ` + "```" + `shell rmk project generate \ - --environment=develop.root-domain=*.example.com \ - --environment=staging.root-domain=test.example.com \ - --environment=production \ + --environment=develop \ --owners=user \ --scopes=rmt-test ` + "```" + ` From e7a2085cd18017038fc3f34090e2afa5eb248a18 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 23 Oct 2024 13:29:04 +0200 Subject: [PATCH 15/39] #29 - implement support Azure provider for Cluster API --- cmd/cluster.go | 405 ++++++++++++++++++++++++++++-- cmd/cluster_capa.go | 38 +++ cmd/cluster_capz.go | 155 ++++++++++++ cmd/commands.go | 36 ++- cmd/config.go | 91 +++++-- cmd/flags.go | 41 ++- cmd/k3d.go | 36 ++- cmd/project.go | 2 + cmd/release.go | 150 +---------- cmd/secret.go | 13 +- config/config.go | 38 +-- go.mod | 117 ++++++--- go.sum | 252 +++++++++++++------ providers/aws_provider/aws.go | 4 + providers/azure_provider/azure.go | 152 +++++++++++ util/dictionary.go | 3 +- 16 files changed, 1203 insertions(+), 330 deletions(-) create mode 100644 cmd/cluster_capa.go create mode 100644 cmd/cluster_capz.go diff --git a/cmd/cluster.go b/cmd/cluster.go index 5a53a63..2c0e282 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -1,55 +1,412 @@ package cmd import ( + "fmt" + "os" + "regexp" "strings" + yaml2 "github.com/ghodss/yaml" "github.com/urfave/cli/v2" + "gopkg.in/yaml.v3" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/clientcmd" + clientcmdlatest "k8s.io/client-go/tools/clientcmd/api/latest" "rmk/config" + "rmk/providers/aws_provider" + "rmk/providers/azure_provider" "rmk/util" ) +type clusterRunner interface { + getKubeContext() (string, string, error) + switchKubeContext() error +} + type ClusterCommands struct { *ReleaseCommands } +type ClusterCTLConfig struct { + ApiVersion string + Kind string + Metadata map[string]string + Spec interface{} +} + func newClusterCommands(conf *config.Config, ctx *cli.Context, workDir string) *ClusterCommands { return &ClusterCommands{&ReleaseCommands{Conf: conf, Ctx: ctx, WorkDir: workDir}} } -func (cc *ClusterCommands) awsEks() *util.SpecCMD { +func (cc *ClusterCommands) clusterCTL(args ...string) *util.SpecCMD { + var envs []string + + switch { + case cc.Conf.ClusterProvider == azure_provider.AzureClusterProvider: + envs = []string{ + "EXP_AKS=true", + "EXP_MACHINE_POOL=true", + "EXP_CLUSTER_RESOURCE_SET=false", + } + } + return &util.SpecCMD{ - Envs: []string{ - "AWS_PROFILE=" + cc.Conf.Profile, - "AWS_CONFIG_FILE=" + strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""), - "AWS_SHARED_CREDENTIALS_FILE=" + strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""), - }, - Args: []string{"eks", "--region", - cc.Conf.Region, - "update-kubeconfig", - "--name", - cc.Conf.Name + "-eks", - "--profile", - cc.Conf.Profile, - }, - Command: "aws", + Args: args, + Command: "clusterctl", Ctx: cc.Ctx.Context, Dir: cc.WorkDir, Debug: true, + Envs: envs, + } +} + +func (cc *ClusterCommands) kubectl(args ...string) *util.SpecCMD { + return &util.SpecCMD{ + Args: args, + Command: "kubectl", + Ctx: cc.Ctx.Context, + Dir: cc.WorkDir, + DisableStdOut: false, + Debug: true, + } +} + +func createClusterCTLConfigFile(output []byte) (string, error) { + clusterCTL := &ClusterCTLConfig{} + if err := yaml.Unmarshal(output, &clusterCTL); err != nil { + return "", err + } + + data, err := yaml.Marshal(clusterCTL.Spec) + if err != nil { + return "", err + } + + return util.CreateTempYAMLFile("/tmp", clusterCTL.Metadata["name"], data) +} + +func (cc *ClusterCommands) getClusterCTLConfig() (string, string, error) { + if cc.Ctx.Command.Category == util.CAPI { + cc.APICluster = true + } + + _, currentContextName, err := cc.getKubeContext() + if err != nil { + return "", "", err + } + + cc.SpecCMD = cc.prepareHelmfile("--log-level", "error", "-l", "config=clusterctl", "template") + cc.SpecCMD.DisableStdOut = true + if err := releaseRunner(cc).runCMD(); err != nil { + return "", "", fmt.Errorf("Helmfile failed to render template by label release: config=clusterctl\n%s", + cc.SpecCMD.StderrBuf.String()) + } + + fileName, err := createClusterCTLConfigFile(cc.SpecCMD.StdoutBuf.Bytes()) + if err != nil { + return "", "", err + } + + return currentContextName, fileName, nil +} + +func (cc *ClusterCommands) initClusterCTLConfig() error { + contextName, clusterCTLConfig, err := cc.getClusterCTLConfig() + if err != nil { + return err + } + + cc.SpecCMD = cc.clusterCTL("init", "--infrastructure", cc.Conf.ClusterProvider, + "--wait-providers", "--kubeconfig-context", contextName, "--config", clusterCTLConfig) + + if err := releaseRunner(cc).runCMD(); err != nil { + if err := os.RemoveAll(clusterCTLConfig); err != nil { + return err + } + + return err + } + + return os.RemoveAll(clusterCTLConfig) +} + +func (cc *ClusterCommands) mergeKubeConfig(clusterContext []byte) error { + var object interface{} + + if err := yaml.Unmarshal(clusterContext, &object); err != nil { + return err + } + + file, err := createManifestFile(object, "/tmp", cc.Conf.Name+"-"+cc.Conf.ClusterProvider+"-kubeconfig") + if err != nil { + return err + } + + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + loadingRules.Precedence = append(loadingRules.Precedence, file) + + mergeConfig, err := loadingRules.Load() + if err != nil { + if err := os.RemoveAll(file); err != nil { + return err + } + + return err + } + + if err := os.RemoveAll(file); err != nil { + return err + } + + json, err := runtime.Encode(clientcmdlatest.Codec, mergeConfig) + if err != nil { + return err + } + + kubeConfig, err := yaml2.JSONToYAML(json) + if err != nil { + return err + } + + if err := os.WriteFile(clientcmd.NewDefaultPathOptions().GlobalFile, kubeConfig, 0644); err != nil { + return err + } + + cc.SpecCMD = cc.kubectl("config", "use-context", cc.Conf.Name) + + return releaseRunner(cc).runCMD() +} + +func (cc *ClusterCommands) getKubeContext() (string, string, error) { + var ( + contextNames []string + contextName string + ) + + kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() + if err != nil { + return "", "", err + } + + re, err := regexp.Compile(`(?i)\b` + cc.Conf.Name + `\b`) + if err != nil { + return "", "", err + } + + for key := range kubeConfig.Contexts { + if re.MatchString(key) { + contextNames = append(contextNames, key) + } + } + + switch { + case len(contextNames) > 1: + return "", "", + fmt.Errorf("detected more than one Kubernetes context with names %s leading to conflict, "+ + "please delete or rename all contexts except one", strings.Join(contextNames, ", ")) + case len(contextNames) > 0: + contextName = contextNames[0] + default: + contextName = "" + } + + if cc.K3DCluster && len(contextName) > 0 && !strings.Contains(contextName, util.K3DPrefix) { + return "", "", fmt.Errorf("remote Kubernetes context already exists %s for this branch", contextName) + } + + return contextName, kubeConfig.CurrentContext, nil +} + +func (cc *ClusterCommands) switchKubeContext() error { + contextName, currentContextName, err := cc.getKubeContext() + if err != nil { + return err + } + + if cc.Ctx.Command.Category == util.CAPI { + kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() + if err != nil { + return err + } + + if _, ok := kubeConfig.Contexts[util.CAPIContextName]; ok && currentContextName != util.CAPIContextName { + cc.SpecCMD = cc.kubectl("config", "use-context", util.CAPIContextName) + return releaseRunner(cc).runCMD() + } else if ok && currentContextName == util.CAPIContextName { + return nil + } } + + if len(contextName) > 0 && !cc.UpdateContext { + if contextName != currentContextName { + cc.SpecCMD = cc.kubectl("config", "use-context", contextName) + return releaseRunner(cc).runCMD() + } + + return nil + } + + if strings.Contains(contextName, util.K3DPrefix) && cc.UpdateContext { + return fmt.Errorf("current context %s already used for K3D cluster, --force flag cannot be used", contextName) + } + + switch cc.Conf.ClusterProvider { + case azure_provider.AzureClusterProvider: + clusterContext, err := cc.azureClusterContext() + if err != nil { + return err + } + + if err := cc.mergeKubeConfig(clusterContext); err != nil { + return err + } + case aws_provider.AWSClusterProvider: + if err := cc.awsClusterContext(); err != nil { + return err + } + + _, currentContext, err := cc.getKubeContext() + if err != nil { + return err + } + + cc.SpecCMD = cc.kubectl("config", "set-credentials", currentContext, + "--exec-env", "AWS_CONFIG_FILE="+strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""), + "--exec-env", "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""), + ) + cc.SpecCMD.DisableStdOut = true + cc.SpecCMD.Debug = true + return releaseRunner(cc).runCMD() + } + + return nil } -func (cc *ClusterCommands) awsClusterContext() error { - cc.SpecCMD = cc.awsEks() - return runner(cc).runCMD() +func (cc *ClusterCommands) provisionDestroyTargetCluster() error { + if cc.Ctx.Command.Category == util.CAPI { + cc.APICluster = true + } + + _, _, err := cc.getKubeContext() + if err != nil { + return err + } + + switch cc.Ctx.Command.Name { + case "provision": + cc.SpecCMD = cc.prepareHelmfile("--log-level", "error", "-l", "cluster="+cc.Conf.ClusterProvider, "sync") + if err := releaseRunner(cc).runCMD(); err != nil { + return err + } + + switch cc.Conf.ClusterProvider { + case azure_provider.AzureClusterProvider: + clusterContext, err := cc.azureClusterContext() + if err != nil { + return err + } + + if err := cc.mergeKubeConfig(clusterContext); err != nil { + return err + } + } + case "destroy": + cc.SpecCMD = cc.prepareHelmfile("--log-level", "error", "-l", "cluster="+cc.Conf.ClusterProvider, "destroy") + if err := releaseRunner(cc).runCMD(); err != nil { + return err + } + + kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() + if err != nil { + return err + } + + if context, ok := kubeConfig.Contexts[cc.Conf.Name]; ok { + cc.SpecCMD = cc.kubectl("config", "delete-context", cc.Conf.Name) + if err := releaseRunner(cc).runCMD(); err != nil { + return err + } + + cc.SpecCMD = cc.kubectl("config", "delete-cluster", context.Cluster) + if err := releaseRunner(cc).runCMD(); err != nil { + return err + } + + cc.SpecCMD = cc.kubectl("config", "delete-user", context.AuthInfo) + if err := releaseRunner(cc).runCMD(); err != nil { + return err + } + } + } + + return nil } func clusterSwitchAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - if err := util.ValidateGitHubToken(c, ""); err != nil { + if err := util.ValidateNArg(c, 0); err != nil { return err } + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { + return err + } + + cc := newClusterCommands(conf, c, util.GetPwdPath("")) + cc.UpdateContext = c.Bool("force") + + return cc.switchKubeContext() + } +} + +func CAPIInitAction(conf *config.Config) cli.AfterFunc { + return func(c *cli.Context) error { + if err := util.ValidateNArg(c, 0); err != nil { + return err + } + + cc := newClusterCommands(conf, c, util.GetPwdPath()) + if err := cc.switchKubeContext(); err != nil { + return err + } + + if err := cc.initClusterCTLConfig(); err != nil { + return err + } + + return cc.applyAzureClusterIdentity() + } +} + +func CAPIUpdateAction(conf *config.Config) cli.ActionFunc { + return func(c *cli.Context) error { + if err := util.ValidateNArg(c, 0); err != nil { + return err + } + + if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { + return err + } + + cc := newClusterCommands(conf, c, util.GetPwdPath()) + if err := cc.switchKubeContext(); err != nil { + return err + } + + if err := cc.initClusterCTLConfig(); err != nil { + return err + } + + return cc.applyAzureClusterIdentity() + } +} + +func CAPIProvisionDestroyAction(conf *config.Config) cli.ActionFunc { + return func(c *cli.Context) error { if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -58,13 +415,11 @@ func clusterSwitchAction(conf *config.Config) cli.ActionFunc { return err } - rc := &ReleaseCommands{ - Conf: conf, - Ctx: c, - WorkDir: util.GetPwdPath(""), - UpdateContext: c.Bool("force"), + cc := newClusterCommands(conf, c, util.GetPwdPath()) + if err := cc.switchKubeContext(); err != nil { + return err } - return rc.releaseKubeContext() + return cc.provisionDestroyTargetCluster() } } diff --git a/cmd/cluster_capa.go b/cmd/cluster_capa.go new file mode 100644 index 0000000..6cfdc1b --- /dev/null +++ b/cmd/cluster_capa.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "strings" + + "rmk/util" +) + +const ( + awsFlagsCategory = "AWS authentication" +) + +func (cc *ClusterCommands) getAWSEksKubeConfig() *util.SpecCMD { + return &util.SpecCMD{ + Envs: []string{ + "AWS_PROFILE=" + cc.Conf.Profile, + "AWS_CONFIG_FILE=" + strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""), + "AWS_SHARED_CREDENTIALS_FILE=" + strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""), + }, + Args: []string{"eks", "--region", + cc.Conf.Region, + "update-kubeconfig", + "--name", + cc.Conf.Name + "-eks", + "--profile", + cc.Conf.Profile, + }, + Command: "aws", + Ctx: cc.Ctx.Context, + Dir: cc.WorkDir, + Debug: true, + } +} + +func (cc *ClusterCommands) awsClusterContext() error { + cc.SpecCMD = cc.getAWSEksKubeConfig() + return releaseRunner(cc).runCMD() +} diff --git a/cmd/cluster_capz.go b/cmd/cluster_capz.go new file mode 100644 index 0000000..5bc1aa4 --- /dev/null +++ b/cmd/cluster_capz.go @@ -0,0 +1,155 @@ +package cmd + +import ( + "os" + "path/filepath" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/json" + v1 "k8s.io/client-go/applyconfigurations/core/v1" + + "rmk/providers/azure_provider" + "rmk/util" +) + +const ( + azureClusterIdentityName = "azure-cluster-identity" + azureClusterIdentityNamespace = "capz-system" + azureClusterIdentitySecret = "azure-cluster-identity-secret" + azureFlagsCategory = "Azure authentication" +) + +var azureClusterIdentitySecretType = corev1.SecretTypeOpaque + +type AzureClusterIdentityConfig struct { + *AzureClusterIdentity + *v1.SecretApplyConfiguration + ManifestFilesDir string + ManifestFiles []string +} + +type AzureClusterIdentity struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AzureClusterIdentitySpec `json:"spec,omitempty"` +} + +type AzureClusterIdentitySpec struct { + AllowedNamespaces struct { + NamespaceList []string `json:"list"` + Selector *metav1.LabelSelector `json:"selector,omitempty"` + } `json:"allowedNamespaces,omitempty"` + ClientID string `json:"clientID"` + ClientSecret corev1.SecretReference `json:"clientSecret,omitempty"` + TenantID string `json:"tenantID"` + Type string `json:"type"` +} + +func NewAzureClusterIdentityConfig(ac *azure_provider.AzureConfigure) *AzureClusterIdentityConfig { + acic := &AzureClusterIdentityConfig{ + AzureClusterIdentity: &AzureClusterIdentity{ + TypeMeta: metav1.TypeMeta{ + Kind: "AzureClusterIdentity", + APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: azureClusterIdentityName, + Namespace: azureClusterIdentityNamespace, + Labels: map[string]string{"clusterctl.cluster.x-k8s.io/move-hierarchy": "true"}, + }, + Spec: AzureClusterIdentitySpec{ + AllowedNamespaces: struct { + NamespaceList []string `json:"list"` + Selector *metav1.LabelSelector `json:"selector,omitempty"` + }(struct { + NamespaceList []string + Selector *metav1.LabelSelector + }{ + NamespaceList: []string{azureClusterIdentityNamespace}, + }), + ClientID: ac.ClientID, + ClientSecret: corev1.SecretReference{ + Name: azureClusterIdentitySecret, + Namespace: azureClusterIdentityNamespace, + }, + TenantID: ac.TenantID, + Type: "ServicePrincipal", + }, + }, + SecretApplyConfiguration: v1.Secret(azureClusterIdentitySecret, azureClusterIdentityNamespace), + ManifestFilesDir: filepath.Join("/tmp", azureClusterIdentityName), + } + + acic.SecretApplyConfiguration.Type = &azureClusterIdentitySecretType + acic.SecretApplyConfiguration.Data = map[string][]byte{"clientSecret": []byte(ac.ClientSecret)} + + return acic +} + +func createManifestFile(object interface{}, dir, fileName string) (string, error) { + data, err := json.Marshal(object) + if err != nil { + return "", err + } + + return util.CreateTempYAMLFile(dir, fileName, data) +} + +func (acic *AzureClusterIdentityConfig) createAzureClusterIdentityManifestFiles() error { + if err := os.MkdirAll(acic.ManifestFilesDir, 0775); err != nil { + return err + } + + fileCR, err := createManifestFile(acic.AzureClusterIdentity, acic.ManifestFilesDir, azureClusterIdentityName) + if err != nil { + return err + } + + acic.ManifestFiles = append(acic.ManifestFiles, fileCR) + + fileSecret, err := createManifestFile(acic.SecretApplyConfiguration, acic.ManifestFilesDir, azureClusterIdentitySecret) + if err != nil { + return err + } + + acic.ManifestFiles = append(acic.ManifestFiles, fileSecret) + + return nil +} + +func (cc *ClusterCommands) applyAzureClusterIdentity() error { + var kubectlArgs = []string{"apply"} + + ac := azure_provider.NewAzureConfigure() + if err := ac.ReadSPCredentials(cc.Conf.Name); err != nil { + return err + } + + acic := NewAzureClusterIdentityConfig(ac) + if err := acic.createAzureClusterIdentityManifestFiles(); err != nil { + return err + } + + for _, val := range acic.ManifestFiles { + kubectlArgs = append(kubectlArgs, "-f", val) + } + + cc.SpecCMD = cc.kubectl(kubectlArgs...) + if err := releaseRunner(cc).runCMD(); err != nil { + return err + } + + return os.RemoveAll(acic.ManifestFilesDir) +} + +func (cc *ClusterCommands) azureClusterContext() ([]byte, error) { + ac := azure_provider.NewAzureConfigure() + + if err := ac.NewAzureManagedClustersClient(cc.Ctx.Context, cc.Conf.Name); err != nil { + return nil, err + } + + return ac.GetAzureClusterContext(cc.Conf.Tenant, cc.Conf.Name) +} diff --git a/cmd/commands.go b/cmd/commands.go index 850405a..09cb140 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -116,17 +116,18 @@ func Commands() []*cli.Command { Subcommands: []*cli.Command{ { Name: "create", - Usage: "Create CAPI cluster", + Usage: "Create CAPI management cluster", Aliases: []string{"c"}, Before: readInputSourceWithContext(gitSpec, conf, flags["clusterK3DCreate"]), Flags: flags["clusterK3DCreate"], Category: "capi", BashComplete: util.ShellCompleteCustomOutput, Action: K3DCreateAction(conf), + After: CAPIInitAction(conf), }, { Name: "delete", - Usage: "Delete CAPI cluster", + Usage: "Delete CAPI management cluster", Aliases: []string{"d"}, Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], @@ -134,9 +135,18 @@ func Commands() []*cli.Command { BashComplete: util.ShellCompleteCustomOutput, Action: K3DAction(conf, K3DRunner.createDeleteK3DCluster), }, + { + Name: "destroy", + Usage: "Destroy K8S target (workload) cluster", + Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), + Flags: flags["hidden"], + Category: "capi", + BashComplete: util.ShellCompleteCustomOutput, + Action: CAPIProvisionDestroyAction(conf), + }, { Name: "list", - Usage: "List CAPI clusters", + Usage: "List CAPI management clusters", Aliases: []string{"l"}, Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), Flags: flags["hidden"], @@ -144,6 +154,26 @@ func Commands() []*cli.Command { BashComplete: util.ShellCompleteCustomOutput, Action: K3DAction(conf, K3DRunner.listK3DClusters), }, + { + Name: "provision", + Usage: "Provision K8S target (workload) cluster", + Aliases: []string{"p"}, + Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), + Flags: flags["hidden"], + Category: "capi", + BashComplete: util.ShellCompleteCustomOutput, + Action: CAPIProvisionDestroyAction(conf), + }, + { + Name: "update", + Usage: "Update CAPI management cluster", + Aliases: []string{"u"}, + Before: readInputSourceWithContext(gitSpec, conf, flags["hidden"]), + Flags: flags["hidden"], + Category: "capi", + BashComplete: util.ShellCompleteCustomOutput, + Action: CAPIUpdateAction(conf), + }, }, }, { diff --git a/cmd/config.go b/cmd/config.go index 5fb209f..1038f38 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -16,6 +16,7 @@ import ( "rmk/config" "rmk/git_handler" "rmk/providers/aws_provider" + "rmk/providers/azure_provider" "rmk/util" ) @@ -94,12 +95,12 @@ func (c *ConfigCommands) checkAwsEnv() (map[string]string, bool) { func (c *ConfigCommands) configAws() error { if awsEnvs, ok := c.checkAwsEnv(); !ok { c.SpecCMD = c.awsConfigure(c.Conf.Profile) - return runner(c).runCMD() + return releaseRunner(c).runCMD() } else { for key, val := range awsEnvs { c.SpecCMD = c.awsConfigure(c.Conf.Profile) c.SpecCMD.Args = append(c.SpecCMD.Args, "set", key, val) - if err := runner(c).runCMD(); err != nil { + if err := releaseRunner(c).runCMD(); err != nil { return err } } @@ -165,7 +166,7 @@ func (c *ConfigCommands) configAwsMFA() error { for key, val := range MFAProfileArgs { c.SpecCMD = c.awsConfigure(c.Conf.AWSMFAProfile) c.SpecCMD.Args = append(c.SpecCMD.Args, "set", key, val) - if err := runner(c).runCMD(); err != nil { + if err := releaseRunner(c).runCMD(); err != nil { return err } } @@ -173,7 +174,7 @@ func (c *ConfigCommands) configAwsMFA() error { for key, val := range regularProfileArgs { c.SpecCMD = c.awsConfigure(regularProfile) c.SpecCMD.Args = append(c.SpecCMD.Args, "set", key, val) - if err := runner(c).runCMD(); err != nil { + if err := releaseRunner(c).runCMD(); err != nil { return err } } @@ -195,7 +196,7 @@ func (c *ConfigCommands) copyAWSProfile(profile string) error { for key, val := range profileArgs { c.SpecCMD = c.awsConfigure(profile) c.SpecCMD.Args = append(c.SpecCMD.Args, "set", key, val) - if err := runner(c).runCMD(); err != nil { + if err := releaseRunner(c).runCMD(); err != nil { return err } } @@ -206,7 +207,7 @@ func (c *ConfigCommands) copyAWSProfile(profile string) error { func (c *ConfigCommands) installHelmPlugin(plugin config.Package, args ...string) error { c.SpecCMD = c.helmPlugin() c.SpecCMD.Args = append(c.SpecCMD.Args, args...) - if err := runner(c).runCMD(); err != nil { + if err := releaseRunner(c).runCMD(); err != nil { if !strings.Contains(c.SpecCMD.StderrBuf.String(), util.HelmPluginExist) { return fmt.Errorf("Helm plugin %s installation failed: \n%s", plugin.Name, c.SpecCMD.StderrBuf.String()) } @@ -228,7 +229,7 @@ func (c *ConfigCommands) configHelmPlugins() error { c.SpecCMD = c.helmPlugin() c.SpecCMD.Args = append(c.SpecCMD.Args, "list") - if err := runner(c).runCMD(); err != nil { + if err := releaseRunner(c).runCMD(); err != nil { return fmt.Errorf("get Helm plugin list failed: %s", c.SpecCMD.StderrBuf.String()) } @@ -258,7 +259,7 @@ func (c *ConfigCommands) configHelmPlugins() error { zap.S().Infof("Helm plugin %s detect new version %s from %s", plugin.Name, plugin.Version, util.TenantProjectFile) c.SpecCMD = c.helmPlugin() c.SpecCMD.Args = append(c.SpecCMD.Args, "uninstall", plugin.Name) - if err := runner(c).runCMD(); err != nil { + if err := releaseRunner(c).runCMD(); err != nil { return fmt.Errorf("Helm plugin %s uninstallation failed: \n%s", plugin.Name, c.SpecCMD.StderrBuf.String()) } @@ -281,7 +282,7 @@ func (c *ConfigCommands) configHelmPlugins() error { func (c *ConfigCommands) rmkConfig() error { c.SpecCMD = c.rmkConfigInit() - return runner(c).runCMD() + return releaseRunner(c).runCMD() } func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.GitSpec) error { @@ -294,7 +295,7 @@ func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi profile = conf.Profile } - if c.Bool("aws-reconfigure") { + if c.Bool("reconfigure") { if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(conf.Profile), "")); err != nil { return err } @@ -348,7 +349,7 @@ func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAwsMFA(); err != nil { return err } - } else if !c.Bool("aws-reconfigure") { + } else if !c.Bool("reconfigure") { if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAwsMFA(); err != nil { return err } @@ -373,7 +374,7 @@ func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_ return err } - if c.String("cluster-provider") == util.AWSClusterProvider { + if c.String("cluster-provider") == aws_provider.AWSClusterProvider { if err := c.Set("config-name-from", conf.Name); err != nil { return err } @@ -437,6 +438,7 @@ func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_ return nil } + //TODO: deprecate after full list CAPI providers will be implemented if err := util.ValidateGitHubToken(c, "required parameter --github-token not set"); err != nil { return err } @@ -451,7 +453,7 @@ func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_ } } - if c.String("cluster-provider") == util.AWSClusterProvider { + if c.String("cluster-provider") == aws_provider.AWSClusterProvider { conf.AwsConfigure = new(aws_provider.AwsConfigure) } @@ -467,8 +469,13 @@ func configDeleteAction(conf *config.Config) cli.ActionFunc { return err } - // TODO: It is necessary to think about whether to delete unconditionally or check taking into account the AWS provider. - if c.String("cluster-provider") == util.AWSClusterProvider { + switch { + case c.String("cluster-provider") == azure_provider.AzureClusterProvider: + if err := os.RemoveAll(util.GetHomePath(azure_provider.AzureHomeDir, + azure_provider.AzurePrefix+conf.Name+".json")); err != nil { + return err + } + case c.String("cluster-provider") == aws_provider.AWSClusterProvider: // Delete MFA profile if len(conf.AWSMFAProfile) > 0 && len(conf.AWSMFATokenExpiration) > 0 { if err := os.RemoveAll(strings.Join(conf.AWSSharedConfigFile(conf.AWSMFAProfile), "")); err != nil { @@ -531,7 +538,56 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act } switch conf.ClusterProvider { - case util.AWSClusterProvider: + case azure_provider.AzureClusterProvider: + conf.AwsConfigure = &aws_provider.AwsConfigure{} + ac := azure_provider.NewAzureConfigure() + asp := azure_provider.NewRawSP() + + if util.IsExists( + util.GetHomePath(azure_provider.AzureHomeDir, azure_provider.AzurePrefix+gitSpec.ID+".json"), true) { + if err := ac.ReadSPCredentials(gitSpec.ID); err != nil { + return err + } + } + + if c.Bool("azure-service-principle") { + if err := json.NewDecoder(os.Stdin).Decode(&asp); err != nil { + return fmt.Errorf("unable to deserialize json from stdin: %s", err.Error()) + } + + ac.MergeAzureRawSP(asp) + } + + if c.IsSet("azure-client-id") { + ac.ClientID = c.String("azure-client-id") + } + + if c.IsSet("azure-client-secret") { + ac.ClientSecret = c.String("azure-client-secret") + } + + if c.IsSet("azure-subscription-id") { + ac.SubscriptionID = c.String("azure-subscription-id") + } + + if c.IsSet("azure-tenant-id") { + ac.TenantID = c.String("azure-tenant-id") + } + + if err := ac.CheckSPCredentials(); err != nil { + return err + } else { + conf.AzureConfigure = azure_provider.NewAzureConfigure() + conf.AzureConfigure.SubscriptionID = ac.SubscriptionID + } + + if err := ac.WriteSPCredentials(gitSpec.ID); err != nil { + return err + } + + conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+azure_provider.AzureClusterProvider) + case aws_provider.AWSClusterProvider: + conf.AzureConfigure = &azure_provider.AzureConfigure{} conf.AwsConfigure.Profile = gitSpec.ID conf.AWSMFAProfile = c.String("aws-mfa-profile") conf.AWSMFATokenExpiration = c.String("aws-mfa-token-expiration") @@ -548,6 +604,7 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act conf.SopsBucketName = conf.Tenant + "-" + util.SopsRootName + "-" + awsUID case util.LocalClusterProvider: conf.AwsConfigure = &aws_provider.AwsConfigure{} + conf.AzureConfigure = &azure_provider.AzureConfigure{} conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+util.LocalClusterProvider) } @@ -559,7 +616,7 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act return err } - if conf.ClusterProvider == util.AWSClusterProvider { + if conf.ClusterProvider == aws_provider.AWSClusterProvider { //create s3 bucket for sops age keys if err := conf.CreateBucket(conf.SopsBucketName); err != nil { return err diff --git a/cmd/flags.go b/cmd/flags.go index d16c531..fad404e 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -3,7 +3,6 @@ package cmd import ( "github.com/urfave/cli/v2" "github.com/urfave/cli/v2/altsrc" - "rmk/util" ) @@ -21,9 +20,43 @@ func flagsConfig() []cli.Flag { Hidden: true, }, ), + &cli.StringFlag{ + Category: azureFlagsCategory, + Name: "azure-client-id", + Usage: "Azure client ID for Service Principal", + Aliases: []string{"azc"}, + EnvVars: []string{"RMK_AZURE_CLIENT_ID", "AZURE_CLIENT_ID"}, + }, + &cli.StringFlag{ + Category: azureFlagsCategory, + Name: "azure-client-secret", + Usage: "Azure client secret for Service Principal", + Aliases: []string{"azp"}, + EnvVars: []string{"RMK_AZURE_CLIENT_SECRET", "AZURE_CLIENT_SECRET"}, + }, + &cli.BoolFlag{ + Category: azureFlagsCategory, + Name: "azure-service-principle", + Usage: "Azure Service Principal stdin content", + Aliases: []string{"azsp"}, + }, + &cli.StringFlag{ + Category: azureFlagsCategory, + Name: "azure-subscription-id", + Usage: "Azure subscription ID for current Azure platform domain", + Aliases: []string{"azs"}, + EnvVars: []string{"RMK_AZURE_SUBSCRIPTION_ID", "AZURE_SUBSCRIPTION_ID"}, + }, + &cli.StringFlag{ + Category: azureFlagsCategory, + Name: "azure-tenant-id", + Usage: "Azure tenant ID for Service Principal", + Aliases: []string{"azt"}, + EnvVars: []string{"RMK_AZURE_TENANT_ID", "AZURE_TENANT_ID"}, + }, &cli.BoolFlag{ - Name: "aws-reconfigure", - Usage: "force AWS profile creation", + Name: "reconfigure", + Usage: "force Cluster Providers credentials recreate", Aliases: []string{"r"}, }, &cli.StringFlag{ @@ -56,7 +89,7 @@ func flagsConfig() []cli.Flag { Usage: "select cluster provider to provision clusters", Aliases: []string{"cp"}, EnvVars: []string{"RMK_CLUSTER_PROVIDER"}, - Value: util.AWSClusterProvider, + Value: util.LocalClusterProvider, }, ), altsrc.NewBoolFlag( diff --git a/cmd/k3d.go b/cmd/k3d.go index 02833c7..f210740 100644 --- a/cmd/k3d.go +++ b/cmd/k3d.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "os" + "strings" "github.com/urfave/cli/v2" @@ -69,13 +70,13 @@ func (k *K3DCommands) selectCluster() { func (k *K3DCommands) createDeleteK3DCluster() error { k.selectCluster() - if _, _, err := k.getKubeContext(); err != nil { + if _, _, err := clusterRunner(&ClusterCommands{k.ReleaseCommands}).getKubeContext(); err != nil { return err } k.SpecCMD = k.prepareHelmfile("--log-level", "error", "-l", "cluster="+k.Ctx.Command.Category, "template") k.SpecCMD.DisableStdOut = true - if err := runner(k).runCMD(); err != nil { + if err := releaseRunner(k).runCMD(); err != nil { return fmt.Errorf("Helmfile failed to render template by label release: cluster=%s\n%s", k.Ctx.Command.Category, k.SpecCMD.StderrBuf.String()) } @@ -89,7 +90,11 @@ func (k *K3DCommands) createDeleteK3DCluster() error { return err } - if err := runner(k).runCMD(); err != nil { + if err := releaseRunner(k).runCMD(); err != nil { + if err := os.RemoveAll(k3dConfig); err != nil { + return err + } + return err } @@ -102,13 +107,13 @@ func (k *K3DCommands) importImageToK3DCluster() error { return err } - return runner(k).runCMD() + return releaseRunner(k).runCMD() } func (k *K3DCommands) listK3DClusters() error { k.selectCluster() - if _, _, err := k.getKubeContext(); err != nil { + if _, _, err := clusterRunner(&ClusterCommands{k.ReleaseCommands}).getKubeContext(); err != nil { return err } @@ -117,20 +122,31 @@ func (k *K3DCommands) listK3DClusters() error { return err } - return runner(k).runCMD() + k.SpecCMD.DisableStdOut = true + if err := releaseRunner(k).runCMD(); err != nil { + if strings.Contains(k.SpecCMD.StderrBuf.String(), "No nodes found for given cluster") { + return fmt.Errorf("cluster %s not running", util.CAPI) + } else { + return fmt.Errorf("%s", k.SpecCMD.StderrBuf.String()) + } + } + + fmt.Printf("%s", k.SpecCMD.StdoutBuf.String()) + + return nil } if err := k.prepareK3D("cluster", k.Ctx.Command.Name); err != nil { return err } - return runner(k).runCMD() + return releaseRunner(k).runCMD() } func (k *K3DCommands) startStopK3DCluster() error { k.selectCluster() - if _, _, err := k.getKubeContext(); err != nil { + if _, _, err := clusterRunner(&ClusterCommands{k.ReleaseCommands}).getKubeContext(); err != nil { return err } @@ -138,11 +154,12 @@ func (k *K3DCommands) startStopK3DCluster() error { return err } - return runner(k).runCMD() + return releaseRunner(k).runCMD() } func K3DCreateAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { + //TODO: deprecate after full list CAPI providers will be implemented if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -161,6 +178,7 @@ func K3DCreateAction(conf *config.Config) cli.ActionFunc { func K3DAction(conf *config.Config, action func(k3dRunner K3DRunner) error) cli.ActionFunc { return func(c *cli.Context) error { + //TODO: deprecate after full list CAPI providers will be implemented if err := util.ValidateGitHubToken(c, ""); err != nil { return err } diff --git a/cmd/project.go b/cmd/project.go index df334fc..f6bc371 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -446,6 +446,7 @@ func (p *ProjectCommands) generateProject(gitSpec *git_handler.GitSpec) error { func projectGenerateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { + //TODO: deprecate after full list CAPI providers will be implemented if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -460,6 +461,7 @@ func projectGenerateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cl func projectUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { + //TODO: deprecate after full list CAPI providers will be implemented if err := util.ValidateGitHubToken(c, ""); err != nil { return err } diff --git a/cmd/release.go b/cmd/release.go index bb9605d..3cd91a1 100644 --- a/cmd/release.go +++ b/cmd/release.go @@ -23,7 +23,7 @@ import ( "rmk/util" ) -type runner interface { +type releaseRunner interface { runCMD() error } @@ -86,43 +86,6 @@ type HelmStatus struct { Namespace string `json:"namespace"` } -type KubeConfig struct { - Kind string `json:"kind"` - ApiVersion string `json:"apiVersion"` - Preferences struct { - } `json:"preferences"` - Clusters []struct { - Name string `json:"name"` - Cluster struct { - Server string `json:"server"` - CertificateAuthorityData string `json:"certificate-authority-data"` - } `json:"cluster"` - } `json:"clusters"` - Users []struct { - Name string `json:"name"` - User struct { - Exec struct { - Command string `json:"command"` - Args []string `json:"args"` - Env []struct { - Name string `json:"name"` - Value string `json:"value"` - } `json:"env"` - ApiVersion string `json:"apiVersion"` - ProvideClusterInfo bool `json:"provideClusterInfo"` - } `json:"exec"` - } `json:"user"` - } `json:"users"` - Contexts []struct { - Name string `json:"name"` - Context struct { - Cluster string `json:"cluster"` - User string `json:"user"` - } `json:"context"` - } `json:"contexts"` - CurrentContext string `json:"current-context"` -} - func (rc *ReleaseCommands) runCMD() error { if err := rc.SpecCMD.AddEnv(); err != nil { return err @@ -176,6 +139,7 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { "AWS_PROFILE="+rc.Conf.Profile, "AWS_CONFIG_FILE="+strings.Join(rc.Conf.AWSSharedConfigFile(rc.Conf.Profile), ""), "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(rc.Conf.AWSSharedCredentialsFile(rc.Conf.Profile), ""), + "AZURE_SUBSCRIPTION_ID="+rc.Conf.AzureConfigure.SubscriptionID, ) envs = append(envs, "ROOT_DOMAIN="+rc.Conf.RootDomain) @@ -210,17 +174,6 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { } } -func (rc *ReleaseCommands) kubeConfig() *util.SpecCMD { - return &util.SpecCMD{ - Args: []string{"config"}, - Command: "kubectl", - Ctx: rc.Ctx.Context, - Dir: rc.WorkDir, - DisableStdOut: true, - Debug: false, - } -} - func (rc *ReleaseCommands) releaseMiddleware() error { if len(rc.Conf.Dependencies) == 0 { if err := os.RemoveAll(filepath.Join(rc.WorkDir, TenantPrDependenciesDir)); err != nil { @@ -232,7 +185,7 @@ func (rc *ReleaseCommands) releaseMiddleware() error { return err } - if _, currentContext, err := rc.getKubeContext(); err != nil { + if _, currentContext, err := clusterRunner(&ClusterCommands{rc}).getKubeContext(); err != nil { return err } else { switch { @@ -256,94 +209,6 @@ func (rc *ReleaseCommands) releaseHelmfile(args ...string) error { return rc.runCMD() } -func (rc *ReleaseCommands) getKubeContext() (string, string, error) { - var ( - contextNames []string - contextName string - ) - - kubeConfig := &KubeConfig{} - - rc.SpecCMD = rc.kubeConfig() - rc.SpecCMD.Args = append(rc.SpecCMD.Args, "view", "--output", "json") - if err := rc.runCMD(); err != nil { - return "", "", fmt.Errorf("Kubectl config failed to view\n%s", rc.SpecCMD.StderrBuf.String()) - } - - if err := json.Unmarshal(rc.SpecCMD.StdoutBuf.Bytes(), &kubeConfig); err != nil { - return "", "", err - } - - re, err := regexp.Compile(`(?i)\b` + rc.Conf.Name + `\b`) - if err != nil { - return "", "", err - } - - for _, val := range kubeConfig.Contexts { - if re.MatchString(val.Name) { - contextNames = append(contextNames, val.Name) - } - } - - switch { - case len(contextNames) > 1: - return "", "", - fmt.Errorf("detected more than one Kubernetes context with names %s leading to conflict, "+ - "please delete or rename all contexts except one", strings.Join(contextNames, ", ")) - case len(contextNames) > 0: - contextName = contextNames[0] - default: - contextName = "" - } - - if rc.K3DCluster && len(contextName) > 0 && !strings.Contains(contextName, util.K3DPrefix) { - return "", "", fmt.Errorf("remote Kubernetes context already exists %s for this branch", contextName) - } - - return contextName, kubeConfig.CurrentContext, nil -} - -func (rc *ReleaseCommands) releaseKubeContext() error { - contextName, currentContextName, err := rc.getKubeContext() - if err != nil { - return err - } - - if len(contextName) > 0 && !rc.UpdateContext { - if contextName != currentContextName { - rc.SpecCMD = rc.kubeConfig() - rc.SpecCMD.Args = append(rc.SpecCMD.Args, "use", contextName) - rc.SpecCMD.DisableStdOut = false - return rc.runCMD() - } - - return nil - } - - if strings.Contains(contextName, util.K3DPrefix) && rc.UpdateContext { - return fmt.Errorf("current context %s already used for K3D cluster, --force flag cannot be used", contextName) - } - - if err := newClusterCommands(rc.Conf, rc.Ctx, util.GetPwdPath("")).awsClusterContext(); err != nil { - return err - } - - _, currentContext, err := rc.getKubeContext() - if err != nil { - return err - } - - rc.SpecCMD = rc.kubeConfig() - rc.SpecCMD.Args = append(rc.SpecCMD.Args, - "set-credentials", currentContext, - "--exec-env", "AWS_CONFIG_FILE="+strings.Join(rc.Conf.AWSSharedConfigFile(rc.Conf.Profile), ""), - "--exec-env", "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(rc.Conf.AWSSharedCredentialsFile(rc.Conf.Profile), ""), - ) - rc.SpecCMD.DisableStdOut = true - rc.SpecCMD.Debug = true - return rc.runCMD() -} - func (sr *SpecRelease) searchReleasesPath() error { paths, err := util.WalkInDir(util.GetPwdPath(util.TenantValuesDIR), sr.Conf.Environment, util.ReleasesFileName) if err != nil { @@ -638,6 +503,7 @@ func (sr *SpecRelease) checkStatusRelease() error { func releaseHelmfileAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { + //TODO: deprecate after full list CAPI providers will be implemented if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -657,7 +523,7 @@ func releaseHelmfileAction(conf *config.Config) cli.ActionFunc { } if !c.Bool("skip-context-switch") { - if err := rc.releaseKubeContext(); err != nil { + if err := clusterRunner(&ClusterCommands{rc}).switchKubeContext(); err != nil { return err } } @@ -691,6 +557,7 @@ func releaseHelmfileAction(conf *config.Config) cli.ActionFunc { func releaseRollbackAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { + //TODO: deprecate after full list CAPI providers will be implemented if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -712,7 +579,7 @@ func releaseRollbackAction(conf *config.Config) cli.ActionFunc { sr.WorkDir = util.GetPwdPath("") if !c.Bool("skip-context-switch") { - if err := sr.releaseKubeContext(); err != nil { + if err := clusterRunner(&ClusterCommands{&sr.ReleaseCommands}).switchKubeContext(); err != nil { return err } } @@ -728,6 +595,7 @@ func releaseRollbackAction(conf *config.Config) cli.ActionFunc { func releaseUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { + //TODO: deprecate after full list CAPI providers will be implemented if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -746,7 +614,7 @@ func releaseUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli. sr.WorkDir = util.GetPwdPath("") if !c.Bool("skip-context-switch") { - if err := sr.releaseKubeContext(); err != nil { + if err := clusterRunner(&ClusterCommands{&sr.ReleaseCommands}).switchKubeContext(); err != nil { return err } } diff --git a/cmd/secret.go b/cmd/secret.go index eeb6db0..9edd670 100644 --- a/cmd/secret.go +++ b/cmd/secret.go @@ -72,13 +72,13 @@ func (sc *SecretCommands) createAgeKey(scope string) error { sc.SpecCMD = sc.ageKeygen("-o", keyPath) sc.SpecCMD.DisableStdOut = true - if err := runner(sc).runCMD(); err != nil { + if err := releaseRunner(sc).runCMD(); err != nil { return err } sc.SpecCMD = sc.ageKeygen("-y", keyPath) sc.SpecCMD.DisableStdOut = true - return runner(sc).runCMD() + return releaseRunner(sc).runCMD() } func (sc *SecretCommands) CreateKeys() error { @@ -265,7 +265,7 @@ func (sc *SecretCommands) SecretManager(option string) error { switch sc.Ctx.Command.Name { case "decrypt": sc.SpecCMD.Args = append(sc.SpecCMD.Args, sc.Ctx.Command.Name, "-i", secret) - if err := runner(sc).runCMD(); err != nil { + if err := releaseRunner(sc).runCMD(); err != nil { if strings.Contains(sc.SpecCMD.StderrBuf.String(), util.HelmSecretsIsNotEncrypted+secret) { zap.S().Warnf(strings.ToLower(util.HelmSecretsIsNotEncrypted)+"%s", secret) continue @@ -277,7 +277,7 @@ func (sc *SecretCommands) SecretManager(option string) error { zap.S().Infof("decrypting: %s", secret) case "encrypt": sc.SpecCMD.Args = append(sc.SpecCMD.Args, sc.Ctx.Command.Name, "-i", secret) - if err := runner(sc).runCMD(); err != nil { + if err := releaseRunner(sc).runCMD(); err != nil { if strings.Contains(sc.SpecCMD.StderrBuf.String(), util.HelmSecretsAlreadyEncrypted+filepath.Base(secret)) { zap.S().Warnf(strings.ToLower(util.HelmSecretsAlreadyEncrypted)+"%s", secret) continue @@ -349,7 +349,7 @@ func (sc *SecretCommands) runHelmSecretsCMD(secretFilePath string, returnCMDErro return fmt.Errorf("file does not exist: %s", secretFilePath) } - if err := runner(sc).runCMD(); err != nil { + if err := releaseRunner(sc).runCMD(); err != nil { if returnCMDError { return err } @@ -379,6 +379,7 @@ func (sc *SecretCommands) runHelmSecretsCMD(secretFilePath string, returnCMDErro func secretMgrEncryptDecryptAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { + //TODO: deprecate after full list CAPI providers will be implemented if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -406,6 +407,7 @@ func secretMgrGenerateAction(conf *config.Config) cli.ActionFunc { } func secretKeysCreateAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { + //TODO: deprecate after full list CAPI providers will be implemented if err := util.ValidateGitHubToken(c, ""); err != nil { return err } @@ -444,6 +446,7 @@ func secretKeysUploadAction(conf *config.Config) cli.ActionFunc { func secretAction(conf *config.Config, action func(secretRunner SecretRunner) error) cli.ActionFunc { return func(c *cli.Context) error { + //TODO: deprecate after full list CAPI providers will be implemented if err := util.ValidateGitHubToken(c, ""); err != nil { return err } diff --git a/config/config.go b/config/config.go index 96824f2..89c1e1d 100644 --- a/config/config.go +++ b/config/config.go @@ -18,28 +18,30 @@ import ( "rmk/git_handler" "rmk/providers/aws_provider" + "rmk/providers/azure_provider" "rmk/util" ) type Config struct { - Name string `yaml:"name,omitempty"` - Tenant string `yaml:"tenant,omitempty"` - Environment string `yaml:"environment,omitempty"` - ConfigNameFrom string `yaml:"config-name-from,omitempty"` - RootDomain string `yaml:"root-domain,omitempty"` - GitHubToken string `yaml:"github-token,omitempty"` - ClusterProvider string `yaml:"cluster-provider"` - SlackNotifications bool `yaml:"slack-notifications"` - SlackWebHook string `yaml:"slack-webhook,omitempty"` - SlackChannel string `yaml:"slack-channel,omitempty"` - SlackMsgDetails []string `yaml:"slack-message-details,omitempty"` - SopsAgeKeys string `yaml:"sops-age-keys,omitempty"` - SopsBucketName string `yaml:"sops-bucket-name,omitempty"` - AWSMFAProfile string `yaml:"aws-mfa-profile,omitempty"` - AWSMFATokenExpiration string `yaml:"aws-mfa-token-expiration,omitempty"` - *aws_provider.AwsConfigure `yaml:"aws,omitempty"` - ProgressBar bool `yaml:"progress-bar"` - ProjectFile `yaml:"project-file"` + Name string `yaml:"name,omitempty"` + Tenant string `yaml:"tenant,omitempty"` + Environment string `yaml:"environment,omitempty"` + ConfigNameFrom string `yaml:"config-name-from,omitempty"` + RootDomain string `yaml:"root-domain,omitempty"` + GitHubToken string `yaml:"github-token,omitempty"` + ClusterProvider string `yaml:"cluster-provider"` + SlackNotifications bool `yaml:"slack-notifications"` + SlackWebHook string `yaml:"slack-webhook,omitempty"` + SlackChannel string `yaml:"slack-channel,omitempty"` + SlackMsgDetails []string `yaml:"slack-message-details,omitempty"` + SopsAgeKeys string `yaml:"sops-age-keys,omitempty"` + SopsBucketName string `yaml:"sops-bucket-name,omitempty"` + AWSMFAProfile string `yaml:"aws-mfa-profile,omitempty"` + AWSMFATokenExpiration string `yaml:"aws-mfa-token-expiration,omitempty"` + *aws_provider.AwsConfigure `yaml:"aws,omitempty"` + *azure_provider.AzureConfigure `yaml:"azure,omitempty"` + ProgressBar bool `yaml:"progress-bar"` + ProjectFile `yaml:"project-file"` } type HookMapping struct { diff --git a/go.mod b/go.mod index d566e8b..bd971bc 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,12 @@ module rmk -go 1.21 +go 1.22.0 + +toolchain go1.22.8 require ( + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.1.0 github.com/Masterminds/semver v1.5.0 github.com/Masterminds/sprig/v3 v3.2.3 github.com/aws/aws-sdk-go-v2 v1.24.1 @@ -15,28 +19,35 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 github.com/aws/smithy-go v1.19.0 github.com/cheggaaa/pb v1.0.29 + github.com/ghodss/yaml v1.0.0 github.com/go-git/go-git/v5 v5.11.0 github.com/google/go-github v17.0.0+incompatible github.com/hashicorp/go-getter v1.7.3 github.com/slack-go/slack v0.12.3 github.com/urfave/cli/v2 v2.27.1 - go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.18.0 - golang.org/x/oauth2 v0.16.0 + go.uber.org/zap v1.27.0 + golang.org/x/crypto v0.27.0 + golang.org/x/net v0.28.0 + golang.org/x/oauth2 v0.21.0 gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.31.1 + k8s.io/apimachinery v0.31.1 + k8s.io/client-go v0.31.1 mvdan.cc/sh/v3 v3.8.0 ) require ( - cloud.google.com/go v0.110.2 // indirect - cloud.google.com/go/compute v1.20.1 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.13.0 // indirect - cloud.google.com/go/storage v1.29.0 // indirect + cloud.google.com/go v0.110.6 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/iam v1.1.1 // indirect + cloud.google.com/go/storage v1.30.1 // indirect dario.cat/mergo v1.0.0 // indirect - github.com/BurntSushi/toml v1.3.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect + github.com/BurntSushi/toml v1.4.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/aws/aws-sdk-go v1.44.122 // indirect @@ -55,59 +66,93 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect - github.com/cloudflare/circl v1.3.3 // indirect + github.com/cloudflare/circl v1.3.7 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/fatih/color v1.17.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-querystring v1.1.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/s2a-go v0.1.4 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.11.0 // indirect - github.com/gorilla/websocket v1.4.2 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-version v1.6.0 // indirect - github.com/huandu/xstrings v1.3.3 // indirect - github.com/imdario/mergo v0.3.11 // indirect + github.com/huandu/xstrings v1.4.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.15.11 // indirect - github.com/mattn/go-runewidth v0.0.4 // indirect - github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/reflectwalk v1.0.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo/v2 v2.20.2 // indirect + github.com/onsi/gomega v1.34.2 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect + github.com/rivo/uniseg v0.4.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect - github.com/shopspring/decimal v1.2.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect github.com/skeema/knownhosts v1.2.1 // indirect - github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/ulikunitz/xz v0.5.10 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.opencensus.io v0.24.0 // indirect - go.uber.org/multierr v1.10.0 // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.17.0 // indirect - golang.org/x/term v0.17.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.17.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.24.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.126.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/grpc v1.55.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect + google.golang.org/grpc v1.64.1 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 6474851..9701f9e 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= -cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= +cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= +cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= @@ -68,10 +68,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg= -cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= @@ -109,8 +107,8 @@ cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y97 cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= +cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= @@ -171,8 +169,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= @@ -187,16 +185,33 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0 h1:5n7dPVqsWfVKw+ZiEKSd3Kzu7gwBkbEBkeXb8rgaE9Q= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0/go.mod h1:HcZY0PHPo/7d75p99lB6lK0qYOP4vLRJUBpiehYXtLQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.1.0 h1:FvuejXWdMIPK6sY0Tt3lgb45BCVybrvmmnGCEC7a1i4= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.1.0/go.mod h1:drbnYtukMoZqUQq9hJASf41w3RB4VoTJPoPpe+XDHPU= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= @@ -271,8 +286,9 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -287,10 +303,13 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -304,10 +323,14 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= @@ -322,8 +345,22 @@ github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lK github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -356,11 +393,13 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -382,6 +421,9 @@ github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4r github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -403,13 +445,16 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= @@ -427,8 +472,9 @@ github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMd github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= @@ -440,22 +486,29 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= @@ -466,32 +519,57 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= +github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= +github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -500,20 +578,25 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/slack-go/slack v0.12.3 h1:92/dfFU8Q5XP6Wp5rr5/T5JHLM5c5Smtn53fhToAP88= github.com/slack-go/slack v0.12.3/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -521,12 +604,14 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -547,12 +632,12 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= -go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -564,8 +649,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -603,8 +688,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -656,8 +741,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -683,8 +768,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -700,8 +785,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -768,21 +853,22 @@ golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -796,11 +882,13 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -838,6 +926,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -846,6 +935,7 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -854,8 +944,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -921,8 +1011,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1024,12 +1115,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 h1:QW9+G6Fir4VcRXVH8x3LilNAb6cxBGLa6+GM4hRwexE= +google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3/go.mod h1:kdrSS/OiLkPrNUpzD4aHgCq2rVuC/YRxok32HXZ4vRE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1065,8 +1156,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1083,8 +1174,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1092,14 +1183,17 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1110,8 +1204,26 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= +k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= +k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= +k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= +k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a h1:zD1uj3Jf+mD4zmA7W+goE5TxDkI7OGJjBNBzq5fJtLA= +k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a/go.mod h1:UxDHUPsUwTOOxSU+oXURfFBcAS6JwiRXTYqYwfuGowc= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= mvdan.cc/sh/v3 v3.8.0 h1:ZxuJipLZwr/HLbASonmXtcvvC9HXY9d2lXZHnKGjFc8= mvdan.cc/sh/v3 v3.8.0/go.mod h1:w04623xkgBVo7/IUK89E0g8hBykgEpN0vgOj3RJr6MY= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/providers/aws_provider/aws.go b/providers/aws_provider/aws.go index 7f74ffb..75a3f77 100644 --- a/providers/aws_provider/aws.go +++ b/providers/aws_provider/aws.go @@ -31,6 +31,10 @@ import ( "rmk/util" ) +const ( + AWSClusterProvider = "aws" +) + type AwsConfigure struct { Profile string `yaml:"profile,omitempty"` Region string `yaml:"region,omitempty"` diff --git a/providers/azure_provider/azure.go b/providers/azure_provider/azure.go index b5dc3a4..a3db01e 100644 --- a/providers/azure_provider/azure.go +++ b/providers/azure_provider/azure.go @@ -1 +1,153 @@ package azure_provider + +import ( + "encoding/json" + "fmt" + "os" + "reflect" + "strings" + + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6" + "golang.org/x/net/context" + + "rmk/util" +) + +const ( + AzureClusterProvider = "azure" + AzureHomeDir = "." + AzureClusterProvider + AzurePrefix = "service-principal-credentials_" +) + +type AzureRawServicePrincipal struct { + AppId string `json:"appId"` + DisplayName string `json:"displayName"` + Password string `json:"password"` + Tenant string `json:"tenant"` +} + +type AzureClient struct { + ARMClient *armcontainerservice.ManagedClustersClient `json:"-" yaml:"-"` + Ctx context.Context `json:"-" yaml:"-"` +} + +type AzureConfigure struct { + AzureClient `json:"-" yaml:"-"` + ClientID string `json:"client-id,omitempty" yaml:"-"` + ClientSecret string `json:"client-secret,omitempty" yaml:"-"` + SubscriptionID string `json:"subscription-id,omitempty"` + TenantID string `json:"tenant-id,omitempty" yaml:"-"` +} + +func NewAzureConfigure() *AzureConfigure { + return &AzureConfigure{} +} + +func NewRawSP() *AzureRawServicePrincipal { + return &AzureRawServicePrincipal{} +} + +func (ac *AzureConfigure) MergeAzureRawSP(asp *AzureRawServicePrincipal) { + ac.ClientID = asp.AppId + ac.ClientSecret = asp.Password + ac.TenantID = asp.Tenant +} + +func getTagStructName(i interface{}, name string) error { + if field, ok := reflect.TypeOf(i).Elem().FieldByName(name); ok { + return fmt.Errorf("service principal option %s not defined", strings.TrimSuffix(field.Tag.Get("json"), ",omitempty")) + } else { + return fmt.Errorf("field by name %s not defined", name) + } +} + +func (ac *AzureConfigure) CheckSPCredentials() error { + if len(ac.ClientID) == 0 { + if err := getTagStructName(ac, "ClientID"); err != nil { + return err + } + } + + if len(ac.ClientSecret) == 0 { + if err := getTagStructName(ac, "ClientSecret"); err != nil { + return err + } + } + + if len(ac.SubscriptionID) == 0 { + if err := getTagStructName(ac, "SubscriptionID"); err != nil { + return err + } + } + + if len(ac.TenantID) == 0 { + if err := getTagStructName(ac, "TenantID"); err != nil { + return err + } + } + + return nil +} + +func (ac *AzureConfigure) ReadSPCredentials(fileSuffix string) error { + data, err := os.ReadFile(util.GetHomePath(AzureHomeDir, AzurePrefix+fileSuffix+".json")) + if err != nil { + return err + } + + return json.Unmarshal(data, &ac) +} + +func (ac *AzureConfigure) WriteSPCredentials(fileSuffix string) error { + data, err := json.MarshalIndent(ac, "", " ") + if err != nil { + return err + } + + data = []byte(string(data) + "\n") + + if !util.IsExists(util.GetHomePath(AzureHomeDir), false) { + if err := os.MkdirAll(util.GetHomePath(AzureHomeDir), 0755); err != nil { + return err + } + } + + return os.WriteFile( + util.GetHomePath(AzureHomeDir, AzurePrefix+fileSuffix+".json"), + data, 0644) +} + +func (ac *AzureConfigure) NewAzureManagedClustersClient(ctx context.Context, fileName string) error { + if err := ac.ReadSPCredentials(fileName); err != nil { + return err + } + + cred, err := azidentity.NewClientSecretCredential(ac.TenantID, ac.ClientID, ac.ClientSecret, nil) + if err != nil { + return err + } + + factory, err := armcontainerservice.NewClientFactory(ac.SubscriptionID, cred, nil) + if err != nil { + return err + } + + ac.ARMClient = factory.NewManagedClustersClient() + ac.Ctx = ctx + + return nil +} + +func (ac *AzureConfigure) GetAzureClusterContext(groupName, clusterName string) ([]byte, error) { + credentials, err := ac.ARMClient.ListClusterAdminCredentials(ac.Ctx, groupName, clusterName, nil) + if err != nil { + return nil, err + } + + if len(credentials.CredentialResults.Kubeconfigs) == 1 { + return credentials.CredentialResults.Kubeconfigs[0].Value, nil + } + + return nil, fmt.Errorf("context for cluster %s not found", clusterName) +} diff --git a/util/dictionary.go b/util/dictionary.go index 760acd9..c923071 100644 --- a/util/dictionary.go +++ b/util/dictionary.go @@ -1,9 +1,8 @@ package util const ( - AWSClusterProvider = "aws" - AzureClusterProvider = "azure" CAPI = "capi" + CAPIContextName = K3DPrefix + "-" + CAPI GitSSHPrivateKey = ".ssh/id_rsa" GlobalsFileName = "globals.yaml.gotmpl" HelmfileFileName = "helmfile.yaml" From 0100612ec0e7072d31d070a46f1943073d28bd16 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Thu, 31 Oct 2024 15:26:55 +0100 Subject: [PATCH 16/39] #29 - implement support AWS provider for Cluster API --- cmd/cluster.go | 84 ++++-- cmd/cluster_capa.go | 175 +++++++++-- cmd/cluster_capz.go | 15 +- cmd/config.go | 470 +++++++++--------------------- cmd/flags.go | 88 +++--- cmd/k3d.go | 12 +- cmd/project.go | 10 - cmd/release.go | 75 ++--- cmd/secret.go | 15 - config/config.go | 1 - go.mod | 12 +- go.sum | 24 +- providers/aws_provider/aws.go | 334 ++++++++++++++------- providers/azure_provider/azure.go | 30 +- util/system.go | 2 +- util/validate.go | 12 - 16 files changed, 712 insertions(+), 647 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index 2c0e282..c5630c6 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -10,6 +10,7 @@ import ( "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/json" "k8s.io/client-go/tools/clientcmd" clientcmdlatest "k8s.io/client-go/tools/clientcmd/api/latest" @@ -42,8 +43,15 @@ func newClusterCommands(conf *config.Config, ctx *cli.Context, workDir string) * func (cc *ClusterCommands) clusterCTL(args ...string) *util.SpecCMD { var envs []string - switch { - case cc.Conf.ClusterProvider == azure_provider.AzureClusterProvider: + switch cc.Conf.ClusterProvider { + case aws_provider.AWSClusterProvider: + envs = []string{ + "AWS_B64ENCODED_CREDENTIALS=", + "CAPA_EKS_IAM=true", + "CAPA_EKS_ADD_ROLES=true", + "EXP_MACHINE_POOL=true", + } + case azure_provider.AzureClusterProvider: envs = []string{ "EXP_AKS=true", "EXP_MACHINE_POOL=true", @@ -72,6 +80,15 @@ func (cc *ClusterCommands) kubectl(args ...string) *util.SpecCMD { } } +func createManifestFile(object interface{}, dir, fileName string) (string, error) { + data, err := json.Marshal(object) + if err != nil { + return "", err + } + + return util.CreateTempYAMLFile(dir, fileName, data) +} + func createClusterCTLConfigFile(output []byte) (string, error) { clusterCTL := &ClusterCTLConfig{} if err := yaml.Unmarshal(output, &clusterCTL); err != nil { @@ -99,7 +116,7 @@ func (cc *ClusterCommands) getClusterCTLConfig() (string, string, error) { cc.SpecCMD = cc.prepareHelmfile("--log-level", "error", "-l", "config=clusterctl", "template") cc.SpecCMD.DisableStdOut = true if err := releaseRunner(cc).runCMD(); err != nil { - return "", "", fmt.Errorf("Helmfile failed to render template by label release: config=clusterctl\n%s", + return "", "", fmt.Errorf("Helmfile failed to render template by release label: config=clusterctl\n%s", cc.SpecCMD.StderrBuf.String()) } @@ -254,6 +271,23 @@ func (cc *ClusterCommands) switchKubeContext() error { } switch cc.Conf.ClusterProvider { + case aws_provider.AWSClusterProvider: + //if err := cc.awsClusterContext(); err != nil { + // return err + //} + // + //_, currentContext, err := cc.getKubeContext() + //if err != nil { + // return err + //} + // + //cc.SpecCMD = cc.kubectl("config", "set-credentials", currentContext, + // "--exec-env", "AWS_CONFIG_FILE="+strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""), + // "--exec-env", "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""), + //) + //cc.SpecCMD.DisableStdOut = true + //cc.SpecCMD.Debug = true + //return releaseRunner(cc).runCMD() case azure_provider.AzureClusterProvider: clusterContext, err := cc.azureClusterContext() if err != nil { @@ -263,23 +297,6 @@ func (cc *ClusterCommands) switchKubeContext() error { if err := cc.mergeKubeConfig(clusterContext); err != nil { return err } - case aws_provider.AWSClusterProvider: - if err := cc.awsClusterContext(); err != nil { - return err - } - - _, currentContext, err := cc.getKubeContext() - if err != nil { - return err - } - - cc.SpecCMD = cc.kubectl("config", "set-credentials", currentContext, - "--exec-env", "AWS_CONFIG_FILE="+strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""), - "--exec-env", "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""), - ) - cc.SpecCMD.DisableStdOut = true - cc.SpecCMD.Debug = true - return releaseRunner(cc).runCMD() } return nil @@ -303,6 +320,15 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { } switch cc.Conf.ClusterProvider { + case aws_provider.AWSClusterProvider: + clusterContext, err := cc.AWSClusterContext() + if err != nil { + return err + } + + if err := cc.mergeKubeConfig(clusterContext); err != nil { + return err + } case azure_provider.AzureClusterProvider: clusterContext, err := cc.azureClusterContext() if err != nil { @@ -378,7 +404,14 @@ func CAPIInitAction(conf *config.Config) cli.AfterFunc { return err } - return cc.applyAzureClusterIdentity() + switch cc.Conf.ClusterProvider { + case aws_provider.AWSClusterProvider: + return cc.applyAWSClusterIdentity() + case azure_provider.AzureClusterProvider: + return cc.applyAzureClusterIdentity() + } + + return nil } } @@ -401,7 +434,14 @@ func CAPIUpdateAction(conf *config.Config) cli.ActionFunc { return err } - return cc.applyAzureClusterIdentity() + switch cc.Conf.ClusterProvider { + case aws_provider.AWSClusterProvider: + return cc.applyAWSClusterIdentity() + case azure_provider.AzureClusterProvider: + return cc.applyAzureClusterIdentity() + } + + return nil } } diff --git a/cmd/cluster_capa.go b/cmd/cluster_capa.go index 6cfdc1b..9661b52 100644 --- a/cmd/cluster_capa.go +++ b/cmd/cluster_capa.go @@ -1,38 +1,167 @@ package cmd import ( - "strings" + "os" + "path/filepath" - "rmk/util" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/client-go/applyconfigurations/core/v1" + + "rmk/providers/aws_provider" ) const ( awsFlagsCategory = "AWS authentication" + + awsIAMControllerCredentialsTemplate = `[default] +aws_access_key_id = {{ .AwsCredentialsProfile.AccessKeyID }} +aws_secret_access_key = {{ .AwsCredentialsProfile.SecretAccessKey }} +region = {{ .Region }} +{{- if .AwsCredentialsProfile.SessionToken }} +aws_session_token = {{ .AwsCredentialsProfile.SessionToken }} +{{- end }} +` + awsIAMControllerSecret = "aws-iam-controller-secret" + awsClusterStaticIdentityName = "aws-cluster-identity" + awsClusterStaticIdentityNamespace = "capa-system" + awsClusterStaticIdentitySecret = "aws-cluster-identity-secret" ) -func (cc *ClusterCommands) getAWSEksKubeConfig() *util.SpecCMD { - return &util.SpecCMD{ - Envs: []string{ - "AWS_PROFILE=" + cc.Conf.Profile, - "AWS_CONFIG_FILE=" + strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""), - "AWS_SHARED_CREDENTIALS_FILE=" + strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""), - }, - Args: []string{"eks", "--region", - cc.Conf.Region, - "update-kubeconfig", - "--name", - cc.Conf.Name + "-eks", - "--profile", - cc.Conf.Profile, +var awsClusterStaticIdentitySecretType = corev1.SecretTypeOpaque + +type AWSClusterStaticIdentityConfig struct { + *AWSClusterStaticIdentity + *v1.SecretApplyConfiguration + AWSIAMControllerSecret *v1.SecretApplyConfiguration + ManifestFiles []string + ManifestFilesDir string +} + +type AWSClusterStaticIdentity struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AWSClusterStaticIdentitySpec `json:"spec,omitempty"` +} + +type AWSClusterStaticIdentitySpec struct { + AllowedNamespaces struct { + NamespaceList []string `json:"list"` + Selector *metav1.LabelSelector `json:"selector,omitempty"` + } `json:"allowedNamespaces,omitempty"` + SecretRef string `json:"secretRef"` +} + +func NewAWSClusterStaticIdentityConfig(ac *aws_provider.AwsConfigure) *AWSClusterStaticIdentityConfig { + acic := &AWSClusterStaticIdentityConfig{ + AWSClusterStaticIdentity: &AWSClusterStaticIdentity{ + TypeMeta: metav1.TypeMeta{ + Kind: "AWSClusterStaticIdentity", + APIVersion: "infrastructure.cluster.x-k8s.io/v1beta2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: awsClusterStaticIdentityName, + Namespace: awsClusterStaticIdentityNamespace, + Labels: map[string]string{"clusterctl.cluster.x-k8s.io/move-hierarchy": "true"}, + }, + Spec: AWSClusterStaticIdentitySpec{ + AllowedNamespaces: struct { + NamespaceList []string `json:"list"` + Selector *metav1.LabelSelector `json:"selector,omitempty"` + }(struct { + NamespaceList []string + Selector *metav1.LabelSelector + }{ + NamespaceList: []string{awsClusterStaticIdentityNamespace}, + }), + SecretRef: awsClusterStaticIdentitySecret, + }, }, - Command: "aws", - Ctx: cc.Ctx.Context, - Dir: cc.WorkDir, - Debug: true, + AWSIAMControllerSecret: v1.Secret(awsIAMControllerSecret, awsClusterStaticIdentityNamespace), + SecretApplyConfiguration: v1.Secret(awsClusterStaticIdentitySecret, awsClusterStaticIdentityNamespace), + ManifestFilesDir: filepath.Join("/tmp", awsClusterStaticIdentityName), + } + + profile, err := ac.RenderAWSConfigProfile(awsIAMControllerCredentialsTemplate) + if err != nil { + return nil + } + + acic.AWSIAMControllerSecret.Type = &awsClusterStaticIdentitySecretType + acic.AWSIAMControllerSecret.Data = map[string][]byte{"credentials": profile} + + acic.SecretApplyConfiguration.Type = &awsClusterStaticIdentitySecretType + acic.SecretApplyConfiguration.Data = map[string][]byte{ + "AccessKeyID": []byte(ac.AwsCredentialsProfile.AccessKeyID), + "SecretAccessKey": []byte(ac.AwsCredentialsProfile.SecretAccessKey), + } + + if len(ac.AwsCredentialsProfile.SessionToken) > 0 { + acic.SecretApplyConfiguration.Data["SessionToken"] = []byte(ac.AwsCredentialsProfile.SessionToken) + } + + return acic +} + +func (acic *AWSClusterStaticIdentityConfig) createAWSClusterIdentityManifestFiles() error { + if err := os.MkdirAll(acic.ManifestFilesDir, 0775); err != nil { + return err } + + fileCR, err := createManifestFile(acic.AWSClusterStaticIdentity, acic.ManifestFilesDir, awsClusterStaticIdentityName) + if err != nil { + return err + } + + acic.ManifestFiles = append(acic.ManifestFiles, fileCR) + + fileCRSecret, err := createManifestFile(acic.SecretApplyConfiguration, acic.ManifestFilesDir, awsClusterStaticIdentitySecret) + if err != nil { + return err + } + + acic.ManifestFiles = append(acic.ManifestFiles, fileCRSecret) + + fileIAMControllerSecret, err := createManifestFile(acic.AWSIAMControllerSecret, acic.ManifestFilesDir, awsIAMControllerSecret) + if err != nil { + return err + } + + acic.ManifestFiles = append(acic.ManifestFiles, fileIAMControllerSecret) + + return nil +} + +func (cc *ClusterCommands) applyAWSClusterIdentity() error { + var kubectlArgs = []string{"apply"} + + ac := aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile) + if err := ac.ReadAWSConfigProfile(); err != nil { + return err + } + + acic := NewAWSClusterStaticIdentityConfig(ac) + if err := acic.createAWSClusterIdentityManifestFiles(); err != nil { + return err + } + + for _, val := range acic.ManifestFiles { + kubectlArgs = append(kubectlArgs, "-f", val) + } + + cc.SpecCMD = cc.kubectl(kubectlArgs...) + if err := releaseRunner(cc).runCMD(); err != nil { + if err := os.RemoveAll(acic.ManifestFilesDir); err != nil { + return err + } + + return err + } + + return os.RemoveAll(acic.ManifestFilesDir) } -func (cc *ClusterCommands) awsClusterContext() error { - cc.SpecCMD = cc.getAWSEksKubeConfig() - return releaseRunner(cc).runCMD() +func (cc *ClusterCommands) AWSClusterContext() ([]byte, error) { + return aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile).GetAWSClusterContext(cc.Conf.Name) } diff --git a/cmd/cluster_capz.go b/cmd/cluster_capz.go index 5bc1aa4..5a30900 100644 --- a/cmd/cluster_capz.go +++ b/cmd/cluster_capz.go @@ -6,11 +6,9 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/json" v1 "k8s.io/client-go/applyconfigurations/core/v1" "rmk/providers/azure_provider" - "rmk/util" ) const ( @@ -88,15 +86,6 @@ func NewAzureClusterIdentityConfig(ac *azure_provider.AzureConfigure) *AzureClus return acic } -func createManifestFile(object interface{}, dir, fileName string) (string, error) { - data, err := json.Marshal(object) - if err != nil { - return "", err - } - - return util.CreateTempYAMLFile(dir, fileName, data) -} - func (acic *AzureClusterIdentityConfig) createAzureClusterIdentityManifestFiles() error { if err := os.MkdirAll(acic.ManifestFilesDir, 0775); err != nil { return err @@ -138,6 +127,10 @@ func (cc *ClusterCommands) applyAzureClusterIdentity() error { cc.SpecCMD = cc.kubectl(kubectlArgs...) if err := releaseRunner(cc).runCMD(); err != nil { + if err := os.RemoveAll(acic.ManifestFilesDir); err != nil { + return err + } + return err } diff --git a/cmd/config.go b/cmd/config.go index 1038f38..d067792 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -28,20 +28,6 @@ func newConfigCommands(conf *config.Config, ctx *cli.Context, workDir string) *C return &ConfigCommands{&ReleaseCommands{Conf: conf, Ctx: ctx, WorkDir: workDir}} } -func (c *ConfigCommands) awsConfigure(profile string) *util.SpecCMD { - return &util.SpecCMD{ - Args: []string{"configure", "--profile", profile}, - Envs: []string{ - "AWS_CONFIG_FILE=" + strings.Join(c.Conf.AWSSharedConfigFile(profile), ""), - "AWS_SHARED_CREDENTIALS_FILE=" + strings.Join(c.Conf.AWSSharedCredentialsFile(profile), ""), - }, - Command: "aws", - Ctx: c.Ctx.Context, - Dir: c.WorkDir, - Debug: false, - } -} - func (c *ConfigCommands) helmPlugin() *util.SpecCMD { return &util.SpecCMD{ Args: []string{"plugin"}, @@ -53,67 +39,55 @@ func (c *ConfigCommands) helmPlugin() *util.SpecCMD { } } -func (c *ConfigCommands) rmkConfigInit() *util.SpecCMD { - exRMK, err := os.Executable() - if err != nil { - panic(err) - } +func (c *ConfigCommands) configAws(profile string) error { + ac := aws_provider.NewAwsConfigure(c.Ctx.Context, profile) + ac.ConfigSource = strings.Join(ac.AWSSharedConfigFile(profile), "") + ac.CredentialsSource = strings.Join(ac.AWSSharedCredentialsFile(profile), "") - return &util.SpecCMD{ - Args: []string{"config", "init"}, - Command: exRMK, - Dir: c.WorkDir, - Ctx: c.Ctx.Context, - Debug: true, + if util.IsExists(ac.ConfigSource, true) { + if err := ac.ReadAWSConfigProfile(); err != nil { + return err + } } -} -func (c *ConfigCommands) checkAwsEnv() (map[string]string, bool) { - awsEnvs := map[string]string{ - "region": "AWS_REGION", - "aws_access_key_id": "AWS_ACCESS_KEY_ID", - "aws_secret_access_key": "AWS_SECRET_ACCESS_KEY", - "aws_session_token": "AWS_SESSION_TOKEN", + if c.Ctx.IsSet("aws-access-key-id") && c.Ctx.IsSet("aws-secret-access-key") { + ac.AwsCredentialsProfile.AccessKeyID = c.Ctx.String("aws-access-key-id") + ac.AwsCredentialsProfile.SecretAccessKey = c.Ctx.String("aws-secret-access-key") + } else if c.Ctx.IsSet("aws-access-key-id") { + ac.AwsCredentialsProfile.AccessKeyID = c.Ctx.String("aws-access-key-id") + ac.AwsCredentialsProfile.SecretAccessKey = "" + } else if c.Ctx.IsSet("aws-secret-access-key") { + ac.AwsCredentialsProfile.AccessKeyID = "" + ac.AwsCredentialsProfile.SecretAccessKey = c.Ctx.String("aws-secret-access-key") } - for key, val := range awsEnvs { - value, ok := os.LookupEnv(val) - if !ok { - delete(awsEnvs, key) - } else { - awsEnvs[key] = value - } + if c.Ctx.IsSet("aws-region") { + ac.Region = c.Ctx.String("aws-region") } - if len(awsEnvs) > 0 { - return awsEnvs, true - } else { - return nil, false + if c.Ctx.IsSet("aws-session-token") { + ac.AwsCredentialsProfile.SessionToken = c.Ctx.String("aws-session-token") } -} -func (c *ConfigCommands) configAws() error { - if awsEnvs, ok := c.checkAwsEnv(); !ok { - c.SpecCMD = c.awsConfigure(c.Conf.Profile) - return releaseRunner(c).runCMD() + if err := ac.ValidateAWSCredentials(); err != nil { + return err } else { - for key, val := range awsEnvs { - c.SpecCMD = c.awsConfigure(c.Conf.Profile) - c.SpecCMD.Args = append(c.SpecCMD.Args, "set", key, val) - if err := releaseRunner(c).runCMD(); err != nil { - return err - } - } + c.Conf.AwsConfigure = ac + } - zap.S().Infof("AWS profile by name %s was created", c.Conf.Profile) - return nil + if err := ac.WriteAWSConfigProfile(); err != nil { + return err } + + return nil } func (c *ConfigCommands) configAwsMFA() error { var tokenExpiration time.Time currentTime := time.Now() regularProfile := c.Conf.Profile + regularProfileConfigSource := c.Conf.ConfigSource + regularProfileCredentialsSource := c.Conf.CredentialsSource if len(c.Conf.AWSMFATokenExpiration) > 0 { unixTime, err := strconv.ParseInt(c.Conf.AWSMFATokenExpiration, 10, 64) @@ -136,6 +110,41 @@ func (c *ConfigCommands) configAwsMFA() error { if len(c.Conf.MFADeviceSerialNumber) > 0 { zap.S().Infof("MFA device SerialNumber: %s", c.Conf.MFADeviceSerialNumber) + } else { + if len(c.Conf.AWSMFAProfile) > 0 && len(c.Conf.AWSMFATokenExpiration) > 0 { + ac := aws_provider.NewAwsConfigure(c.Ctx.Context, c.Conf.AWSMFAProfile) + ac.ConfigSource = strings.Join(ac.AWSSharedConfigFile(c.Conf.AWSMFAProfile), "") + ac.CredentialsSource = strings.Join(ac.AWSSharedCredentialsFile(c.Conf.AWSMFAProfile), "") + if util.IsExists(ac.ConfigSource, true) { + if err := ac.ReadAWSConfigProfile(); err != nil { + return err + } + } + + ac.Profile = strings.TrimSuffix(regularProfile, "-mfa") + ac.ConfigSource = strings.TrimSuffix(regularProfileConfigSource, "-mfa") + ac.CredentialsSource = strings.TrimSuffix(regularProfileCredentialsSource, "-mfa") + + if err := ac.WriteAWSConfigProfile(); err != nil { + return err + } + + if err := os.RemoveAll(strings.Join(c.Conf.AWSSharedConfigFile(c.Conf.AWSMFAProfile), "")); err != nil { + return err + } + + if err := os.RemoveAll(strings.Join(c.Conf.AWSSharedCredentialsFile(c.Conf.AWSMFAProfile), "")); err != nil { + return err + } + + c.Conf.AWSMFAProfile = "" + c.Conf.AWSMFATokenExpiration = "" + c.Conf.AwsConfigure.Profile = ac.Profile + c.Conf.AwsConfigure.ConfigSource = ac.ConfigSource + c.Conf.AwsConfigure.CredentialsSource = ac.CredentialsSource + + return nil + } } if currentTime.Before(tokenExpiration) { @@ -150,57 +159,34 @@ func (c *ConfigCommands) configAwsMFA() error { c.Conf.AWSMFAProfile = regularProfile + "-mfa" c.Conf.AWSMFATokenExpiration = strconv.FormatInt(c.Conf.Expiration.Unix(), 10) - MFAProfileArgs := map[string]string{ - "aws_access_key_id": c.Conf.MFAProfileCredentials.AccessKeyID, - "aws_secret_access_key": c.Conf.MFAProfileCredentials.SecretAccessKey, - "output": "text", - "region": c.Conf.Region, - } - - regularProfileArgs := map[string]string{ - "aws_access_key_id": c.Conf.MFAToken.AccessKeyId, - "aws_secret_access_key": c.Conf.MFAToken.SecretAccessKey, - "aws_session_token": c.Conf.MFAToken.SessionToken, - } + acMFA := aws_provider.NewAwsConfigure(c.Ctx.Context, regularProfile+"-mfa") + acMFA.AwsCredentialsProfile.AccessKeyID = c.Conf.MFAProfileCredentials.AccessKeyID + acMFA.AwsCredentialsProfile.SecretAccessKey = c.Conf.MFAProfileCredentials.SecretAccessKey + acMFA.ConfigSource = regularProfileConfigSource + "-mfa" + acMFA.CredentialsSource = regularProfileCredentialsSource + "-mfa" + acMFA.Region = c.Conf.Region - for key, val := range MFAProfileArgs { - c.SpecCMD = c.awsConfigure(c.Conf.AWSMFAProfile) - c.SpecCMD.Args = append(c.SpecCMD.Args, "set", key, val) - if err := releaseRunner(c).runCMD(); err != nil { - return err - } + if err := acMFA.WriteAWSConfigProfile(); err != nil { + return err } - for key, val := range regularProfileArgs { - c.SpecCMD = c.awsConfigure(regularProfile) - c.SpecCMD.Args = append(c.SpecCMD.Args, "set", key, val) - if err := releaseRunner(c).runCMD(); err != nil { - return err - } - } - } + acRegular := aws_provider.NewAwsConfigure(c.Ctx.Context, regularProfile) + acRegular.AwsCredentialsProfile.AccessKeyID = c.Conf.MFAToken.AccessKeyID + acRegular.AwsCredentialsProfile.SecretAccessKey = c.Conf.MFAToken.SecretAccessKey + acRegular.AwsCredentialsProfile.SessionToken = c.Conf.MFAToken.SessionToken + acRegular.ConfigSource = regularProfileConfigSource + acRegular.CredentialsSource = regularProfileCredentialsSource + acRegular.Region = c.Conf.Region - c.Conf.AwsConfigure.Profile = regularProfile - - return nil -} - -func (c *ConfigCommands) copyAWSProfile(profile string) error { - profileArgs := map[string]string{ - "aws_access_key_id": c.Conf.MFAProfileCredentials.AccessKeyID, - "aws_secret_access_key": c.Conf.MFAProfileCredentials.SecretAccessKey, - "output": "text", - "region": c.Conf.Region, - } - - for key, val := range profileArgs { - c.SpecCMD = c.awsConfigure(profile) - c.SpecCMD.Args = append(c.SpecCMD.Args, "set", key, val) - if err := releaseRunner(c).runCMD(); err != nil { + if err := acRegular.WriteAWSConfigProfile(); err != nil { return err } } + c.Conf.AwsConfigure.Profile = strings.TrimSuffix(regularProfile, "-mfa") + c.Conf.AwsConfigure.ConfigSource = strings.TrimSuffix(regularProfileConfigSource, "-mfa") + c.Conf.AwsConfigure.CredentialsSource = strings.TrimSuffix(regularProfileCredentialsSource, "-mfa") + return nil } @@ -280,14 +266,13 @@ func (c *ConfigCommands) configHelmPlugins() error { return nil } -func (c *ConfigCommands) rmkConfig() error { - c.SpecCMD = c.rmkConfigInit() - return releaseRunner(c).runCMD() -} - func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.GitSpec) error { var profile string + conf.AwsConfigure = aws_provider.NewAwsConfigure(c.Context, gitSpec.ID) + conf.AWSMFAProfile = c.String("aws-mfa-profile") + conf.AWSMFATokenExpiration = c.String("aws-mfa-token-expiration") + // Detect if MFA is enabled if len(conf.AWSMFAProfile) > 0 && len(conf.AWSMFATokenExpiration) > 0 { profile = conf.AWSMFAProfile @@ -295,170 +280,67 @@ func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi profile = conf.Profile } - if c.Bool("reconfigure") { - if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(conf.Profile), "")); err != nil { - return err - } - - // Reconfigure regular AWS profile - if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAws(); err != nil { - return err - } - - // Get CallerIdentity and region for regular AWS profile - if _, err := conf.AwsConfigure.GetAwsConfigure(conf.Profile); err != nil { - return err - } - - // Delete MFA profile - if strings.Contains(profile, "-mfa") { - if err := os.RemoveAll(strings.Join(conf.AWSSharedConfigFile(profile), "")); err != nil { - return err - } - - if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(profile), "")); err != nil { - return err - } - } - - // Reset ConfigFrom value for config for current environment - conf.ConfigNameFrom = gitSpec.ID - // Reset AWSMFAProfile value for config for current environment - conf.AWSMFAProfile = "" - // Reset AWSMFATokenExpiration value for config for current environment - conf.AWSMFATokenExpiration = "" - // Returning a regular profile value - profile = conf.Profile - - // Create new MFA profile - if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAwsMFA(); err != nil { - return err - } + if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAws(profile); err != nil { + return err } if ok, err := conf.AwsConfigure.GetAwsConfigure(profile); err != nil && ok { zap.S().Warnf("%s", err.Error()) - if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAws(); err != nil { - return err - } - - if _, err := conf.AwsConfigure.GetAwsConfigure(profile); err != nil { - return err - } - - if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAwsMFA(); err != nil { - return err - } - } else if !c.Bool("reconfigure") { - if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAwsMFA(); err != nil { - return err - } } else if !ok && err != nil { return err } + if err := newConfigCommands(conf, c, util.GetPwdPath("")).configAwsMFA(); err != nil { + return err + } + return nil } -func getConfigFromEnvironment(c *cli.Context, conf *config.Config, gitSpec *git_handler.GitSpec) error { - // TODO: A possible solution is to check the current cluster provider with from and prohibit such inheritance - // currentClusterProvider := c.String("cluster-provider") +func initAzureProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.GitSpec) error { + ac := azure_provider.NewAzureConfigure() + asp := azure_provider.NewRawSP() - if len(c.String("config-from")) > 0 { - configPath := util.GetHomePath(util.RMKDir, util.RMKConfig, c.String("config-from")+".yaml") - - if err := conf.ReadConfigFile(configPath); err != nil { - zap.S().Errorf("RMK config %s.yaml not initialized, please check RMK configs of exists "+ - "via command 'rmk config list' and run command 'rmk config init' with specific parameters", - c.String("config-from")) + if util.IsExists( + util.GetHomePath(azure_provider.AzureHomeDir, azure_provider.AzurePrefix+gitSpec.ID+".json"), true) { + if err := ac.ReadSPCredentials(gitSpec.ID); err != nil { return err } + } - if c.String("cluster-provider") == aws_provider.AWSClusterProvider { - if err := c.Set("config-name-from", conf.Name); err != nil { - return err - } - - // Delete regular profile - if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(gitSpec.ID), "")); err != nil { - return err - } - - if len(conf.AWSMFAProfile) > 0 && len(conf.AWSMFATokenExpiration) > 0 { - regularProfile := conf.Profile - - // Get MFA profile credentials. - conf.AwsConfigure.Profile = conf.AWSMFAProfile - if err := conf.GetAWSCredentials(); err != nil { - return err - } - - // Copy MFA profile for current environment - conf.AwsConfigure.Profile = regularProfile - if err := newConfigCommands(conf, c, util.GetPwdPath("")).copyAWSProfile(gitSpec.ID); err != nil { - return err - } - } else { - // Delete config MFA profile - if err := os.RemoveAll(strings.Join(conf.AWSSharedConfigFile(gitSpec.ID+"-mfa"), "")); err != nil { - return err - } - - // Delete credentials MFA profile - if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(gitSpec.ID+"-mfa"), "")); err != nil { - return err - } - - // Get regular profile credentials. - if err := conf.GetAWSCredentials(); err != nil { - return err - } - - // Copy regular profile for current environment - if err := newConfigCommands(conf, c, util.GetPwdPath("")).copyAWSProfile(gitSpec.ID); err != nil { - return err - } - } - - // Reset AWSMFAProfile value for config for current environment - if err := c.Set("aws-mfa-profile", ""); err != nil { - return err - } - - // Reset AWSMFATokenExpiration value for config for current environment - if err := c.Set("aws-mfa-token-expiration", ""); err != nil { - return err - } - - conf.AwsConfigure.Profile = gitSpec.ID + if c.Bool("azure-service-principle") { + if err := json.NewDecoder(os.Stdin).Decode(&asp); err != nil { + return fmt.Errorf("unable to deserialize JSON from STDIN: %s", err.Error()) } - conf.ConfigNameFrom = c.String("config-name-from") + ac.MergeAzureRawSP(asp) + } - return nil + if c.IsSet("azure-client-id") { + ac.ClientID = c.String("azure-client-id") } - //TODO: deprecate after full list CAPI providers will be implemented - if err := util.ValidateGitHubToken(c, "required parameter --github-token not set"); err != nil { - return err + if c.IsSet("azure-client-secret") { + ac.ClientSecret = c.String("azure-client-secret") } - if err := util.ValidateNArg(c, 0); err != nil { - return err + if c.IsSet("azure-subscription-id") { + ac.SubscriptionID = c.String("azure-subscription-id") } - if !c.IsSet("config-name-from") { - if err := c.Set("config-name-from", gitSpec.ID); err != nil { - return err - } + if c.IsSet("azure-tenant-id") { + ac.TenantID = c.String("azure-tenant-id") } - if c.String("cluster-provider") == aws_provider.AWSClusterProvider { - conf.AwsConfigure = new(aws_provider.AwsConfigure) + if err := ac.ValidateSPCredentials(); err != nil { + return err + } else { + conf.AzureConfigure = ac } - conf.ConfigNameFrom = c.String("config-name-from") - conf.GitHubToken = c.String("github-token") + if err := ac.WriteSPCredentials(gitSpec.ID); err != nil { + return err + } return nil } @@ -470,11 +352,6 @@ func configDeleteAction(conf *config.Config) cli.ActionFunc { } switch { - case c.String("cluster-provider") == azure_provider.AzureClusterProvider: - if err := os.RemoveAll(util.GetHomePath(azure_provider.AzureHomeDir, - azure_provider.AzurePrefix+conf.Name+".json")); err != nil { - return err - } case c.String("cluster-provider") == aws_provider.AWSClusterProvider: // Delete MFA profile if len(conf.AWSMFAProfile) > 0 && len(conf.AWSMFATokenExpiration) > 0 { @@ -487,15 +364,20 @@ func configDeleteAction(conf *config.Config) cli.ActionFunc { } } - // Delete config MFA profile + // Delete config regular profile if err := os.RemoveAll(strings.Join(conf.AWSSharedConfigFile(conf.Profile), "")); err != nil { return err } - // Delete credentials MFA profile + // Delete credentials regular profile if err := os.RemoveAll(strings.Join(conf.AWSSharedCredentialsFile(conf.Profile), "")); err != nil { return err } + case c.String("cluster-provider") == azure_provider.AzureClusterProvider: + if err := os.RemoveAll(util.GetHomePath(azure_provider.AzureHomeDir, + azure_provider.AzurePrefix+conf.Name+".json")); err != nil { + return err + } } if err := os.RemoveAll(c.String("config")); err != nil { @@ -510,10 +392,6 @@ func configDeleteAction(conf *config.Config) cli.ActionFunc { func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { - if err := getConfigFromEnvironment(c, conf, gitSpec); err != nil { - return err - } - zap.S().Infof("loaded config file by path: %s", c.String("config")) start := time.Now() @@ -523,6 +401,7 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act conf.Environment = gitSpec.DefaultBranch conf.ClusterProvider = c.String("cluster-provider") conf.ProgressBar = c.Bool("progress-bar") + conf.GitHubToken = c.String("github-token") zap.S().Infof("RMK will use values for %s environment", conf.Environment) if c.Bool("slack-notifications") { @@ -538,73 +417,23 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act } switch conf.ClusterProvider { - case azure_provider.AzureClusterProvider: - conf.AwsConfigure = &aws_provider.AwsConfigure{} - ac := azure_provider.NewAzureConfigure() - asp := azure_provider.NewRawSP() - - if util.IsExists( - util.GetHomePath(azure_provider.AzureHomeDir, azure_provider.AzurePrefix+gitSpec.ID+".json"), true) { - if err := ac.ReadSPCredentials(gitSpec.ID); err != nil { - return err - } - } - - if c.Bool("azure-service-principle") { - if err := json.NewDecoder(os.Stdin).Decode(&asp); err != nil { - return fmt.Errorf("unable to deserialize json from stdin: %s", err.Error()) - } - - ac.MergeAzureRawSP(asp) - } - - if c.IsSet("azure-client-id") { - ac.ClientID = c.String("azure-client-id") - } - - if c.IsSet("azure-client-secret") { - ac.ClientSecret = c.String("azure-client-secret") - } - - if c.IsSet("azure-subscription-id") { - ac.SubscriptionID = c.String("azure-subscription-id") - } - - if c.IsSet("azure-tenant-id") { - ac.TenantID = c.String("azure-tenant-id") - } - - if err := ac.CheckSPCredentials(); err != nil { + case aws_provider.AWSClusterProvider: + conf.AzureConfigure = nil + if err := initAWSProfile(c, conf, gitSpec); err != nil { return err - } else { - conf.AzureConfigure = azure_provider.NewAzureConfigure() - conf.AzureConfigure.SubscriptionID = ac.SubscriptionID } - if err := ac.WriteSPCredentials(gitSpec.ID); err != nil { + conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+aws_provider.AWSClusterProvider) + case azure_provider.AzureClusterProvider: + conf.AwsConfigure = nil + if err := initAzureProfile(c, conf, gitSpec); err != nil { return err } conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+azure_provider.AzureClusterProvider) - case aws_provider.AWSClusterProvider: - conf.AzureConfigure = &azure_provider.AzureConfigure{} - conf.AwsConfigure.Profile = gitSpec.ID - conf.AWSMFAProfile = c.String("aws-mfa-profile") - conf.AWSMFATokenExpiration = c.String("aws-mfa-token-expiration") - - // AWS Profile init configuration with support MFA - if err := initAWSProfile(c, conf, gitSpec); err != nil { - return err - } - - //Formation of a unique bucket name, consisting of the prefix tenant of the repository, - //constant and the first 3 and last 2 numbers AWS account id - awsUID := conf.AccountID[0:3] + conf.AccountID[len(conf.AccountID)-2:] - conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+awsUID) - conf.SopsBucketName = conf.Tenant + "-" + util.SopsRootName + "-" + awsUID case util.LocalClusterProvider: - conf.AwsConfigure = &aws_provider.AwsConfigure{} - conf.AzureConfigure = &azure_provider.AzureConfigure{} + conf.AwsConfigure = nil + conf.AzureConfigure = nil conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+util.LocalClusterProvider) } @@ -616,25 +445,6 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act return err } - if conf.ClusterProvider == aws_provider.AWSClusterProvider { - //create s3 bucket for sops age keys - if err := conf.CreateBucket(conf.SopsBucketName); err != nil { - return err - } - - if err := conf.DownloadFromBucket("", conf.SopsBucketName, conf.SopsAgeKeys, conf.Tenant); err != nil { - return err - } - - if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { - return err - } - - zap.S().Infof("time spent on initialization: %.fs", time.Since(start).Seconds()) - - return nil - } - if err := resolveDependencies(conf.InitConfig(), c, false); err != nil { return err } diff --git a/cmd/flags.go b/cmd/flags.go index fad404e..5acaf51 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -3,11 +3,40 @@ package cmd import ( "github.com/urfave/cli/v2" "github.com/urfave/cli/v2/altsrc" + "rmk/util" ) func flagsConfig() []cli.Flag { return []cli.Flag{ + &cli.StringFlag{ + Category: awsFlagsCategory, + Name: "aws-access-key-id", + Usage: "AWS access key ID for IAM user", + Aliases: []string{"awid"}, + EnvVars: []string{"RMK_AWS_ACCESS_KEY_ID", "AWS_ACCESS_KEY_ID"}, + }, + &cli.StringFlag{ + Category: awsFlagsCategory, + Name: "aws-region", + Usage: "AWS region for current AWS account", + Aliases: []string{"awr"}, + EnvVars: []string{"RMK_AWS_REGION", "AWS_REGION", "AWS_DEFAULT_REGION"}, + }, + &cli.StringFlag{ + Category: awsFlagsCategory, + Name: "aws-secret-access-key", + Usage: "AWS secret access key for IAM user", + Aliases: []string{"awsk"}, + EnvVars: []string{"RMK_AWS_SECRET_ACCESS_KEY", "AWS_SECRET_ACCESS_KEY"}, + }, + &cli.StringFlag{ + Category: awsFlagsCategory, + Name: "aws-session-token", + Usage: "AWS session token for IAM user", + Aliases: []string{"awst"}, + EnvVars: []string{"RMK_AWS_SESSION_TOKEN", "AWS_SESSION_TOKEN"}, + }, altsrc.NewStringFlag( &cli.StringFlag{ Name: "aws-mfa-profile", @@ -24,7 +53,7 @@ func flagsConfig() []cli.Flag { Category: azureFlagsCategory, Name: "azure-client-id", Usage: "Azure client ID for Service Principal", - Aliases: []string{"azc"}, + Aliases: []string{"azid"}, EnvVars: []string{"RMK_AZURE_CLIENT_ID", "AZURE_CLIENT_ID"}, }, &cli.StringFlag{ @@ -37,13 +66,13 @@ func flagsConfig() []cli.Flag { &cli.BoolFlag{ Category: azureFlagsCategory, Name: "azure-service-principle", - Usage: "Azure Service Principal stdin content", + Usage: "Azure service principal STDIN content", Aliases: []string{"azsp"}, }, &cli.StringFlag{ Category: azureFlagsCategory, Name: "azure-subscription-id", - Usage: "Azure subscription ID for current Azure platform domain", + Usage: "Azure subscription ID for current platform domain", Aliases: []string{"azs"}, EnvVars: []string{"RMK_AZURE_SUBSCRIPTION_ID", "AZURE_SUBSCRIPTION_ID"}, }, @@ -54,31 +83,14 @@ func flagsConfig() []cli.Flag { Aliases: []string{"azt"}, EnvVars: []string{"RMK_AZURE_TENANT_ID", "AZURE_TENANT_ID"}, }, - &cli.BoolFlag{ - Name: "reconfigure", - Usage: "force Cluster Providers credentials recreate", - Aliases: []string{"r"}, - }, &cli.StringFlag{ Name: "config", Hidden: true, }, - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "config-name-from", - Hidden: true, - }, - ), - &cli.StringFlag{ - Name: "config-from", - Usage: "inheritance of RMK config credentials from another RMK config", - Aliases: []string{"cf"}, - EnvVars: []string{"RMK_CONFIG_FROM"}, - }, altsrc.NewStringFlag( &cli.StringFlag{ Name: "github-token", - Usage: "personal access token for download GitHub artifacts", + Usage: "GitHub personal access token, required when using private repositories", Aliases: []string{"ght"}, EnvVars: []string{"RMK_GITHUB_TOKEN"}, }, @@ -102,33 +114,37 @@ func flagsConfig() []cli.Flag { ), altsrc.NewBoolFlag( &cli.BoolFlag{ - Name: "slack-notifications", - Usage: "enable Slack notifications", - Aliases: []string{"n"}, + Category: "Slack notifications", + Name: "slack-notifications", + Usage: "enable Slack notifications", + Aliases: []string{"n"}, }, ), altsrc.NewStringFlag( &cli.StringFlag{ - Name: "slack-webhook", - Usage: "URL for Slack webhook", - Aliases: []string{"sw"}, - EnvVars: []string{"RMK_SLACK_WEBHOOK"}, + Category: "Slack notifications", + Name: "slack-webhook", + Usage: "URL for Slack webhook", + Aliases: []string{"sw"}, + EnvVars: []string{"RMK_SLACK_WEBHOOK"}, }, ), altsrc.NewStringFlag( &cli.StringFlag{ - Name: "slack-channel", - Usage: "channel name for Slack notification", - Aliases: []string{"sc"}, - EnvVars: []string{"RMK_SLACK_CHANNEL"}, + Category: "Slack notifications", + Name: "slack-channel", + Usage: "channel name for Slack notifications", + Aliases: []string{"sc"}, + EnvVars: []string{"RMK_SLACK_CHANNEL"}, }, ), altsrc.NewStringSliceFlag( &cli.StringSliceFlag{ - Name: "slack-message-details", - Usage: "additional information for body of Slack message", - Aliases: []string{"smd"}, - EnvVars: []string{"RMK_SLACK_MESSAGE_DETAILS"}, + Category: "Slack notifications", + Name: "slack-message-details", + Usage: "additional information for body of Slack messages", + Aliases: []string{"smd"}, + EnvVars: []string{"RMK_SLACK_MESSAGE_DETAILS"}, }, ), } diff --git a/cmd/k3d.go b/cmd/k3d.go index f210740..092c60c 100644 --- a/cmd/k3d.go +++ b/cmd/k3d.go @@ -125,7 +125,7 @@ func (k *K3DCommands) listK3DClusters() error { k.SpecCMD.DisableStdOut = true if err := releaseRunner(k).runCMD(); err != nil { if strings.Contains(k.SpecCMD.StderrBuf.String(), "No nodes found for given cluster") { - return fmt.Errorf("cluster %s not running", util.CAPI) + return fmt.Errorf("%s management cluster not found", strings.ToUpper(util.CAPI)) } else { return fmt.Errorf("%s", k.SpecCMD.StderrBuf.String()) } @@ -159,11 +159,6 @@ func (k *K3DCommands) startStopK3DCluster() error { func K3DCreateAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - //TODO: deprecate after full list CAPI providers will be implemented - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -178,11 +173,6 @@ func K3DCreateAction(conf *config.Config) cli.ActionFunc { func K3DAction(conf *config.Config, action func(k3dRunner K3DRunner) error) cli.ActionFunc { return func(c *cli.Context) error { - //TODO: deprecate after full list CAPI providers will be implemented - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - if err := util.ValidateNArg(c, 0); err != nil { return err } diff --git a/cmd/project.go b/cmd/project.go index f6bc371..a371cf8 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -446,11 +446,6 @@ func (p *ProjectCommands) generateProject(gitSpec *git_handler.GitSpec) error { func projectGenerateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { - //TODO: deprecate after full list CAPI providers will be implemented - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -461,11 +456,6 @@ func projectGenerateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cl func projectUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { - //TODO: deprecate after full list CAPI providers will be implemented - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - if err := util.ValidateNArg(c, 0); err != nil { return err } diff --git a/cmd/release.go b/cmd/release.go index 3cd91a1..54f6cb6 100644 --- a/cmd/release.go +++ b/cmd/release.go @@ -20,6 +20,8 @@ import ( "rmk/config" "rmk/git_handler" "rmk/notification" + "rmk/providers/aws_provider" + "rmk/providers/azure_provider" "rmk/util" ) @@ -87,31 +89,33 @@ type HelmStatus struct { } func (rc *ReleaseCommands) runCMD() error { - if err := rc.SpecCMD.AddEnv(); err != nil { + if err := rc.SpecCMD.AddOSEnv(); err != nil { return err } if err := rc.SpecCMD.ExecCMD(); err != nil { - if rc.SpecCMD.Debug { - zap.S().Debugf("command: %s", rc.SpecCMD.CommandStr) - zap.S().Debugf("path: %s", rc.SpecCMD.Dir) - for _, val := range rc.SpecCMD.Envs { - zap.S().Debugf("env: %s", strings.ReplaceAll(val, rc.Conf.GitHubToken, "[rmk_sensitive]")) - } - } + rc.debugLevel() return err } + rc.debugLevel() + + return nil +} + +func (rc *ReleaseCommands) debugLevel() { if rc.SpecCMD.Debug { zap.S().Debugf("command: %s", rc.SpecCMD.CommandStr) zap.S().Debugf("path: %s", rc.SpecCMD.Dir) for _, val := range rc.SpecCMD.Envs { - zap.S().Debugf("env: %s", strings.ReplaceAll(val, rc.Conf.GitHubToken, "[rmk_sensitive]")) + if len(rc.Conf.GitHubToken) > 0 { + zap.S().Debugf("env: %s", strings.ReplaceAll(val, rc.Conf.GitHubToken, "[rmk_sensitive]")) + } else { + zap.S().Debugf("env: %s", val) + } } } - - return nil } func (rc *ReleaseCommands) nestedHelmfiles(envs ...string) []string { @@ -129,20 +133,38 @@ func (rc *ReleaseCommands) nestedHelmfiles(envs ...string) []string { } func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { + var sensKeyWords []string + defaultArgs := []string{"--environment", rc.Conf.Environment} + // generating common environment variables envs := append([]string{}, "NAME="+rc.Conf.Name, - "TENANT="+rc.Conf.Tenant, + "ROOT_DOMAIN="+rc.Conf.RootDomain, "SOPS_AGE_KEY_FILE="+filepath.Join(rc.Conf.SopsAgeKeys, util.SopsAgeKeyFile), - "GITHUB_TOKEN="+rc.Conf.GitHubToken, - "AWS_PROFILE="+rc.Conf.Profile, - "AWS_CONFIG_FILE="+strings.Join(rc.Conf.AWSSharedConfigFile(rc.Conf.Profile), ""), - "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(rc.Conf.AWSSharedCredentialsFile(rc.Conf.Profile), ""), - "AZURE_SUBSCRIPTION_ID="+rc.Conf.AzureConfigure.SubscriptionID, + "TENANT="+rc.Conf.Tenant, ) - envs = append(envs, "ROOT_DOMAIN="+rc.Conf.RootDomain) + if len(rc.Conf.GitHubToken) > 0 { + sensKeyWords = []string{rc.Conf.GitHubToken} + envs = append(envs, "GITHUB_TOKEN="+rc.Conf.GitHubToken) + } else { + sensKeyWords = []string{} + } + + // generating additional environment variables to specific cluster provider + switch rc.Conf.ClusterProvider { + case aws_provider.AWSClusterProvider: + envs = append(envs, + "AWS_PROFILE="+rc.Conf.Profile, + "AWS_CONFIG_FILE="+strings.Join(rc.Conf.AWSSharedConfigFile(rc.Conf.Profile), ""), + "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(rc.Conf.AWSSharedCredentialsFile(rc.Conf.Profile), ""), + ) + case azure_provider.AzureClusterProvider: + envs = append(envs, + "AZURE_SUBSCRIPTION_ID="+rc.Conf.AzureConfigure.SubscriptionID, + ) + } for _, val := range rc.Conf.HooksMapping { keyTenantEnv := regexp.MustCompile(`[\-.]`).ReplaceAllString(val.Tenant, "_") @@ -170,7 +192,7 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { Dir: rc.WorkDir, Envs: envs, Debug: true, - SensKeyWords: []string{rc.Conf.GitHubToken}, + SensKeyWords: sensKeyWords, } } @@ -503,11 +525,6 @@ func (sr *SpecRelease) checkStatusRelease() error { func releaseHelmfileAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - //TODO: deprecate after full list CAPI providers will be implemented - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -557,11 +574,6 @@ func releaseHelmfileAction(conf *config.Config) cli.ActionFunc { func releaseRollbackAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - //TODO: deprecate after full list CAPI providers will be implemented - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -595,11 +607,6 @@ func releaseRollbackAction(conf *config.Config) cli.ActionFunc { func releaseUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.ActionFunc { return func(c *cli.Context) error { - //TODO: deprecate after full list CAPI providers will be implemented - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - if err := util.ValidateNArg(c, 0); err != nil { return err } diff --git a/cmd/secret.go b/cmd/secret.go index 9edd670..f2947fd 100644 --- a/cmd/secret.go +++ b/cmd/secret.go @@ -379,11 +379,6 @@ func (sc *SecretCommands) runHelmSecretsCMD(secretFilePath string, returnCMDErro func secretMgrEncryptDecryptAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - //TODO: deprecate after full list CAPI providers will be implemented - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -407,11 +402,6 @@ func secretMgrGenerateAction(conf *config.Config) cli.ActionFunc { } func secretKeysCreateAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { - //TODO: deprecate after full list CAPI providers will be implemented - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - if err := util.ValidateNArg(c, 0); err != nil { return err } @@ -446,11 +436,6 @@ func secretKeysUploadAction(conf *config.Config) cli.ActionFunc { func secretAction(conf *config.Config, action func(secretRunner SecretRunner) error) cli.ActionFunc { return func(c *cli.Context) error { - //TODO: deprecate after full list CAPI providers will be implemented - if err := util.ValidateGitHubToken(c, ""); err != nil { - return err - } - if err := util.ValidateNArg(c, 1); err != nil { return err } diff --git a/config/config.go b/config/config.go index 89c1e1d..c79b50e 100644 --- a/config/config.go +++ b/config/config.go @@ -26,7 +26,6 @@ type Config struct { Name string `yaml:"name,omitempty"` Tenant string `yaml:"tenant,omitempty"` Environment string `yaml:"environment,omitempty"` - ConfigNameFrom string `yaml:"config-name-from,omitempty"` RootDomain string `yaml:"root-domain,omitempty"` GitHubToken string `yaml:"github-token,omitempty"` ClusterProvider string `yaml:"cluster-provider"` diff --git a/go.mod b/go.mod index bd971bc..6a70fda 100644 --- a/go.mod +++ b/go.mod @@ -9,15 +9,13 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.1.0 github.com/Masterminds/semver v1.5.0 github.com/Masterminds/sprig/v3 v3.2.3 - github.com/aws/aws-sdk-go-v2 v1.24.1 + github.com/aws/aws-sdk-go-v2 v1.32.3 github.com/aws/aws-sdk-go-v2/config v1.26.3 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11 - github.com/aws/aws-sdk-go-v2/service/dynamodb v1.26.8 - github.com/aws/aws-sdk-go-v2/service/ecr v1.24.7 + github.com/aws/aws-sdk-go-v2/service/eks v1.51.1 github.com/aws/aws-sdk-go-v2/service/iam v1.28.7 github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 - github.com/aws/smithy-go v1.19.0 github.com/cheggaaa/pb v1.0.29 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-git/v5 v5.11.0 @@ -54,17 +52,17 @@ require ( github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.16.14 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.8.11 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect + github.com/aws/smithy-go v1.22.0 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect diff --git a/go.sum b/go.sum index 9701f9e..b047807 100644 --- a/go.sum +++ b/go.sum @@ -227,8 +227,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.44.122 h1:p6mw01WBaNpbdP2xrisz5tIkcNwzj/HysobNoaAHjgo= github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= -github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= +github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk= +github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo= github.com/aws/aws-sdk-go-v2/config v1.26.3 h1:dKuc2jdp10y13dEEvPqWxqLoc0vF3Z9FC45MvuQSxOA= @@ -239,26 +239,22 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tC github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11 h1:I6lAa3wBWfCz/cKkOpAcumsETRkFAl70sWi8ItcMEsM= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11/go.mod h1:be1NIO30kJA23ORBLqPo1LttEM6tPNSEcjkd1eKzNW0= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 h1:5oE2WzJE56/mVveuDZPJESKlg/00AaS2pY2QZcnxg4M= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10/go.mod h1:FHbKWQtRBYUz4vO5WBWjzMD2by126ny5y/1EoaWoLfI= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.26.8 h1:XKO0BswTDeZMLDBd/b5pCEZGttNXrzRUVtFvp2Ak/Vo= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.26.8/go.mod h1:N5tqZcYMM0N1PN7UQYJNWuGyO886OfnMhf/3MAbqMcI= -github.com/aws/aws-sdk-go-v2/service/ecr v1.24.7 h1:3iaT/LnGV6jNtbBkvHZDlzz7Ky3wMHDJAyFtGd5GUJI= -github.com/aws/aws-sdk-go-v2/service/ecr v1.24.7/go.mod h1:mtzCLxk6M+KZbkJdq3cUH9GCrudw8qCy5C3EHO+5vLc= +github.com/aws/aws-sdk-go-v2/service/eks v1.51.1 h1:OQjVHkANBbwE055NK49M/kelQbapsQOsSfUUWP1mi3w= +github.com/aws/aws-sdk-go-v2/service/eks v1.51.1/go.mod h1:9wMtzHTjYbK5MLzYBWSznUPsys/n9LapMwb6UhKOVPQ= github.com/aws/aws-sdk-go-v2/service/iam v1.28.7 h1:FKPRDYZOO0Eur19vWUL1B40Op0j89KQj3kARjrszMK8= github.com/aws/aws-sdk-go-v2/service/iam v1.28.7/go.mod h1:YzMYyQ7S4twfYzLjwP24G1RAxypozVZeNaG1r2jxRms= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10 h1:L0ai8WICYHozIKK+OtPzVJBugL7culcuM4E4JOpIEm8= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10/go.mod h1:byqfyxJBshFk0fF9YmK0M0ugIO8OWjzH2T3bPG4eGuA= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.8.11 h1:e9AVb17H4x5FTE5KWIP5M1Du+9M86pS+Hw0lBUdN8EY= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.8.11/go.mod h1:B90ZQJa36xo0ph9HsoteI1+r8owgQH/U1QNfqZQkj1Q= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 h1:KOxnQeWy5sXyS37fdKEvAsGHOr9fa/qvwxfJurR/BzE= @@ -271,8 +267,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 h1:Yf2MIo9x+0tyv76GljxzqA3W github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= -github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= -github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= +github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM= +github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= diff --git a/providers/aws_provider/aws.go b/providers/aws_provider/aws.go index 75a3f77..4d80426 100644 --- a/providers/aws_provider/aws.go +++ b/providers/aws_provider/aws.go @@ -10,54 +10,84 @@ import ( "net/http" "os" "path/filepath" + "reflect" "strconv" "strings" + "text/template" "time" "github.com/aws/aws-sdk-go-v2/aws" awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" - "github.com/aws/aws-sdk-go-v2/service/dynamodb" - ddbtype "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" - "github.com/aws/aws-sdk-go-v2/service/ecr" + "github.com/aws/aws-sdk-go-v2/service/eks" + eksType "github.com/aws/aws-sdk-go-v2/service/eks/types" "github.com/aws/aws-sdk-go-v2/service/iam" "github.com/aws/aws-sdk-go-v2/service/s3" s3type "github.com/aws/aws-sdk-go-v2/service/s3/types" "github.com/aws/aws-sdk-go-v2/service/sts" - "github.com/aws/smithy-go" "go.uber.org/zap" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/clientcmd/api" "rmk/util" ) const ( AWSClusterProvider = "aws" + + AWSConfigTemplateFile = `[profile {{ .Profile }}] +region = {{ .Region }} +output = {{ .Output }} +` + + AWSCredentialsTemplateFile = `[{{ .Profile }}] +aws_access_key_id = {{ .AwsCredentialsProfile.AccessKeyID }} +aws_secret_access_key = {{ .AwsCredentialsProfile.SecretAccessKey }} +{{- if .AwsCredentialsProfile.SessionToken }} +aws_session_token = {{ .AwsCredentialsProfile.SessionToken }} +{{- end }} +` ) type AwsConfigure struct { - Profile string `yaml:"profile,omitempty"` - Region string `yaml:"region,omitempty"` - AccountID string `yaml:"account_id,omitempty"` - UserName string `yaml:"user_name,omitempty"` - MFADeviceSerialNumber string `yaml:"mfa_device,omitempty"` *MFAToken `yaml:"-"` + AccountID string `yaml:"account_id,omitempty"` + AwsCredentialsProfile `yaml:"-"` + ConfigSource string `yaml:"config-source"` + CredentialsSource string `yaml:"credentials-source"` + Ctx context.Context `yaml:"-"` + IAMUserName string `yaml:"user-name,omitempty"` + MFADeviceSerialNumber string `yaml:"mfa-device,omitempty"` MFAProfileCredentials aws.Credentials `yaml:"-"` + Output string `yaml:"output,omitempty"` + Profile string `yaml:"profile,omitempty"` + Region string `json:"aws-region,omitempty" yaml:"region,omitempty"` } type MFAToken struct { - AccessKeyId string + AccessKeyID string Expiration time.Time SecretAccessKey string SessionToken string } +type AwsCredentialsProfile struct { + AccessKeyID string `json:"aws-access-key-id,omitempty" yaml:"-"` + SecretAccessKey string `json:"aws-secret-access-key,omitempty" yaml:"-"` + SessionToken string `yaml:"-"` +} + func (a *AwsConfigure) AWSSharedConfigFile(profile string) []string { - return []string{util.GetHomePath(".aws", "config_"+profile)} + return []string{config.DefaultSharedConfigFilename() + "_" + profile} } func (a *AwsConfigure) AWSSharedCredentialsFile(profile string) []string { - return []string{util.GetHomePath(".aws", "credentials_"+profile)} + return []string{config.DefaultSharedCredentialsFilename() + "_" + profile} +} + +func NewAwsConfigure(ctx context.Context, profile string) *AwsConfigure { + return &AwsConfigure{Ctx: ctx, Output: "text", Profile: profile} } func (a *AwsConfigure) errorProxy(cfg aws.Config, err error) (aws.Config, error) { @@ -82,31 +112,128 @@ func (a *AwsConfigure) configOptions() []func(options *config.LoadOptions) error } } +func getTagStructName(i interface{}, name string) error { + if field, ok := reflect.TypeOf(i).Elem().FieldByName(name); ok { + return fmt.Errorf("profile option %s required", strings.TrimSuffix(field.Tag.Get("json"), ",omitempty")) + } else { + return fmt.Errorf("field with name %s not defined", name) + } +} + +// ValidateAWSCredentials will validate the required parameters for AWS authentication +func (a *AwsConfigure) ValidateAWSCredentials() error { + if len(a.AwsCredentialsProfile.AccessKeyID) == 0 { + return getTagStructName(&a.AwsCredentialsProfile, "AccessKeyID") + } + + if len(a.AwsCredentialsProfile.SecretAccessKey) == 0 { + return getTagStructName(&a.AwsCredentialsProfile, "SecretAccessKey") + } + + if len(a.Region) == 0 { + return getTagStructName(a, "Region") + } + + return nil +} + +// RenderAWSConfigProfile will render the AWS profile. +func (a *AwsConfigure) RenderAWSConfigProfile(temp string) ([]byte, error) { + tmpl, err := template.New("AWS config Profile").Parse(temp) + if err != nil { + return nil, err + } + + var credsFileStr bytes.Buffer + err = tmpl.Execute(&credsFileStr, a) + if err != nil { + return nil, err + } + + return credsFileStr.Bytes(), nil +} + +// RenderBase64EncodedAWSConfigProfile will render the AWS profile, encoded in base 64. +func (a *AwsConfigure) RenderBase64EncodedAWSConfigProfile(temp string) (string, error) { + configProfile, err := a.RenderAWSConfigProfile(temp) + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString(configProfile), nil +} + +func (a *AwsConfigure) ReadAWSConfigProfile() error { + cfg, err := config.LoadDefaultConfig(a.Ctx, a.configOptions()...) + if err != nil { + return err + } + + for _, val := range cfg.ConfigSources { + switch result := val.(type) { + case config.SharedConfig: + a.AwsCredentialsProfile.AccessKeyID = result.Credentials.AccessKeyID + a.AwsCredentialsProfile.SecretAccessKey = result.Credentials.SecretAccessKey + a.AwsCredentialsProfile.SessionToken = result.Credentials.SessionToken + a.Region = result.Region + } + } + + return nil +} + +func (a *AwsConfigure) WriteAWSConfigProfile() error { + var ( + err error + sharedFiles = make(map[string][]byte) + ) + + sharedFiles[a.ConfigSource], err = a.RenderAWSConfigProfile(AWSConfigTemplateFile) + if err != nil { + return err + } + + sharedFiles[a.CredentialsSource], err = a.RenderAWSConfigProfile(AWSCredentialsTemplateFile) + if err != nil { + return err + } + + for key, val := range sharedFiles { + if err := os.MkdirAll(filepath.Dir(key), 0755); err != nil { + return err + } + + if err := os.WriteFile(key, val, 0644); err != nil { + return err + } + } + + return nil +} + func (a *AwsConfigure) GetUserName() error { - ctx := context.TODO() - cfg, err := a.errorProxy(config.LoadDefaultConfig(ctx, a.configOptions()...)) + cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) if err != nil { return err } - user, err := iam.NewFromConfig(cfg).GetUser(ctx, &iam.GetUserInput{}) + user, err := iam.NewFromConfig(cfg).GetUser(a.Ctx, &iam.GetUserInput{}) if err != nil { return err } - a.UserName = aws.ToString(user.User.UserName) + a.IAMUserName = aws.ToString(user.User.UserName) return nil } -func (a *AwsConfigure) GetAWSCredentials() error { - ctx := context.TODO() - cfg, err := a.errorProxy(config.LoadDefaultConfig(ctx, a.configOptions()...)) +func (a *AwsConfigure) GetMFACredentials() error { + cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) if err != nil { return err } - if a.MFAProfileCredentials, err = cfg.Credentials.Retrieve(ctx); err != nil { + if a.MFAProfileCredentials, err = cfg.Credentials.Retrieve(a.Ctx); err != nil { return err } @@ -120,18 +247,17 @@ func (a *AwsConfigure) GetMFADevicesSerialNumbers() error { return err } - if err := a.GetAWSCredentials(); err != nil { + if err := a.GetMFACredentials(); err != nil { return err } - ctx := context.TODO() - cfg, err := a.errorProxy(config.LoadDefaultConfig(ctx, a.configOptions()...)) + cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) if err != nil { return err } - mfaDevices, err := iam.NewFromConfig(cfg).ListMFADevices(ctx, - &iam.ListMFADevicesInput{UserName: aws.String(a.UserName)}) + mfaDevices, err := iam.NewFromConfig(cfg).ListMFADevices(a.Ctx, + &iam.ListMFADevicesInput{UserName: aws.String(a.IAMUserName)}) if err != nil { return err } @@ -155,8 +281,7 @@ func (a *AwsConfigure) GetMFADevicesSerialNumbers() error { } func (a *AwsConfigure) GetMFASessionToken() error { - ctx := context.TODO() - cfg, err := a.errorProxy(config.LoadDefaultConfig(ctx, a.configOptions()...)) + cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) if err != nil { return err } @@ -165,7 +290,7 @@ func (a *AwsConfigure) GetMFASessionToken() error { return err } - token, err := sts.NewFromConfig(cfg).GetSessionToken(ctx, &sts.GetSessionTokenInput{ + token, err := sts.NewFromConfig(cfg).GetSessionToken(a.Ctx, &sts.GetSessionTokenInput{ DurationSeconds: aws.Int32(43200), SerialNumber: aws.String(a.MFADeviceSerialNumber), TokenCode: aws.String(util.ReadStdin("TOTP")), @@ -175,7 +300,7 @@ func (a *AwsConfigure) GetMFASessionToken() error { } a.MFAToken = &MFAToken{ - AccessKeyId: aws.ToString(token.Credentials.AccessKeyId), + AccessKeyID: aws.ToString(token.Credentials.AccessKeyId), Expiration: aws.ToTime(token.Credentials.Expiration), SecretAccessKey: aws.ToString(token.Credentials.SecretAccessKey), SessionToken: aws.ToString(token.Credentials.SessionToken), @@ -185,8 +310,7 @@ func (a *AwsConfigure) GetMFASessionToken() error { } func (a *AwsConfigure) GetAwsConfigure(profile string) (bool, error) { - ctx := context.TODO() - cfg, err := a.errorProxy(config.LoadDefaultConfig(ctx, + cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, config.WithSharedConfigFiles(a.AWSSharedConfigFile(profile)), config.WithSharedCredentialsFiles(a.AWSSharedCredentialsFile(profile)), config.WithSharedConfigProfile(profile), @@ -196,7 +320,7 @@ func (a *AwsConfigure) GetAwsConfigure(profile string) (bool, error) { } client := sts.NewFromConfig(cfg) - identity, err := client.GetCallerIdentity(ctx, &sts.GetCallerIdentityInput{}) + identity, err := client.GetCallerIdentity(a.Ctx, &sts.GetCallerIdentityInput{}) if err != nil { return false, err } @@ -207,39 +331,99 @@ func (a *AwsConfigure) GetAwsConfigure(profile string) (bool, error) { return true, nil } -func (a *AwsConfigure) GetECRCredentials(region string) (map[string]string, error) { - ctx := context.TODO() - ecrCredentials := make(map[string]string) +func (a *AwsConfigure) GetAWSClusterContext(clusterName string) ([]byte, error) { + cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) + if err != nil { + return nil, err + } - cfg, err := a.errorProxy(config.LoadDefaultConfig(ctx, a.configOptions()...)) + client := eks.NewFromConfig(cfg) + cluster, err := client.DescribeCluster(a.Ctx, &eks.DescribeClusterInput{Name: aws.String(clusterName)}) if err != nil { return nil, err } - // needed for specific AWS account where ECR used - cfg.Region = region + return a.generateUserKubeconfig(cluster.Cluster) +} + +func (a *AwsConfigure) generateUserKubeconfig(cluster *eksType.Cluster) ([]byte, error) { + var execEnvVars []api.ExecEnvVar - svc := ecr.NewFromConfig(cfg) - token, err := svc.GetAuthorizationToken(ctx, &ecr.GetAuthorizationTokenInput{}) + clusterName := aws.ToString(cluster.Name) + userName := a.getKubeConfigUserName(clusterName) + + cfg, err := a.generateBaseKubeConfig(cluster) if err != nil { - return nil, err + return nil, fmt.Errorf("creating base kubeconfig: %w", err) + } + + execEnvVars = append(execEnvVars, + api.ExecEnvVar{Name: "AWS_PROFILE", Value: a.Profile}, + api.ExecEnvVar{Name: "AWS_CONFIG_FILE", Value: strings.Join(a.AWSSharedConfigFile(a.Profile), "")}, + api.ExecEnvVar{Name: "AWS_SHARED_CREDENTIALS_FILE", Value: strings.Join(a.AWSSharedCredentialsFile(a.Profile), "")}, + ) + + // Version v1alpha1 was removed in Kubernetes v1.23. + // Version v1 was released in Kubernetes v1.23. + // Version v1beta1 was selected as it has the widest range of support + // This should be changed to v1 once EKS no longer supports Kubernetes Date: Thu, 31 Oct 2024 15:31:59 +0100 Subject: [PATCH 17/39] #29 - refactoring --- cmd/cluster.go | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index c5630c6..3c190ff 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -272,22 +272,14 @@ func (cc *ClusterCommands) switchKubeContext() error { switch cc.Conf.ClusterProvider { case aws_provider.AWSClusterProvider: - //if err := cc.awsClusterContext(); err != nil { - // return err - //} - // - //_, currentContext, err := cc.getKubeContext() - //if err != nil { - // return err - //} - // - //cc.SpecCMD = cc.kubectl("config", "set-credentials", currentContext, - // "--exec-env", "AWS_CONFIG_FILE="+strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""), - // "--exec-env", "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""), - //) - //cc.SpecCMD.DisableStdOut = true - //cc.SpecCMD.Debug = true - //return releaseRunner(cc).runCMD() + clusterContext, err := cc.AWSClusterContext() + if err != nil { + return err + } + + if err := cc.mergeKubeConfig(clusterContext); err != nil { + return err + } case azure_provider.AzureClusterProvider: clusterContext, err := cc.azureClusterContext() if err != nil { From 8b518de32ae785be132755705f96dbf1e8742be0 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 6 Nov 2024 17:23:11 +0100 Subject: [PATCH 18/39] #29 - add creating SSH keys pair for AWS provider --- cmd/cluster.go | 46 +++++++++++++++++++------ cmd/cluster_capa.go | 10 +++++- cmd/cluster_capz.go | 2 +- go.mod | 7 ++-- go.sum | 10 +++--- providers/aws_provider/aws.go | 63 +++++++++++++++++++++++++++++++++++ 6 files changed, 119 insertions(+), 19 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index 3c190ff..8bc2a60 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "go.uber.org/zap" "os" "regexp" "strings" @@ -272,7 +273,7 @@ func (cc *ClusterCommands) switchKubeContext() error { switch cc.Conf.ClusterProvider { case aws_provider.AWSClusterProvider: - clusterContext, err := cc.AWSClusterContext() + clusterContext, err := cc.getAWSClusterContext() if err != nil { return err } @@ -281,7 +282,7 @@ func (cc *ClusterCommands) switchKubeContext() error { return err } case azure_provider.AzureClusterProvider: - clusterContext, err := cc.azureClusterContext() + clusterContext, err := cc.getAzureClusterContext() if err != nil { return err } @@ -294,6 +295,18 @@ func (cc *ClusterCommands) switchKubeContext() error { return nil } +func (cc *ClusterCommands) deleteKubeConfigItem(itemType, itemName string) error { + cc.SpecCMD = cc.kubectl("config", itemType, itemName) + cc.SpecCMD.DisableStdOut = true + if err := releaseRunner(cc).runCMD(); err != nil { + return fmt.Errorf("%s", cc.SpecCMD.StderrBuf.String()) + } + + zap.S().Info(cc.SpecCMD.StdoutBuf.String()) + + return nil +} + func (cc *ClusterCommands) provisionDestroyTargetCluster() error { if cc.Ctx.Command.Category == util.CAPI { cc.APICluster = true @@ -306,6 +319,14 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { switch cc.Ctx.Command.Name { case "provision": + switch cc.Conf.ClusterProvider { + case aws_provider.AWSClusterProvider: + if err := cc.createAWSClusterSSHKey(); err != nil { + return err + } + case azure_provider.AzureClusterProvider: + } + cc.SpecCMD = cc.prepareHelmfile("--log-level", "error", "-l", "cluster="+cc.Conf.ClusterProvider, "sync") if err := releaseRunner(cc).runCMD(); err != nil { return err @@ -313,7 +334,7 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { switch cc.Conf.ClusterProvider { case aws_provider.AWSClusterProvider: - clusterContext, err := cc.AWSClusterContext() + clusterContext, err := cc.getAWSClusterContext() if err != nil { return err } @@ -322,7 +343,7 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { return err } case azure_provider.AzureClusterProvider: - clusterContext, err := cc.azureClusterContext() + clusterContext, err := cc.getAzureClusterContext() if err != nil { return err } @@ -337,6 +358,14 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { return err } + switch cc.Conf.ClusterProvider { + case aws_provider.AWSClusterProvider: + if err := cc.deleteAWSClusterSSHKey(); err != nil { + return err + } + case azure_provider.AzureClusterProvider: + } + kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() if err != nil { @@ -344,18 +373,15 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { } if context, ok := kubeConfig.Contexts[cc.Conf.Name]; ok { - cc.SpecCMD = cc.kubectl("config", "delete-context", cc.Conf.Name) - if err := releaseRunner(cc).runCMD(); err != nil { + if err := cc.deleteKubeConfigItem("delete-context", cc.Conf.Name); err != nil { return err } - cc.SpecCMD = cc.kubectl("config", "delete-cluster", context.Cluster) - if err := releaseRunner(cc).runCMD(); err != nil { + if err := cc.deleteKubeConfigItem("delete-cluster", context.Cluster); err != nil { return err } - cc.SpecCMD = cc.kubectl("config", "delete-user", context.AuthInfo) - if err := releaseRunner(cc).runCMD(); err != nil { + if err := cc.deleteKubeConfigItem("delete-user", context.AuthInfo); err != nil { return err } } diff --git a/cmd/cluster_capa.go b/cmd/cluster_capa.go index 9661b52..254439a 100644 --- a/cmd/cluster_capa.go +++ b/cmd/cluster_capa.go @@ -162,6 +162,14 @@ func (cc *ClusterCommands) applyAWSClusterIdentity() error { return os.RemoveAll(acic.ManifestFilesDir) } -func (cc *ClusterCommands) AWSClusterContext() ([]byte, error) { +func (cc *ClusterCommands) getAWSClusterContext() ([]byte, error) { return aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile).GetAWSClusterContext(cc.Conf.Name) } + +func (cc *ClusterCommands) createAWSClusterSSHKey() error { + return aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile).CreateEC2SSHKey(cc.Conf.Name) +} + +func (cc *ClusterCommands) deleteAWSClusterSSHKey() error { + return aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile).DeleteEC2SSHKey(cc.Conf.Name) +} diff --git a/cmd/cluster_capz.go b/cmd/cluster_capz.go index 5a30900..06c3593 100644 --- a/cmd/cluster_capz.go +++ b/cmd/cluster_capz.go @@ -137,7 +137,7 @@ func (cc *ClusterCommands) applyAzureClusterIdentity() error { return os.RemoveAll(acic.ManifestFilesDir) } -func (cc *ClusterCommands) azureClusterContext() ([]byte, error) { +func (cc *ClusterCommands) getAzureClusterContext() ([]byte, error) { ac := azure_provider.NewAzureConfigure() if err := ac.NewAzureManagedClustersClient(cc.Ctx.Context, cc.Conf.Name); err != nil { diff --git a/go.mod b/go.mod index 6a70fda..cf9c388 100644 --- a/go.mod +++ b/go.mod @@ -12,10 +12,12 @@ require ( github.com/aws/aws-sdk-go-v2 v1.32.3 github.com/aws/aws-sdk-go-v2/config v1.26.3 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.187.0 github.com/aws/aws-sdk-go-v2/service/eks v1.51.1 github.com/aws/aws-sdk-go-v2/service/iam v1.28.7 github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 + github.com/aws/smithy-go v1.22.0 github.com/cheggaaa/pb v1.0.29 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-git/v5 v5.11.0 @@ -56,13 +58,12 @@ require ( github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect - github.com/aws/smithy-go v1.22.0 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect diff --git a/go.sum b/go.sum index b047807..0fe0e99 100644 --- a/go.sum +++ b/go.sum @@ -247,16 +247,18 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsM github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 h1:5oE2WzJE56/mVveuDZPJESKlg/00AaS2pY2QZcnxg4M= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10/go.mod h1:FHbKWQtRBYUz4vO5WBWjzMD2by126ny5y/1EoaWoLfI= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.187.0 h1:cA4hWo269CN5RY7Arqt8BfzXF0KIN8DSNo/KcqHKkWk= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.187.0/go.mod h1:ossaD9Z1ugYb6sq9QIqQLEOorCGcqUoxlhud9M9yE70= github.com/aws/aws-sdk-go-v2/service/eks v1.51.1 h1:OQjVHkANBbwE055NK49M/kelQbapsQOsSfUUWP1mi3w= github.com/aws/aws-sdk-go-v2/service/eks v1.51.1/go.mod h1:9wMtzHTjYbK5MLzYBWSznUPsys/n9LapMwb6UhKOVPQ= github.com/aws/aws-sdk-go-v2/service/iam v1.28.7 h1:FKPRDYZOO0Eur19vWUL1B40Op0j89KQj3kARjrszMK8= github.com/aws/aws-sdk-go-v2/service/iam v1.28.7/go.mod h1:YzMYyQ7S4twfYzLjwP24G1RAxypozVZeNaG1r2jxRms= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10 h1:L0ai8WICYHozIKK+OtPzVJBugL7culcuM4E4JOpIEm8= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10/go.mod h1:byqfyxJBshFk0fF9YmK0M0ugIO8OWjzH2T3bPG4eGuA= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 h1:qcxX0JYlgWH3hpPUnd6U0ikcl6LLA9sLkXE2w1fpMvY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3/go.mod h1:cLSNEmI45soc+Ef8K/L+8sEA3A3pYFEYf5B5UI+6bH4= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 h1:KOxnQeWy5sXyS37fdKEvAsGHOr9fa/qvwxfJurR/BzE= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10/go.mod h1:jMx5INQFYFYB3lQD9W0D8Ohgq6Wnl7NYOJ2TQndbulI= github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 h1:PJTdBMsyvra6FtED7JZtDpQrIAflYDHFoZAu/sKYkwU= diff --git a/providers/aws_provider/aws.go b/providers/aws_provider/aws.go index 4d80426..a93bcdd 100644 --- a/providers/aws_provider/aws.go +++ b/providers/aws_provider/aws.go @@ -20,12 +20,15 @@ import ( awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2Type "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go-v2/service/eks" eksType "github.com/aws/aws-sdk-go-v2/service/eks/types" "github.com/aws/aws-sdk-go-v2/service/iam" "github.com/aws/aws-sdk-go-v2/service/s3" s3type "github.com/aws/aws-sdk-go-v2/service/s3/types" "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/aws/smithy-go" "go.uber.org/zap" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" @@ -426,6 +429,66 @@ func (a *AwsConfigure) getKubeConfigUserName(clusterName string) string { return fmt.Sprintf("%s-capi-admin", clusterName) } +func (a *AwsConfigure) CreateEC2SSHKey(clusterName string) error { + var respError smithy.APIError + + cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) + if err != nil { + return err + } + + params := &ec2.CreateKeyPairInput{ + KeyName: aws.String(clusterName), + DryRun: aws.Bool(false), + KeyType: ec2Type.KeyTypeEd25519, + TagSpecifications: append([]ec2Type.TagSpecification{}, ec2Type.TagSpecification{ + ResourceType: ec2Type.ResourceTypeKeyPair, + Tags: append([]ec2Type.Tag{}, ec2Type.Tag{ + Key: aws.String("kubernetes.io/cluster/" + clusterName), + Value: aws.String("owned"), + }), + }), + } + + client := ec2.NewFromConfig(cfg) + sshKey, err := client.CreateKeyPair(a.Ctx, params) + if err != nil { + if errors.As(err, &respError) && respError.ErrorCode() != "InvalidKeyPair.Duplicate" { + return err + } + } + + if sshKey != nil { + zap.S().Infof("SSHKey %s with id %s was created", aws.ToString(sshKey.KeyName), aws.ToString(sshKey.KeyPairId)) + } + + return nil +} + +func (a *AwsConfigure) DeleteEC2SSHKey(clusterName string) error { + cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) + if err != nil { + return err + } + + params := &ec2.DeleteKeyPairInput{ + KeyName: aws.String(clusterName), + DryRun: aws.Bool(false), + } + + client := ec2.NewFromConfig(cfg) + sshKey, err := client.DeleteKeyPair(a.Ctx, params) + if err != nil { + return err + } + + if sshKey.KeyPairId != nil { + zap.S().Infof("SSHKey %s with id %s was deleted", clusterName, aws.ToString(sshKey.KeyPairId)) + } + + return nil +} + func (a *AwsConfigure) CreateBucket(bucketName string) error { var ( respError s3.ResponseError From 1c0ca2f5dd3a09c0a70dc2b0e976ca0b577a532e Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 6 Nov 2024 18:00:09 +0100 Subject: [PATCH 19/39] #29 - refactoring --- cmd/cluster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index 8bc2a60..dda319c 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -302,7 +302,7 @@ func (cc *ClusterCommands) deleteKubeConfigItem(itemType, itemName string) error return fmt.Errorf("%s", cc.SpecCMD.StderrBuf.String()) } - zap.S().Info(cc.SpecCMD.StdoutBuf.String()) + zap.S().Infof("%s", cc.SpecCMD.StdoutBuf.String()) return nil } From c4e02de18e9909416a92e49bc74c9706be51ab28 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 6 Nov 2024 21:07:00 +0100 Subject: [PATCH 20/39] #29 - refactoring --- cmd/cluster.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index dda319c..0d61635 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -299,10 +299,10 @@ func (cc *ClusterCommands) deleteKubeConfigItem(itemType, itemName string) error cc.SpecCMD = cc.kubectl("config", itemType, itemName) cc.SpecCMD.DisableStdOut = true if err := releaseRunner(cc).runCMD(); err != nil { - return fmt.Errorf("%s", cc.SpecCMD.StderrBuf.String()) + return fmt.Errorf("%s", strings.ReplaceAll(cc.SpecCMD.StderrBuf.String(), "\n", "")) } - zap.S().Infof("%s", cc.SpecCMD.StdoutBuf.String()) + zap.S().Infof("%s", strings.ReplaceAll(cc.SpecCMD.StdoutBuf.String(), "\n", "")) return nil } @@ -385,6 +385,18 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { return err } } + + if err := cc.deleteKubeConfigItem("delete-context", cc.Conf.Name); err != nil { + return err + } + + if err := cc.deleteKubeConfigItem("delete-cluster", cc.Conf.Name); err != nil { + return err + } + + if err := cc.deleteKubeConfigItem("delete-user", cc.Conf.Name); err != nil { + return err + } } return nil From 016ac74fff1203256a74322b7bcf263a8f36aa42 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 6 Nov 2024 21:55:24 +0100 Subject: [PATCH 21/39] #29 - refactoring --- cmd/cluster.go | 60 +++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index 0d61635..c487896 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -149,7 +149,19 @@ func (cc *ClusterCommands) initClusterCTLConfig() error { return os.RemoveAll(clusterCTLConfig) } -func (cc *ClusterCommands) mergeKubeConfig(clusterContext []byte) error { +func (cc *ClusterCommands) manageKubeConfigItem(itemType, itemName string) error { + cc.SpecCMD = cc.kubectl("config", itemType, itemName) + cc.SpecCMD.DisableStdOut = true + if err := releaseRunner(cc).runCMD(); err != nil { + return fmt.Errorf("%s", strings.ReplaceAll(cc.SpecCMD.StderrBuf.String(), "\n", "")) + } + + zap.S().Infof("%s", strings.ReplaceAll(cc.SpecCMD.StdoutBuf.String(), "\n", "")) + + return nil +} + +func (cc *ClusterCommands) mergeKubeConfigs(clusterContext []byte) error { var object interface{} if err := yaml.Unmarshal(clusterContext, &object); err != nil { @@ -177,12 +189,12 @@ func (cc *ClusterCommands) mergeKubeConfig(clusterContext []byte) error { return err } - json, err := runtime.Encode(clientcmdlatest.Codec, mergeConfig) + data, err := runtime.Encode(clientcmdlatest.Codec, mergeConfig) if err != nil { return err } - kubeConfig, err := yaml2.JSONToYAML(json) + kubeConfig, err := yaml2.JSONToYAML(data) if err != nil { return err } @@ -191,9 +203,7 @@ func (cc *ClusterCommands) mergeKubeConfig(clusterContext []byte) error { return err } - cc.SpecCMD = cc.kubectl("config", "use-context", cc.Conf.Name) - - return releaseRunner(cc).runCMD() + return cc.manageKubeConfigItem("use-context", cc.Conf.Name) } func (cc *ClusterCommands) getKubeContext() (string, string, error) { @@ -251,8 +261,7 @@ func (cc *ClusterCommands) switchKubeContext() error { } if _, ok := kubeConfig.Contexts[util.CAPIContextName]; ok && currentContextName != util.CAPIContextName { - cc.SpecCMD = cc.kubectl("config", "use-context", util.CAPIContextName) - return releaseRunner(cc).runCMD() + return cc.manageKubeConfigItem("use-context", util.CAPIContextName) } else if ok && currentContextName == util.CAPIContextName { return nil } @@ -260,8 +269,7 @@ func (cc *ClusterCommands) switchKubeContext() error { if len(contextName) > 0 && !cc.UpdateContext { if contextName != currentContextName { - cc.SpecCMD = cc.kubectl("config", "use-context", contextName) - return releaseRunner(cc).runCMD() + return cc.manageKubeConfigItem("use-context", contextName) } return nil @@ -278,7 +286,7 @@ func (cc *ClusterCommands) switchKubeContext() error { return err } - if err := cc.mergeKubeConfig(clusterContext); err != nil { + if err := cc.mergeKubeConfigs(clusterContext); err != nil { return err } case azure_provider.AzureClusterProvider: @@ -287,7 +295,7 @@ func (cc *ClusterCommands) switchKubeContext() error { return err } - if err := cc.mergeKubeConfig(clusterContext); err != nil { + if err := cc.mergeKubeConfigs(clusterContext); err != nil { return err } } @@ -295,18 +303,6 @@ func (cc *ClusterCommands) switchKubeContext() error { return nil } -func (cc *ClusterCommands) deleteKubeConfigItem(itemType, itemName string) error { - cc.SpecCMD = cc.kubectl("config", itemType, itemName) - cc.SpecCMD.DisableStdOut = true - if err := releaseRunner(cc).runCMD(); err != nil { - return fmt.Errorf("%s", strings.ReplaceAll(cc.SpecCMD.StderrBuf.String(), "\n", "")) - } - - zap.S().Infof("%s", strings.ReplaceAll(cc.SpecCMD.StdoutBuf.String(), "\n", "")) - - return nil -} - func (cc *ClusterCommands) provisionDestroyTargetCluster() error { if cc.Ctx.Command.Category == util.CAPI { cc.APICluster = true @@ -339,7 +335,7 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { return err } - if err := cc.mergeKubeConfig(clusterContext); err != nil { + if err := cc.mergeKubeConfigs(clusterContext); err != nil { return err } case azure_provider.AzureClusterProvider: @@ -348,7 +344,7 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { return err } - if err := cc.mergeKubeConfig(clusterContext); err != nil { + if err := cc.mergeKubeConfigs(clusterContext); err != nil { return err } } @@ -373,28 +369,28 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { } if context, ok := kubeConfig.Contexts[cc.Conf.Name]; ok { - if err := cc.deleteKubeConfigItem("delete-context", cc.Conf.Name); err != nil { + if err := cc.manageKubeConfigItem("delete-context", cc.Conf.Name); err != nil { return err } - if err := cc.deleteKubeConfigItem("delete-cluster", context.Cluster); err != nil { + if err := cc.manageKubeConfigItem("delete-cluster", context.Cluster); err != nil { return err } - if err := cc.deleteKubeConfigItem("delete-user", context.AuthInfo); err != nil { + if err := cc.manageKubeConfigItem("delete-user", context.AuthInfo); err != nil { return err } } - if err := cc.deleteKubeConfigItem("delete-context", cc.Conf.Name); err != nil { + if err := cc.manageKubeConfigItem("delete-context", cc.Conf.Name); err != nil { return err } - if err := cc.deleteKubeConfigItem("delete-cluster", cc.Conf.Name); err != nil { + if err := cc.manageKubeConfigItem("delete-cluster", cc.Conf.Name); err != nil { return err } - if err := cc.deleteKubeConfigItem("delete-user", cc.Conf.Name); err != nil { + if err := cc.manageKubeConfigItem("delete-user", cc.Conf.Name); err != nil { return err } } From c5fedc51ca67cbb73e4232952101316b481ac7c6 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Thu, 7 Nov 2024 15:33:10 +0100 Subject: [PATCH 22/39] #29 - refactoring --- cmd/cluster.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index c487896..36c251b 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -381,18 +381,6 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { return err } } - - if err := cc.manageKubeConfigItem("delete-context", cc.Conf.Name); err != nil { - return err - } - - if err := cc.manageKubeConfigItem("delete-cluster", cc.Conf.Name); err != nil { - return err - } - - if err := cc.manageKubeConfigItem("delete-user", cc.Conf.Name); err != nil { - return err - } } return nil From 8597c608b72f803da69c16566f01119f80148b99 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Fri, 8 Nov 2024 09:11:48 +0100 Subject: [PATCH 23/39] #29 - refactoring --- providers/aws_provider/aws.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/providers/aws_provider/aws.go b/providers/aws_provider/aws.go index a93bcdd..7662859 100644 --- a/providers/aws_provider/aws.go +++ b/providers/aws_provider/aws.go @@ -459,7 +459,7 @@ func (a *AwsConfigure) CreateEC2SSHKey(clusterName string) error { } if sshKey != nil { - zap.S().Infof("SSHKey %s with id %s was created", aws.ToString(sshKey.KeyName), aws.ToString(sshKey.KeyPairId)) + zap.S().Infof("created SSHKey %s with id %s", aws.ToString(sshKey.KeyName), aws.ToString(sshKey.KeyPairId)) } return nil @@ -483,7 +483,7 @@ func (a *AwsConfigure) DeleteEC2SSHKey(clusterName string) error { } if sshKey.KeyPairId != nil { - zap.S().Infof("SSHKey %s with id %s was deleted", clusterName, aws.ToString(sshKey.KeyPairId)) + zap.S().Infof("deleted SSHKey %s with id %s", clusterName, aws.ToString(sshKey.KeyPairId)) } return nil From 57d810cf2b44cc63004724e0970a6a3f61c72607 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Thu, 21 Nov 2024 10:46:21 +0100 Subject: [PATCH 24/39] #29 - implement support GCP provider for Cluster API --- cmd/cluster.go | 34 ++++++ cmd/cluster_capg.go | 87 +++++++++++++++ cmd/config.go | 41 ++++++++ cmd/flags.go | 25 +++-- cmd/release.go | 6 ++ config/config.go | 2 + go.mod | 37 ++++--- go.sum | 80 ++++++++------ providers/aws_provider/aws.go | 3 +- providers/azure_provider/azure.go | 40 +++++-- providers/google_provider/gcp.go | 169 ++++++++++++++++++++++++++++++ 11 files changed, 461 insertions(+), 63 deletions(-) create mode 100644 cmd/cluster_capg.go create mode 100644 providers/google_provider/gcp.go diff --git a/cmd/cluster.go b/cmd/cluster.go index 36c251b..5c3d87e 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -18,6 +18,7 @@ import ( "rmk/config" "rmk/providers/aws_provider" "rmk/providers/azure_provider" + "rmk/providers/google_provider" "rmk/util" ) @@ -58,6 +59,11 @@ func (cc *ClusterCommands) clusterCTL(args ...string) *util.SpecCMD { "EXP_MACHINE_POOL=true", "EXP_CLUSTER_RESOURCE_SET=false", } + case google_provider.GoogleClusterProvider: + envs = []string{ + "GCP_B64ENCODED_CREDENTIALS=", + "EXP_CAPG_GKE=true", + } } return &util.SpecCMD{ @@ -295,6 +301,15 @@ func (cc *ClusterCommands) switchKubeContext() error { return err } + if err := cc.mergeKubeConfigs(clusterContext); err != nil { + return err + } + case google_provider.GoogleClusterProvider: + clusterContext, err := cc.getGCPClusterContext() + if err != nil { + return err + } + if err := cc.mergeKubeConfigs(clusterContext); err != nil { return err } @@ -321,6 +336,9 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { return err } case azure_provider.AzureClusterProvider: + // Pre-provision hook for Azure + case google_provider.GoogleClusterProvider: + // Pre-provision hook for GCP } cc.SpecCMD = cc.prepareHelmfile("--log-level", "error", "-l", "cluster="+cc.Conf.ClusterProvider, "sync") @@ -344,6 +362,15 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { return err } + if err := cc.mergeKubeConfigs(clusterContext); err != nil { + return err + } + case google_provider.GoogleClusterProvider: + clusterContext, err := cc.getGCPClusterContext() + if err != nil { + return err + } + if err := cc.mergeKubeConfigs(clusterContext); err != nil { return err } @@ -360,6 +387,9 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { return err } case azure_provider.AzureClusterProvider: + // Pre-destroy hook for Azure + case google_provider.GoogleClusterProvider: + // Pre-destroy hook for GCP } kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( @@ -423,6 +453,8 @@ func CAPIInitAction(conf *config.Config) cli.AfterFunc { return cc.applyAWSClusterIdentity() case azure_provider.AzureClusterProvider: return cc.applyAzureClusterIdentity() + case google_provider.GoogleClusterProvider: + return cc.applyGCPClusterIdentitySecret() } return nil @@ -453,6 +485,8 @@ func CAPIUpdateAction(conf *config.Config) cli.ActionFunc { return cc.applyAWSClusterIdentity() case azure_provider.AzureClusterProvider: return cc.applyAzureClusterIdentity() + case google_provider.GoogleClusterProvider: + return cc.applyGCPClusterIdentitySecret() } return nil diff --git a/cmd/cluster_capg.go b/cmd/cluster_capg.go new file mode 100644 index 0000000..59407a8 --- /dev/null +++ b/cmd/cluster_capg.go @@ -0,0 +1,87 @@ +package cmd + +import ( + "os" + "path/filepath" + + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/client-go/applyconfigurations/core/v1" + + "rmk/providers/google_provider" +) + +const ( + gcpClusterIdentityName = "gcp-cluster-identity" + gcpClusterIdentityNamespace = "capg-system" + gcpClusterIdentitySecret = "gcp-cluster-identity-secret" + gcpFlagsCategory = "GCP authentication" +) + +var gcpClusterIdentitySecretType = corev1.SecretTypeOpaque + +type GCPClusterIdentityConfig struct { + *v1.SecretApplyConfiguration + ManifestFiles []string + ManifestFilesDir string +} + +func NewGCPClusterIdentityConfig(gcp *google_provider.GCPConfigure) *GCPClusterIdentityConfig { + gcpcc := &GCPClusterIdentityConfig{ + SecretApplyConfiguration: v1.Secret(gcpClusterIdentitySecret, gcpClusterIdentityNamespace), + ManifestFilesDir: filepath.Join("/tmp", gcpClusterIdentityName), + } + + gcpcc.SecretApplyConfiguration.Type = &gcpClusterIdentitySecretType + gcpcc.SecretApplyConfiguration.Data = map[string][]byte{"credentials": gcp.AppCredentials.JSON()} + + return gcpcc +} + +func (gic *GCPClusterIdentityConfig) createGCPClusterIdentitySecretManifestFiles() error { + if err := os.MkdirAll(gic.ManifestFilesDir, 0775); err != nil { + return err + } + + fileSecret, err := createManifestFile(gic.SecretApplyConfiguration, gic.ManifestFilesDir, gcpClusterIdentitySecret) + if err != nil { + return err + } + + gic.ManifestFiles = append(gic.ManifestFiles, fileSecret) + + return nil +} + +func (cc *ClusterCommands) applyGCPClusterIdentitySecret() error { + var kubectlArgs = []string{"apply"} + + gcp := google_provider.NewGCPConfigure(cc.Ctx.Context, cc.Conf.GCPConfigure.AppCredentialsPath) + if err := gcp.ReadSACredentials(); err != nil { + return err + } + + gic := NewGCPClusterIdentityConfig(gcp) + if err := gic.createGCPClusterIdentitySecretManifestFiles(); err != nil { + return err + } + + for _, val := range gic.ManifestFiles { + kubectlArgs = append(kubectlArgs, "-f", val) + } + + cc.SpecCMD = cc.kubectl(kubectlArgs...) + if err := releaseRunner(cc).runCMD(); err != nil { + if err := os.RemoveAll(gic.ManifestFilesDir); err != nil { + return err + } + + return err + } + + return os.RemoveAll(gic.ManifestFilesDir) +} + +func (cc *ClusterCommands) getGCPClusterContext() ([]byte, error) { + return google_provider.NewGCPConfigure(cc.Ctx.Context, cc.Conf.GCPConfigure.AppCredentialsPath). + GetGCPClusterContext(cc.Conf.Name) +} diff --git a/cmd/config.go b/cmd/config.go index d067792..7cc5487 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -17,6 +17,7 @@ import ( "rmk/git_handler" "rmk/providers/aws_provider" "rmk/providers/azure_provider" + "rmk/providers/google_provider" "rmk/util" ) @@ -345,6 +346,30 @@ func initAzureProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler. return nil } +func initGCPProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.GitSpec) error { + gcp := google_provider.NewGCPConfigure(c.Context, + util.GetHomePath(google_provider.GoogleHomeDir, google_provider.GooglePrefix+gitSpec.ID+".json")) + + if c.IsSet("google-application-credentials") { + gcp.AppCredentialsPath = c.String("google-application-credentials") + if err := gcp.ReadSACredentials(); err != nil { + return err + } + + if err := gcp.CopySACredentials(gitSpec.ID); err != nil { + return err + } + } else { + if err := gcp.ReadSACredentials(); err != nil { + return err + } + } + + conf.GCPConfigure = gcp + + return nil +} + func configDeleteAction(conf *config.Config) cli.ActionFunc { return func(c *cli.Context) error { if err := util.ValidateNArg(c, 0); err != nil { @@ -378,6 +403,11 @@ func configDeleteAction(conf *config.Config) cli.ActionFunc { azure_provider.AzurePrefix+conf.Name+".json")); err != nil { return err } + case c.String("cluster-provider") == google_provider.GoogleClusterProvider: + if err := os.RemoveAll(util.GetHomePath(google_provider.GoogleHomeDir, + google_provider.GooglePrefix+conf.Name+".json")); err != nil { + return err + } } if err := os.RemoveAll(c.String("config")); err != nil { @@ -419,6 +449,7 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act switch conf.ClusterProvider { case aws_provider.AWSClusterProvider: conf.AzureConfigure = nil + conf.GCPConfigure = nil if err := initAWSProfile(c, conf, gitSpec); err != nil { return err } @@ -426,14 +457,24 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+aws_provider.AWSClusterProvider) case azure_provider.AzureClusterProvider: conf.AwsConfigure = nil + conf.GCPConfigure = nil if err := initAzureProfile(c, conf, gitSpec); err != nil { return err } conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+azure_provider.AzureClusterProvider) + case google_provider.GoogleClusterProvider: + conf.AwsConfigure = nil + conf.AzureConfigure = nil + if err := initGCPProfile(c, conf, gitSpec); err != nil { + return err + } + + conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+google_provider.GoogleClusterProvider) case util.LocalClusterProvider: conf.AwsConfigure = nil conf.AzureConfigure = nil + conf.GCPConfigure = nil conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+util.LocalClusterProvider) } diff --git a/cmd/flags.go b/cmd/flags.go index 5acaf51..7dab8e1 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -83,6 +83,15 @@ func flagsConfig() []cli.Flag { Aliases: []string{"azt"}, EnvVars: []string{"RMK_AZURE_TENANT_ID", "AZURE_TENANT_ID"}, }, + altsrc.NewStringFlag( + &cli.StringFlag{ + Name: "cluster-provider", + Usage: "select cluster provider to provision clusters", + Aliases: []string{"cp"}, + EnvVars: []string{"RMK_CLUSTER_PROVIDER"}, + Value: util.LocalClusterProvider, + }, + ), &cli.StringFlag{ Name: "config", Hidden: true, @@ -95,15 +104,13 @@ func flagsConfig() []cli.Flag { EnvVars: []string{"RMK_GITHUB_TOKEN"}, }, ), - altsrc.NewStringFlag( - &cli.StringFlag{ - Name: "cluster-provider", - Usage: "select cluster provider to provision clusters", - Aliases: []string{"cp"}, - EnvVars: []string{"RMK_CLUSTER_PROVIDER"}, - Value: util.LocalClusterProvider, - }, - ), + &cli.StringFlag{ + Category: gcpFlagsCategory, + Name: "google-application-credentials", + Usage: "GCP service account credentials file path in JSON format", + Aliases: []string{"gac"}, + EnvVars: []string{"RMK_GOOGLE_APPLICATION_CREDENTIALS", "GOOGLE_APPLICATION_CREDENTIALS"}, + }, altsrc.NewBoolFlag( &cli.BoolFlag{ Name: "progress-bar", diff --git a/cmd/release.go b/cmd/release.go index 54f6cb6..511064f 100644 --- a/cmd/release.go +++ b/cmd/release.go @@ -22,6 +22,7 @@ import ( "rmk/notification" "rmk/providers/aws_provider" "rmk/providers/azure_provider" + "rmk/providers/google_provider" "rmk/util" ) @@ -164,6 +165,11 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { envs = append(envs, "AZURE_SUBSCRIPTION_ID="+rc.Conf.AzureConfigure.SubscriptionID, ) + case google_provider.GoogleClusterProvider: + envs = append(envs, + "GCP_PROJECT_ID="+rc.Conf.GCPConfigure.ProjectID, + "GOOGLE_APPLICATION_CREDENTIALS="+rc.Conf.GCPConfigure.AppCredentialsPath, + ) } for _, val := range rc.Conf.HooksMapping { diff --git a/config/config.go b/config/config.go index c79b50e..40d0ecf 100644 --- a/config/config.go +++ b/config/config.go @@ -19,6 +19,7 @@ import ( "rmk/git_handler" "rmk/providers/aws_provider" "rmk/providers/azure_provider" + "rmk/providers/google_provider" "rmk/util" ) @@ -39,6 +40,7 @@ type Config struct { AWSMFATokenExpiration string `yaml:"aws-mfa-token-expiration,omitempty"` *aws_provider.AwsConfigure `yaml:"aws,omitempty"` *azure_provider.AzureConfigure `yaml:"azure,omitempty"` + *google_provider.GCPConfigure `yaml:"gcp,omitempty"` ProgressBar bool `yaml:"progress-bar"` ProjectFile `yaml:"project-file"` } diff --git a/go.mod b/go.mod index cf9c388..299685b 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.22.0 toolchain go1.22.8 require ( + cloud.google.com/go/auth v0.10.2 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.1.0 github.com/Masterminds/semver v1.5.0 @@ -27,8 +28,9 @@ require ( github.com/urfave/cli/v2 v2.27.1 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.27.0 - golang.org/x/net v0.28.0 - golang.org/x/oauth2 v0.21.0 + golang.org/x/net v0.29.0 + golang.org/x/oauth2 v0.22.0 + google.golang.org/api v0.177.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.31.1 k8s.io/apimachinery v0.31.1 @@ -37,10 +39,11 @@ require ( ) require ( - cloud.google.com/go v0.110.6 // indirect - cloud.google.com/go/compute/metadata v0.3.0 // indirect - cloud.google.com/go/iam v1.1.1 // indirect - cloud.google.com/go/storage v1.30.1 // indirect + cloud.google.com/go v0.112.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect + cloud.google.com/go/compute/metadata v0.5.1 // indirect + cloud.google.com/go/iam v1.1.6 // indirect + cloud.google.com/go/storage v1.39.1 // indirect dario.cat/mergo v1.0.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect @@ -72,10 +75,12 @@ require ( github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/fatih/color v1.17.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect @@ -86,10 +91,10 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/s2a-go v0.1.4 // indirect + github.com/google/s2a-go v0.1.8 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.11.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.12.3 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect @@ -129,6 +134,11 @@ require ( github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/sync v0.8.0 // indirect @@ -137,13 +147,10 @@ require ( golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.24.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.126.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect - google.golang.org/grpc v1.64.1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/grpc v1.66.2 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index 0fe0e99..f2b253b 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= -cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.112.2 h1:ZaGT6LiG7dBzi6zNOvVZwacaXlmf3lRqnC4DQzqyRQw= +cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= @@ -46,6 +46,10 @@ cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjby cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/auth v0.10.2 h1:oKF7rgBfSHdp/kuhXtqU/tNDr0mZqhYbEh+6SiqzkKo= +cloud.google.com/go/auth v0.10.2/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= +cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= +cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= @@ -68,8 +72,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.5.1 h1:NM6oZeZNlYjiwYje+sYFjEpP0Q0zCan1bmQW/KmIrGs= +cloud.google.com/go/compute/metadata v0.5.1/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= @@ -107,8 +111,8 @@ cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y97 cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= @@ -169,8 +173,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +cloud.google.com/go/storage v1.39.1 h1:MvraqHKhogCOTXTlct/9C3K3+Uy2jBmFYb3/Sp6dVtY= +cloud.google.com/go/storage v1.39.1/go.mod h1:xK6xZmxZmo+fyP7+DEF6FhNc24/JAe95OLyOHCXFH1o= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= @@ -324,6 +328,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= @@ -343,8 +349,11 @@ github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lK github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -446,8 +455,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -456,8 +465,8 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -467,8 +476,8 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= -github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= +github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= +github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -629,6 +638,18 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -642,7 +663,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= @@ -739,8 +759,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -766,8 +786,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -876,7 +896,6 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= @@ -951,8 +970,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1001,8 +1021,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.177.0 h1:8a0p/BbPa65GlqGWtUKxot4p0TV8OGOfyTjtmkXNXmk= +google.golang.org/api v0.177.0/go.mod h1:srbhue4MLjkjbkux5p3dw/ocYOSZTaIEvf7bCOnFQDw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1010,8 +1030,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1113,12 +1131,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 h1:QW9+G6Fir4VcRXVH8x3LilNAb6cxBGLa6+GM4hRwexE= google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3/go.mod h1:kdrSS/OiLkPrNUpzD4aHgCq2rVuC/YRxok32HXZ4vRE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1154,8 +1172,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= +google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/providers/aws_provider/aws.go b/providers/aws_provider/aws.go index 7662859..8952268 100644 --- a/providers/aws_provider/aws.go +++ b/providers/aws_provider/aws.go @@ -343,7 +343,8 @@ func (a *AwsConfigure) GetAWSClusterContext(clusterName string) ([]byte, error) client := eks.NewFromConfig(cfg) cluster, err := client.DescribeCluster(a.Ctx, &eks.DescribeClusterInput{Name: aws.String(clusterName)}) if err != nil { - return nil, err + return nil, fmt.Errorf("kubecontext to %s provider's for cluster %s not found", + strings.ToUpper(AWSClusterProvider), clusterName) } return a.generateUserKubeconfig(cluster.Cluster) diff --git a/providers/azure_provider/azure.go b/providers/azure_provider/azure.go index ee88ee1..b9d352c 100644 --- a/providers/azure_provider/azure.go +++ b/providers/azure_provider/azure.go @@ -28,8 +28,9 @@ type AzureRawServicePrincipal struct { } type AzureClient struct { - ARMClient *armcontainerservice.ManagedClustersClient `json:"-" yaml:"-"` - Ctx context.Context `json:"-" yaml:"-"` + ManagedClustersClient *armcontainerservice.ManagedClustersClient `json:"-" yaml:"-"` + //SSHPublicKeysClient *armcompute.SSHPublicKeysClient `json:"-" yaml:"-"` + Ctx context.Context `json:"-" yaml:"-"` } type AzureConfigure struct { @@ -118,26 +119,51 @@ func (ac *AzureConfigure) NewAzureManagedClustersClient(ctx context.Context, fil return err } - factory, err := armcontainerservice.NewClientFactory(ac.SubscriptionID, cred, nil) + ManagedClustersFactory, err := armcontainerservice.NewClientFactory(ac.SubscriptionID, cred, nil) if err != nil { return err } - ac.ARMClient = factory.NewManagedClustersClient() + //SSHPublicKeysFactory, err := armcompute.NewClientFactory(ac.SubscriptionID, cred, nil) + //if err != nil { + // return err + //} + ac.Ctx = ctx + ac.ManagedClustersClient = ManagedClustersFactory.NewManagedClustersClient() + //ac.SSHPublicKeysClient = SSHPublicKeysFactory.NewSSHPublicKeysClient() return nil } func (ac *AzureConfigure) GetAzureClusterContext(groupName, clusterName string) ([]byte, error) { - credentials, err := ac.ARMClient.ListClusterAdminCredentials(ac.Ctx, groupName, clusterName, nil) + var cpTitle = strings.ToUpper(AzureClusterProvider[:1]) + strings.ToLower(AzureClusterProvider[1:]) + + credentials, err := ac.ManagedClustersClient.ListClusterAdminCredentials(ac.Ctx, groupName, clusterName, nil) if err != nil { - return nil, err + return nil, fmt.Errorf("kubecontext to %s provider's for cluster %s not found", + cpTitle, clusterName) } if len(credentials.CredentialResults.Kubeconfigs) == 1 { return credentials.CredentialResults.Kubeconfigs[0].Value, nil } - return nil, fmt.Errorf("context for cluster %s not found", clusterName) + return nil, fmt.Errorf("kubecontext to %s provider's for cluster %s not found", + cpTitle, clusterName) } + +//func (ac *AzureConfigure) CreateVMSSHKey(groupName, clusterName string) ([]byte, error) { +// res, err := ac.SSHPublicKeysClient.GenerateKeyPair(ac.Ctx, groupName, clusterName, +// &armcompute.SSHPublicKeysClientGenerateKeyPairOptions{ +// Parameters: &armcompute.SSHGenerateKeyPairInputParameters{ +// EncryptionType: to.Ptr(armcompute.SSHEncryptionTypesEd25519), +// }, +// }, +// ) +// if err != nil { +// return nil, err +// } +// +// return res.MarshalJSON() +//} diff --git a/providers/google_provider/gcp.go b/providers/google_provider/gcp.go new file mode 100644 index 0000000..1f24f9e --- /dev/null +++ b/providers/google_provider/gcp.go @@ -0,0 +1,169 @@ +package google_provider + +import ( + "context" + "encoding/base64" + "fmt" + "os" + "strings" + + "cloud.google.com/go/auth" + "cloud.google.com/go/auth/credentials" + container "google.golang.org/api/container/v1beta1" + "google.golang.org/api/option" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/clientcmd/api" + + "rmk/util" +) + +const ( + GoogleClusterProvider = "gcp" + GoogleHomeDir = ".config/gcloud" + GooglePrefix = "gcp-credentials-" +) + +type GCPConfigure struct { + AppCredentials *auth.Credentials `yaml:"-"` + AppCredentialsPath string `yaml:"app-credentials-path"` + Ctx context.Context `yaml:"-"` + ProjectID string `yaml:"project-id"` +} + +func NewGCPConfigure(ctx context.Context, appCredentialsPath string) *GCPConfigure { + return &GCPConfigure{Ctx: ctx, AppCredentialsPath: appCredentialsPath} +} + +func (gcp *GCPConfigure) ReadSACredentials() error { + data, err := os.ReadFile(gcp.AppCredentialsPath) + if err != nil { + return err + } + + gcp.AppCredentials, err = credentials.DetectDefault(&credentials.DetectOptions{CredentialsJSON: data}) + if err != nil { + return err + } + + gcp.ProjectID, err = gcp.AppCredentials.ProjectID(gcp.Ctx) + if err != nil { + return err + } + + return nil +} + +func (gcp *GCPConfigure) CopySACredentials(fileSuffix string) error { + if err := os.MkdirAll(util.GetHomePath(GoogleHomeDir), 0755); err != nil { + return err + } + + gcp.AppCredentialsPath = util.GetHomePath(GoogleHomeDir, GooglePrefix+fileSuffix+".json") + + return os.WriteFile(util.GetHomePath(GoogleHomeDir, GooglePrefix+fileSuffix+".json"), + gcp.AppCredentials.JSON(), 0644) +} + +func (gcp *GCPConfigure) GetGCPClusterContext(clusterName string) ([]byte, error) { + var cluster *container.Cluster + + if err := gcp.ReadSACredentials(); err != nil { + return nil, err + } + + client, err := container.NewService(gcp.Ctx, option.WithAuthCredentials(gcp.AppCredentials)) + if err != nil { + return nil, err + } + + resp, err := client.Projects.Zones.Clusters.List(gcp.ProjectID, "-").Context(gcp.Ctx).Do() + if err != nil { + return nil, err + } + + for _, val := range resp.Clusters { + if val.Name == clusterName { + cluster = val + break + } + } + + if cluster != nil { + return gcp.generateUserKubeconfig(cluster) + } + + return nil, fmt.Errorf("kubecontext to %s provider's for cluster %s not found", + strings.ToUpper(GoogleClusterProvider), clusterName) +} + +func (gcp *GCPConfigure) generateUserKubeconfig(cluster *container.Cluster) ([]byte, error) { + var execEnvVars []api.ExecEnvVar + + userName := gcp.getKubeConfigUserName(cluster.Name) + cfg, err := gcp.generateBaseKubeConfig(cluster) + if err != nil { + return nil, fmt.Errorf("creating base kubeconfig: %w", err) + } + + execEnvVars = append(execEnvVars, + api.ExecEnvVar{Name: "GOOGLE_APPLICATION_CREDENTIALS", Value: gcp.AppCredentialsPath}, + ) + + // Version v1alpha1 was removed in Kubernetes v1.23. + // Version v1 was released in Kubernetes v1.23. + // Version v1beta1 was selected as it has the widest range of support + // This should be changed to v1 once EKS no longer supports Kubernetes Date: Thu, 21 Nov 2024 11:35:53 +0100 Subject: [PATCH 25/39] #29 - refactoring --- cmd/cluster.go | 4 ++-- cmd/cluster_capa.go | 2 +- cmd/cluster_capg.go | 2 +- cmd/cluster_capz.go | 2 +- cmd/flags.go | 4 ++-- cmd/k3d.go | 2 +- providers/aws_provider/aws.go | 4 ++-- providers/azure_provider/azure.go | 4 ++-- providers/google_provider/gcp.go | 4 ++-- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index 5c3d87e..01ee646 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -107,7 +107,7 @@ func createClusterCTLConfigFile(output []byte) (string, error) { return "", err } - return util.CreateTempYAMLFile("/tmp", clusterCTL.Metadata["name"], data) + return util.CreateTempYAMLFile(os.TempDir(), clusterCTL.Metadata["name"], data) } func (cc *ClusterCommands) getClusterCTLConfig() (string, string, error) { @@ -174,7 +174,7 @@ func (cc *ClusterCommands) mergeKubeConfigs(clusterContext []byte) error { return err } - file, err := createManifestFile(object, "/tmp", cc.Conf.Name+"-"+cc.Conf.ClusterProvider+"-kubeconfig") + file, err := createManifestFile(object, os.TempDir(), cc.Conf.Name+"-"+cc.Conf.ClusterProvider+"-kubeconfig") if err != nil { return err } diff --git a/cmd/cluster_capa.go b/cmd/cluster_capa.go index 254439a..b5d555a 100644 --- a/cmd/cluster_capa.go +++ b/cmd/cluster_capa.go @@ -80,7 +80,7 @@ func NewAWSClusterStaticIdentityConfig(ac *aws_provider.AwsConfigure) *AWSCluste }, AWSIAMControllerSecret: v1.Secret(awsIAMControllerSecret, awsClusterStaticIdentityNamespace), SecretApplyConfiguration: v1.Secret(awsClusterStaticIdentitySecret, awsClusterStaticIdentityNamespace), - ManifestFilesDir: filepath.Join("/tmp", awsClusterStaticIdentityName), + ManifestFilesDir: filepath.Join(os.TempDir(), awsClusterStaticIdentityName), } profile, err := ac.RenderAWSConfigProfile(awsIAMControllerCredentialsTemplate) diff --git a/cmd/cluster_capg.go b/cmd/cluster_capg.go index 59407a8..787b575 100644 --- a/cmd/cluster_capg.go +++ b/cmd/cluster_capg.go @@ -28,7 +28,7 @@ type GCPClusterIdentityConfig struct { func NewGCPClusterIdentityConfig(gcp *google_provider.GCPConfigure) *GCPClusterIdentityConfig { gcpcc := &GCPClusterIdentityConfig{ SecretApplyConfiguration: v1.Secret(gcpClusterIdentitySecret, gcpClusterIdentityNamespace), - ManifestFilesDir: filepath.Join("/tmp", gcpClusterIdentityName), + ManifestFilesDir: filepath.Join(os.TempDir(), gcpClusterIdentityName), } gcpcc.SecretApplyConfiguration.Type = &gcpClusterIdentitySecretType diff --git a/cmd/cluster_capz.go b/cmd/cluster_capz.go index 06c3593..74c0f60 100644 --- a/cmd/cluster_capz.go +++ b/cmd/cluster_capz.go @@ -77,7 +77,7 @@ func NewAzureClusterIdentityConfig(ac *azure_provider.AzureConfigure) *AzureClus }, }, SecretApplyConfiguration: v1.Secret(azureClusterIdentitySecret, azureClusterIdentityNamespace), - ManifestFilesDir: filepath.Join("/tmp", azureClusterIdentityName), + ManifestFilesDir: filepath.Join(os.TempDir(), azureClusterIdentityName), } acic.SecretApplyConfiguration.Type = &azureClusterIdentitySecretType diff --git a/cmd/flags.go b/cmd/flags.go index 7dab8e1..09f2a36 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -86,7 +86,7 @@ func flagsConfig() []cli.Flag { altsrc.NewStringFlag( &cli.StringFlag{ Name: "cluster-provider", - Usage: "select cluster provider to provision clusters", + Usage: "cluster provider for provisioning", Aliases: []string{"cp"}, EnvVars: []string{"RMK_CLUSTER_PROVIDER"}, Value: util.LocalClusterProvider, @@ -107,7 +107,7 @@ func flagsConfig() []cli.Flag { &cli.StringFlag{ Category: gcpFlagsCategory, Name: "google-application-credentials", - Usage: "GCP service account credentials file path in JSON format", + Usage: "path to GCP service account credentials JSON file", Aliases: []string{"gac"}, EnvVars: []string{"RMK_GOOGLE_APPLICATION_CREDENTIALS", "GOOGLE_APPLICATION_CREDENTIALS"}, }, diff --git a/cmd/k3d.go b/cmd/k3d.go index 092c60c..d6a3203 100644 --- a/cmd/k3d.go +++ b/cmd/k3d.go @@ -81,7 +81,7 @@ func (k *K3DCommands) createDeleteK3DCluster() error { k.Ctx.Command.Category, k.SpecCMD.StderrBuf.String()) } - k3dConfig, err := util.CreateTempYAMLFile("/tmp", k.Ctx.Command.Category+"-config", k.SpecCMD.StdoutBuf.Bytes()) + k3dConfig, err := util.CreateTempYAMLFile("os.TempDir()", k.Ctx.Command.Category+"-config", k.SpecCMD.StdoutBuf.Bytes()) if err != nil { return err } diff --git a/providers/aws_provider/aws.go b/providers/aws_provider/aws.go index 8952268..2e601ef 100644 --- a/providers/aws_provider/aws.go +++ b/providers/aws_provider/aws.go @@ -343,7 +343,7 @@ func (a *AwsConfigure) GetAWSClusterContext(clusterName string) ([]byte, error) client := eks.NewFromConfig(cfg) cluster, err := client.DescribeCluster(a.Ctx, &eks.DescribeClusterInput{Name: aws.String(clusterName)}) if err != nil { - return nil, fmt.Errorf("kubecontext to %s provider's for cluster %s not found", + return nil, fmt.Errorf("kubecontext for %s provider's %s cluster not found", strings.ToUpper(AWSClusterProvider), clusterName) } @@ -390,7 +390,7 @@ func (a *AwsConfigure) generateUserKubeconfig(cluster *eksType.Cluster) ([]byte, out, err := clientcmd.Write(*cfg) if err != nil { - return nil, fmt.Errorf("failed to serialize kubeconfig to yaml: %w", err) + return nil, fmt.Errorf("failed to serialize kubeconfig to YAML: %w", err) } return out, nil diff --git a/providers/azure_provider/azure.go b/providers/azure_provider/azure.go index b9d352c..103dac6 100644 --- a/providers/azure_provider/azure.go +++ b/providers/azure_provider/azure.go @@ -141,7 +141,7 @@ func (ac *AzureConfigure) GetAzureClusterContext(groupName, clusterName string) credentials, err := ac.ManagedClustersClient.ListClusterAdminCredentials(ac.Ctx, groupName, clusterName, nil) if err != nil { - return nil, fmt.Errorf("kubecontext to %s provider's for cluster %s not found", + return nil, fmt.Errorf("kubecontext for %s provider's %s cluster not found", cpTitle, clusterName) } @@ -149,7 +149,7 @@ func (ac *AzureConfigure) GetAzureClusterContext(groupName, clusterName string) return credentials.CredentialResults.Kubeconfigs[0].Value, nil } - return nil, fmt.Errorf("kubecontext to %s provider's for cluster %s not found", + return nil, fmt.Errorf("kubecontext for %s provider's %s cluster not found", cpTitle, clusterName) } diff --git a/providers/google_provider/gcp.go b/providers/google_provider/gcp.go index 1f24f9e..37d21f6 100644 --- a/providers/google_provider/gcp.go +++ b/providers/google_provider/gcp.go @@ -92,7 +92,7 @@ func (gcp *GCPConfigure) GetGCPClusterContext(clusterName string) ([]byte, error return gcp.generateUserKubeconfig(cluster) } - return nil, fmt.Errorf("kubecontext to %s provider's for cluster %s not found", + return nil, fmt.Errorf("kubecontext for %s provider's %s cluster not found", strings.ToUpper(GoogleClusterProvider), clusterName) } @@ -130,7 +130,7 @@ func (gcp *GCPConfigure) generateUserKubeconfig(cluster *container.Cluster) ([]b out, err := clientcmd.Write(*cfg) if err != nil { - return nil, fmt.Errorf("failed to serialize kubeconfig to yaml: %w", err) + return nil, fmt.Errorf("failed to serialize kubeconfig to YAML: %w", err) } return out, nil From a00f79d5a1ef369bd4c434795803ff67f26cda31 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Thu, 21 Nov 2024 13:31:23 +0100 Subject: [PATCH 26/39] #29 - refactoring --- cmd/k3d.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/k3d.go b/cmd/k3d.go index d6a3203..e7fa295 100644 --- a/cmd/k3d.go +++ b/cmd/k3d.go @@ -81,7 +81,7 @@ func (k *K3DCommands) createDeleteK3DCluster() error { k.Ctx.Command.Category, k.SpecCMD.StderrBuf.String()) } - k3dConfig, err := util.CreateTempYAMLFile("os.TempDir()", k.Ctx.Command.Category+"-config", k.SpecCMD.StdoutBuf.Bytes()) + k3dConfig, err := util.CreateTempYAMLFile(os.TempDir(), k.Ctx.Command.Category+"-config", k.SpecCMD.StdoutBuf.Bytes()) if err != nil { return err } From 358d4e62310825ca3d64d1e71d1a8d25d2d29dd3 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Mon, 25 Nov 2024 15:32:44 +0100 Subject: [PATCH 27/39] #29 - refactoring Google provider environment variables --- cmd/cluster.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/cluster.go b/cmd/cluster.go index 01ee646..96122e9 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -63,6 +63,7 @@ func (cc *ClusterCommands) clusterCTL(args ...string) *util.SpecCMD { envs = []string{ "GCP_B64ENCODED_CREDENTIALS=", "EXP_CAPG_GKE=true", + "EXP_MACHINE_POOL=true", } } From 57c742a53bd3200387c0ff153972623b122a2302 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 11 Dec 2024 08:58:07 +0100 Subject: [PATCH 28/39] #29 - implement support Azure key vault for store SOPS Age keys --- cmd/cluster.go | 9 +- cmd/cluster_capg.go | 5 + cmd/cluster_capz.go | 40 ++++- cmd/config.go | 42 ++++- cmd/flags.go | 7 + cmd/release.go | 1 + cmd/secret.go | 73 ++++++++- config/config.go | 1 - go.mod | 6 +- go.sum | 10 ++ providers/azure_provider/azure.go | 251 +++++++++++++++++++++++++++--- providers/google_provider/gcp.go | 29 ++++ 12 files changed, 439 insertions(+), 35 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index 96122e9..3cc7f30 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -337,9 +337,16 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { return err } case azure_provider.AzureClusterProvider: - // Pre-provision hook for Azure + if err := cc.createAzureSecrets(cc.Conf.AzureConfigure); err != nil { + return err + } case google_provider.GoogleClusterProvider: // Pre-provision hook for GCP + //if err := cc.createGCPNATGateway(); err != nil { + // return err + //} + + //return nil } cc.SpecCMD = cc.prepareHelmfile("--log-level", "error", "-l", "cluster="+cc.Conf.ClusterProvider, "sync") diff --git a/cmd/cluster_capg.go b/cmd/cluster_capg.go index 787b575..57acdd7 100644 --- a/cmd/cluster_capg.go +++ b/cmd/cluster_capg.go @@ -85,3 +85,8 @@ func (cc *ClusterCommands) getGCPClusterContext() ([]byte, error) { return google_provider.NewGCPConfigure(cc.Ctx.Context, cc.Conf.GCPConfigure.AppCredentialsPath). GetGCPClusterContext(cc.Conf.Name) } + +//func (cc *ClusterCommands) createGCPNATGateway() error { +// return google_provider.NewGCPConfigure(cc.Ctx.Context, cc.Conf.GCPConfigure.AppCredentialsPath). +// CreateGateway() +//} diff --git a/cmd/cluster_capz.go b/cmd/cluster_capz.go index 74c0f60..d0e5b4f 100644 --- a/cmd/cluster_capz.go +++ b/cmd/cluster_capz.go @@ -3,12 +3,14 @@ package cmd import ( "os" "path/filepath" + "strings" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/client-go/applyconfigurations/core/v1" "rmk/providers/azure_provider" + "rmk/util" ) const ( @@ -140,9 +142,45 @@ func (cc *ClusterCommands) applyAzureClusterIdentity() error { func (cc *ClusterCommands) getAzureClusterContext() ([]byte, error) { ac := azure_provider.NewAzureConfigure() - if err := ac.NewAzureManagedClustersClient(cc.Ctx.Context, cc.Conf.Name); err != nil { + if err := ac.NewAzureClient(cc.Ctx.Context, cc.Conf.Name); err != nil { return nil, err } return ac.GetAzureClusterContext(cc.Conf.Tenant, cc.Conf.Name) } + +func (cc *ClusterCommands) createAzureSecrets(ac *azure_provider.AzureConfigure) error { + if err := ac.NewAzureClient(cc.Ctx.Context, cc.Conf.Name); err != nil { + return err + } + + secrets, err := ac.GetAzureSecrets() + if err != nil { + return err + } + + if len(secrets) > 0 { + return err + } + + walkMatch, err := util.WalkMatch(cc.Conf.SopsAgeKeys, cc.Conf.Tenant+"*"+util.SopsAgeKeyExt) + if err != nil { + return err + } + + for _, val := range walkMatch { + file, err := os.ReadFile(val) + if err != nil { + return err + } + + keyName := strings.TrimSuffix(filepath.Base(val), util.SopsAgeKeyExt) + value := string(file) + + if err := ac.SetAzureSecret(keyName, value); err != nil { + return err + } + } + + return nil +} diff --git a/cmd/config.go b/cmd/config.go index 7cc5487..8cdd4ff 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "regexp" "strconv" "strings" @@ -325,6 +326,10 @@ func initAzureProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler. ac.ClientSecret = c.String("azure-client-secret") } + if c.IsSet("azure-location") { + ac.Location = c.String("azure-location") + } + if c.IsSet("azure-subscription-id") { ac.SubscriptionID = c.String("azure-subscription-id") } @@ -343,6 +348,35 @@ func initAzureProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler. return err } + if err := ac.NewAzureClient(c.Context, gitSpec.ID); err != nil { + return err + } + + ok, err := ac.GetAzureKeyVault(conf.Tenant) + if err != nil { + return err + } + + if !ok { + err := ac.CreateAzureKeyVault(conf.Tenant) + if err != nil { + return err + } + } else { + secrets, err := ac.GetAzureSecrets() + if err != nil { + return err + } + + for key, val := range secrets { + zap.S().Infof("download Azure key vault secret %s to %s", + key, filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) + if err := os.WriteFile(filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { + return err + } + } + } + return nil } @@ -432,6 +466,7 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act conf.ClusterProvider = c.String("cluster-provider") conf.ProgressBar = c.Bool("progress-bar") conf.GitHubToken = c.String("github-token") + conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant) zap.S().Infof("RMK will use values for %s environment", conf.Environment) if c.Bool("slack-notifications") { @@ -453,29 +488,22 @@ func configInitAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli.Act if err := initAWSProfile(c, conf, gitSpec); err != nil { return err } - - conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+aws_provider.AWSClusterProvider) case azure_provider.AzureClusterProvider: conf.AwsConfigure = nil conf.GCPConfigure = nil if err := initAzureProfile(c, conf, gitSpec); err != nil { return err } - - conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+azure_provider.AzureClusterProvider) case google_provider.GoogleClusterProvider: conf.AwsConfigure = nil conf.AzureConfigure = nil if err := initGCPProfile(c, conf, gitSpec); err != nil { return err } - - conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+google_provider.GoogleClusterProvider) case util.LocalClusterProvider: conf.AwsConfigure = nil conf.AzureConfigure = nil conf.GCPConfigure = nil - conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant+"-"+util.SopsRootName+"-"+util.LocalClusterProvider) } if err := conf.InitConfig().SetRootDomain(c, gitSpec.ID); err != nil { diff --git a/cmd/flags.go b/cmd/flags.go index 09f2a36..4308c8a 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -63,6 +63,13 @@ func flagsConfig() []cli.Flag { Aliases: []string{"azp"}, EnvVars: []string{"RMK_AZURE_CLIENT_SECRET", "AZURE_CLIENT_SECRET"}, }, + &cli.StringFlag{ + Category: azureFlagsCategory, + Name: "azure-location", + Usage: "Azure location region", + Aliases: []string{"azl"}, + EnvVars: []string{"RMK_AZURE_LOCATION", "AZURE_LOCATION"}, + }, &cli.BoolFlag{ Category: azureFlagsCategory, Name: "azure-service-principle", diff --git a/cmd/release.go b/cmd/release.go index 511064f..bd46fea 100644 --- a/cmd/release.go +++ b/cmd/release.go @@ -163,6 +163,7 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { ) case azure_provider.AzureClusterProvider: envs = append(envs, + "AZURE_LOCATION="+rc.Conf.AzureConfigure.Location, "AZURE_SUBSCRIPTION_ID="+rc.Conf.AzureConfigure.SubscriptionID, ) case google_provider.GoogleClusterProvider: diff --git a/cmd/secret.go b/cmd/secret.go index f2947fd..a5eabf3 100644 --- a/cmd/secret.go +++ b/cmd/secret.go @@ -13,6 +13,9 @@ import ( "gopkg.in/yaml.v3" "rmk/config" + "rmk/providers/aws_provider" + "rmk/providers/azure_provider" + "rmk/providers/google_provider" "rmk/util" ) @@ -150,6 +153,72 @@ func (sc *SecretCommands) CreateKeys() error { return nil } +func (sc *SecretCommands) DownloadKeys() error { + switch sc.Conf.ClusterProvider { + case aws_provider.AWSClusterProvider: + return nil + case azure_provider.AzureClusterProvider: + if err := sc.Conf.NewAzureClient(sc.Ctx.Context, sc.Conf.Name); err != nil { + return err + } + + secrets, err := sc.Conf.GetAzureSecrets() + if err != nil { + return err + } + + for key, val := range secrets { + zap.S().Infof("download Azure key vault secret %s to %s", + key, filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) + if err := os.WriteFile(filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { + return err + } + } + + return nil + case google_provider.GoogleClusterProvider: + return nil + default: + return nil + } +} + +func (sc *SecretCommands) UploadKeys() error { + switch sc.Conf.ClusterProvider { + case aws_provider.AWSClusterProvider: + return nil + case azure_provider.AzureClusterProvider: + if err := sc.Conf.NewAzureClient(sc.Ctx.Context, sc.Conf.Name); err != nil { + return err + } + + walkMatch, err := util.WalkMatch(sc.Conf.SopsAgeKeys, sc.Conf.Tenant+"*"+util.SopsAgeKeyExt) + if err != nil { + return err + } + + for _, val := range walkMatch { + file, err := os.ReadFile(val) + if err != nil { + return err + } + + keyName := strings.TrimSuffix(filepath.Base(val), util.SopsAgeKeyExt) + value := string(file) + + if err := sc.Conf.SetAzureSecret(keyName, value); err != nil { + return err + } + } + + return nil + case google_provider.GoogleClusterProvider: + return nil + default: + return nil + } +} + func (sc *SecretCommands) getOptionFiles(option string) ([]string, error) { var optionFiles, optionPaths []string var check int @@ -420,7 +489,7 @@ func secretKeysDownloadAction(conf *config.Config) cli.ActionFunc { return err } - return conf.DownloadFromBucket("", conf.SopsBucketName, conf.SopsAgeKeys, conf.Tenant) + return newSecretCommands(conf, c, util.GetPwdPath("")).DownloadKeys() } } @@ -430,7 +499,7 @@ func secretKeysUploadAction(conf *config.Config) cli.ActionFunc { return err } - return conf.UploadToBucket(conf.SopsBucketName, conf.SopsAgeKeys, "*"+util.SopsAgeKeyExt) + return newSecretCommands(conf, c, util.GetPwdPath("")).UploadKeys() } } diff --git a/config/config.go b/config/config.go index 40d0ecf..8493676 100644 --- a/config/config.go +++ b/config/config.go @@ -35,7 +35,6 @@ type Config struct { SlackChannel string `yaml:"slack-channel,omitempty"` SlackMsgDetails []string `yaml:"slack-message-details,omitempty"` SopsAgeKeys string `yaml:"sops-age-keys,omitempty"` - SopsBucketName string `yaml:"sops-bucket-name,omitempty"` AWSMFAProfile string `yaml:"aws-mfa-profile,omitempty"` AWSMFATokenExpiration string `yaml:"aws-mfa-token-expiration,omitempty"` *aws_provider.AwsConfigure `yaml:"aws,omitempty"` diff --git a/go.mod b/go.mod index 299685b..699da08 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,12 @@ toolchain go1.22.8 require ( cloud.google.com/go/auth v0.10.2 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 + github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.1.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.4.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 github.com/Masterminds/semver v1.5.0 github.com/Masterminds/sprig/v3 v3.2.3 github.com/aws/aws-sdk-go-v2 v1.32.3 @@ -45,8 +49,8 @@ require ( cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/storage v1.39.1 // indirect dario.cat/mergo v1.0.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/BurntSushi/toml v1.4.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect diff --git a/go.sum b/go.sum index f2b253b..de55e31 100644 --- a/go.sum +++ b/go.sum @@ -195,12 +195,22 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0 h1:xnO4sFyG8UH2fElBkcqLTOZsAajvKfnSlgBBW8dXYjw= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0/go.mod h1:XD3DIOOVgBCO03OleB1fHjgktVRFxlT++KwKgIOewdM= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0 h1:5n7dPVqsWfVKw+ZiEKSd3Kzu7gwBkbEBkeXb8rgaE9Q= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0/go.mod h1:HcZY0PHPo/7d75p99lB6lK0qYOP4vLRJUBpiehYXtLQ= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.1.0 h1:FvuejXWdMIPK6sY0Tt3lgb45BCVybrvmmnGCEC7a1i4= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.1.0/go.mod h1:drbnYtukMoZqUQq9hJASf41w3RB4VoTJPoPpe+XDHPU= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.4.0 h1:HlZMUZW8S4P9oob1nCHxCCKrytxyLc+24nUJGssoEto= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.4.0/go.mod h1:StGsLbuJh06Bd8IBfnAlIFV3fLb+gkczONWf15hpX2E= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= diff --git a/providers/azure_provider/azure.go b/providers/azure_provider/azure.go index 103dac6..0744096 100644 --- a/providers/azure_provider/azure.go +++ b/providers/azure_provider/azure.go @@ -1,14 +1,22 @@ package azure_provider import ( + "crypto/sha1" "encoding/json" + "errors" "fmt" "os" "reflect" "strings" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" + "go.uber.org/zap" "golang.org/x/net/context" "rmk/util" @@ -27,16 +35,26 @@ type AzureRawServicePrincipal struct { Tenant string `json:"tenant"` } +type AzureKeyVault struct { + KeyVaultName string `json:"-" yaml:"key-vault-name,omitempty"` + KeyVaultURI string `json:"-" yaml:"key-vault-uri,omitempty"` + ResourceGroupName string `json:"-" yaml:"resource-group-name,omitempty"` +} + type AzureClient struct { + Credentials *azidentity.ClientSecretCredential `json:"-" yaml:"-"` + Ctx context.Context `json:"-" yaml:"-"` + GroupClient *armresources.ResourceGroupsClient `json:"-" yaml:"-"` ManagedClustersClient *armcontainerservice.ManagedClustersClient `json:"-" yaml:"-"` - //SSHPublicKeysClient *armcompute.SSHPublicKeysClient `json:"-" yaml:"-"` - Ctx context.Context `json:"-" yaml:"-"` + VaultsClient *armkeyvault.VaultsClient `json:"-" yaml:"-"` } type AzureConfigure struct { AzureClient `json:"-" yaml:"-"` + AzureKeyVault `json:"-" yaml:"key-vault,omitempty"` ClientID string `json:"client-id,omitempty" yaml:"-"` ClientSecret string `json:"client-secret,omitempty" yaml:"-"` + Location string `json:"location,omitempty" yaml:"location,omitempty"` SubscriptionID string `json:"subscription-id,omitempty" yaml:"subscription-id,omitempty"` TenantID string `json:"tenant-id,omitempty" yaml:"-"` } @@ -63,6 +81,10 @@ func getTagStructName(i interface{}, name string) error { } } +func generateKeyVaultName(tenant string) string { + return "kv-" + fmt.Sprintf("%x", sha1.Sum([]byte(tenant)))[0:16] +} + func (ac *AzureConfigure) ValidateSPCredentials() error { if len(ac.ClientID) == 0 { return getTagStructName(ac, "ClientID") @@ -72,6 +94,10 @@ func (ac *AzureConfigure) ValidateSPCredentials() error { return getTagStructName(ac, "ClientSecret") } + if len(ac.Location) == 0 { + return getTagStructName(ac, "Location") + } + if len(ac.SubscriptionID) == 0 { return getTagStructName(ac, "SubscriptionID") } @@ -109,7 +135,7 @@ func (ac *AzureConfigure) WriteSPCredentials(fileSuffix string) error { data, 0644) } -func (ac *AzureConfigure) NewAzureManagedClustersClient(ctx context.Context, fileName string) error { +func (ac *AzureConfigure) NewAzureClient(ctx context.Context, fileName string) error { if err := ac.ReadSPCredentials(fileName); err != nil { return err } @@ -119,19 +145,26 @@ func (ac *AzureConfigure) NewAzureManagedClustersClient(ctx context.Context, fil return err } + GroupFactory, err := armresources.NewClientFactory(ac.SubscriptionID, cred, nil) + if err != nil { + return err + } + ManagedClustersFactory, err := armcontainerservice.NewClientFactory(ac.SubscriptionID, cred, nil) if err != nil { return err } - //SSHPublicKeysFactory, err := armcompute.NewClientFactory(ac.SubscriptionID, cred, nil) - //if err != nil { - // return err - //} + VaultFactory, err := armkeyvault.NewClientFactory(ac.SubscriptionID, cred, nil) + if err != nil { + return err + } ac.Ctx = ctx + ac.Credentials = cred + ac.GroupClient = GroupFactory.NewResourceGroupsClient() + ac.VaultsClient = VaultFactory.NewVaultsClient() ac.ManagedClustersClient = ManagedClustersFactory.NewManagedClustersClient() - //ac.SSHPublicKeysClient = SSHPublicKeysFactory.NewSSHPublicKeysClient() return nil } @@ -153,17 +186,191 @@ func (ac *AzureConfigure) GetAzureClusterContext(groupName, clusterName string) cpTitle, clusterName) } -//func (ac *AzureConfigure) CreateVMSSHKey(groupName, clusterName string) ([]byte, error) { -// res, err := ac.SSHPublicKeysClient.GenerateKeyPair(ac.Ctx, groupName, clusterName, -// &armcompute.SSHPublicKeysClientGenerateKeyPairOptions{ -// Parameters: &armcompute.SSHGenerateKeyPairInputParameters{ -// EncryptionType: to.Ptr(armcompute.SSHEncryptionTypesEd25519), -// }, -// }, -// ) -// if err != nil { -// return nil, err -// } -// -// return res.MarshalJSON() -//} +func (ac *AzureConfigure) createKeyVaultResourceGroup(tenant string) error { + params := armresources.ResourceGroup{ + Location: to.Ptr(ac.Location), + } + + update, err := ac.GroupClient.CreateOrUpdate(ac.Ctx, tenant+"-"+util.SopsRootName, params, nil) + if err != nil { + var respErr *azcore.ResponseError + if errors.As(err, &respErr) && respErr.StatusCode == 403 { + zap.S().Warnf("permissions denied to create Azure resource group: %s", tenant+"-"+util.SopsRootName) + return nil + } + + return err + } + + ac.ResourceGroupName = *update.Name + + zap.S().Infof("created Azure resource group: %s", ac.ResourceGroupName) + + return nil +} + +func (ac *AzureConfigure) existsKeyVaultResourceGroup(tenant string) (bool, error) { + existence, err := ac.GroupClient.CheckExistence(ac.Ctx, tenant+"-"+util.SopsRootName, nil) + if err != nil { + var respErr *azcore.ResponseError + if errors.As(err, &respErr) && respErr.StatusCode == 403 { + zap.S().Warnf("permissions denied to check existence Azure resource group: %s", + tenant+"-"+util.SopsRootName) + return false, nil + } + + return false, err + } + + return existence.Success, nil +} + +func (ac *AzureConfigure) CreateAzureKeyVault(tenant string) error { + if err := ac.createKeyVaultResourceGroup(tenant); err != nil { + return err + } + + if len(ac.ResourceGroupName) == 0 { + ac.ResourceGroupName = tenant + "-" + util.SopsRootName + } + + ac.KeyVaultName = generateKeyVaultName(tenant) + + params := armkeyvault.VaultCreateOrUpdateParameters{ + Location: to.Ptr(ac.Location), + Properties: &armkeyvault.VaultProperties{ + EnableRbacAuthorization: to.Ptr(true), + SKU: &armkeyvault.SKU{ + Family: to.Ptr(armkeyvault.SKUFamilyA), + Name: to.Ptr(armkeyvault.SKUNameStandard), + }, + TenantID: to.Ptr(ac.TenantID), + }, + } + + update, err := ac.VaultsClient.BeginCreateOrUpdate(ac.Ctx, ac.ResourceGroupName, ac.KeyVaultName, params, nil) + if err != nil { + var respErr *azcore.ResponseError + if errors.As(err, &respErr) && respErr.StatusCode == 403 { + zap.S().Warnf("permissions denied to create Azure key vault: %s", + ac.KeyVaultName) + return nil + } + + return err + } + + result, err := update.PollUntilDone(ac.Ctx, nil) + if err != nil { + return err + } + + ac.KeyVaultURI = *result.Properties.VaultURI + + zap.S().Infof("created Azure key vault: %s, %s", ac.KeyVaultName, ac.KeyVaultURI) + + return nil +} + +func (ac *AzureConfigure) GetAzureKeyVault(tenant string) (bool, error) { + if ok, err := ac.existsKeyVaultResourceGroup(tenant); err != nil { + return false, err + } else if !ok { + return false, nil + } + + ac.KeyVaultName = generateKeyVaultName(tenant) + ac.ResourceGroupName = tenant + "-" + util.SopsRootName + + resp, err := ac.VaultsClient.Get(ac.Ctx, tenant+"-"+util.SopsRootName, ac.KeyVaultName, nil) + if err != nil { + var respErr *azcore.ResponseError + if errors.As(err, &respErr) && respErr.StatusCode == 404 { + return false, nil + } + + if errors.As(err, &respErr) && respErr.StatusCode == 403 { + zap.S().Warnf("permissions denied to get Azure Key Vault: %s", ac.KeyVaultName) + return false, nil + } + + return false, err + } + + ac.KeyVaultURI = *resp.Properties.VaultURI + + return true, nil +} + +func (ac *AzureConfigure) SetAzureSecret(keyName, value string) error { + client, err := azsecrets.NewClient(ac.KeyVaultURI, ac.Credentials, nil) + if err != nil { + return err + } + + params := azsecrets.SetSecretParameters{ + Value: to.Ptr(value), + } + + secret, err := client.SetSecret(ac.Ctx, keyName, params, nil) + if err != nil { + var respErr *azcore.ResponseError + if errors.As(err, &respErr) && respErr.StatusCode == 403 { + zap.S().Warnf("permissions denied to create Azure key vault secret: %s", keyName) + return nil + } + + return err + } + + zap.S().Infof("created Azure key vault secret: %s, %s", keyName, *secret.ID) + + return nil +} + +func (ac *AzureConfigure) GetAzureSecrets() (map[string][]byte, error) { + var secrets = make(map[string][]byte) + + client, err := azsecrets.NewClient(ac.KeyVaultURI, ac.Credentials, nil) + if err != nil { + return nil, err + } + + listSecrets := client.NewListSecretsPager(nil) + if err != nil { + return nil, err + } + + for listSecrets.More() { + page, err := listSecrets.NextPage(ac.Ctx) + if err != nil { + var respErr *azcore.ResponseError + if errors.As(err, &respErr) && respErr.StatusCode == 403 { + zap.S().Warnf("permissions denied to get list of Azure key vault secrets") + return nil, nil + } + + return nil, err + } + + for _, val := range page.Value { + name := val.ID.Name() + version := val.ID.Version() + + secret, err := client.GetSecret(ac.Ctx, name, version, nil) + if err != nil { + var respErr *azcore.ResponseError + if errors.As(err, &respErr) && respErr.StatusCode == 403 { + zap.S().Warnf("permissions denied to get of Azure key vault secret: %s", name) + return nil, nil + } + + return nil, err + } + + secrets[secret.ID.Name()] = []byte(*secret.Value) + } + } + + return secrets, nil +} diff --git a/providers/google_provider/gcp.go b/providers/google_provider/gcp.go index 37d21f6..d2e0345 100644 --- a/providers/google_provider/gcp.go +++ b/providers/google_provider/gcp.go @@ -64,6 +64,35 @@ func (gcp *GCPConfigure) CopySACredentials(fileSuffix string) error { gcp.AppCredentials.JSON(), 0644) } +//func (gcp *GCPConfigure) CreateGateway() error { +// if err := gcp.ReadSACredentials(); err != nil { +// return err +// } +// +// client, err := compute.NewService(gcp.Ctx, option.WithAuthCredentials(gcp.AppCredentials)) +// if err != nil { +// return err +// } +// +// client.BasePath = compute.CloudPlatformScope + "/v1" +// +// fmt.Printf("%#v\n", *client) +// +// req := client.Routers.List(gcp.ProjectID, "europe-central2") +// if err := req.Pages(gcp.Ctx, func(page *compute.RouterList) error { +// for _, router := range page.Items { +// // process each `router` resource: +// fmt.Printf("%#v\n", router) +// // NAT Gateways are found in router.nats +// } +// return nil +// }); err != nil { +// log.Fatal(err) +// } +// +// return nil +//} + func (gcp *GCPConfigure) GetGCPClusterContext(clusterName string) ([]byte, error) { var cluster *container.Cluster From e37b7e39364682f0ce2aa2ed083d7ff6d53b1854 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 11 Dec 2024 10:41:52 +0100 Subject: [PATCH 29/39] #29 - refactoring --- providers/azure_provider/azure.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/providers/azure_provider/azure.go b/providers/azure_provider/azure.go index 0744096..4395456 100644 --- a/providers/azure_provider/azure.go +++ b/providers/azure_provider/azure.go @@ -195,7 +195,7 @@ func (ac *AzureConfigure) createKeyVaultResourceGroup(tenant string) error { if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permissions denied to create Azure resource group: %s", tenant+"-"+util.SopsRootName) + zap.S().Warnf("permission denied to create Azure resource group: %s", tenant+"-"+util.SopsRootName) return nil } @@ -214,7 +214,7 @@ func (ac *AzureConfigure) existsKeyVaultResourceGroup(tenant string) (bool, erro if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permissions denied to check existence Azure resource group: %s", + zap.S().Warnf("permission denied to check existence of Azure resource group: %s", tenant+"-"+util.SopsRootName) return false, nil } @@ -252,7 +252,7 @@ func (ac *AzureConfigure) CreateAzureKeyVault(tenant string) error { if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permissions denied to create Azure key vault: %s", + zap.S().Warnf("permission denied to create Azure key vault: %s", ac.KeyVaultName) return nil } @@ -290,7 +290,7 @@ func (ac *AzureConfigure) GetAzureKeyVault(tenant string) (bool, error) { } if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permissions denied to get Azure Key Vault: %s", ac.KeyVaultName) + zap.S().Warnf("permission denied to get Azure key vault: %s", ac.KeyVaultName) return false, nil } @@ -316,7 +316,7 @@ func (ac *AzureConfigure) SetAzureSecret(keyName, value string) error { if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permissions denied to create Azure key vault secret: %s", keyName) + zap.S().Warnf("permission denied to create Azure key vault secret: %s", keyName) return nil } @@ -346,7 +346,7 @@ func (ac *AzureConfigure) GetAzureSecrets() (map[string][]byte, error) { if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permissions denied to get list of Azure key vault secrets") + zap.S().Warnf("permission denied to list Azure key vault secrets") return nil, nil } @@ -361,7 +361,7 @@ func (ac *AzureConfigure) GetAzureSecrets() (map[string][]byte, error) { if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permissions denied to get of Azure key vault secret: %s", name) + zap.S().Warnf("permission denied to get of Azure key vault secret: %s", name) return nil, nil } From 92ed29eb60cba5b0571cb309f1a7eead6c66391a Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 11 Dec 2024 11:03:26 +0100 Subject: [PATCH 30/39] #29 - refactoring --- cmd/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/flags.go b/cmd/flags.go index 4308c8a..cc24b04 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -66,7 +66,7 @@ func flagsConfig() []cli.Flag { &cli.StringFlag{ Category: azureFlagsCategory, Name: "azure-location", - Usage: "Azure location region", + Usage: "Azure location", Aliases: []string{"azl"}, EnvVars: []string{"RMK_AZURE_LOCATION", "AZURE_LOCATION"}, }, From 81db18846eb037f672d25b4cec7914eabd636f1b Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Mon, 16 Dec 2024 10:56:31 +0100 Subject: [PATCH 31/39] #29 - implement support GCP secrets manager for store SOPS Age keys --- cmd/cluster.go | 15 +- cmd/cluster_capg.go | 48 +++++- cmd/cluster_capz.go | 2 +- cmd/config.go | 22 ++- cmd/flags.go | 9 ++ cmd/release.go | 5 +- cmd/secret.go | 35 +++++ config/config.go | 1 + go.mod | 3 +- go.sum | 2 + providers/azure_provider/azure.go | 52 +++---- providers/google_provider/gcp.go | 243 +++++++++++++++++++++++++----- 12 files changed, 362 insertions(+), 75 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index 3cc7f30..264e045 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -341,12 +341,13 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { return err } case google_provider.GoogleClusterProvider: - // Pre-provision hook for GCP - //if err := cc.createGCPNATGateway(); err != nil { - // return err - //} + if err := cc.createGCPNATGateway(); err != nil { + return err + } - //return nil + if err := cc.createGCPSecrets(); err != nil { + return err + } } cc.SpecCMD = cc.prepareHelmfile("--log-level", "error", "-l", "cluster="+cc.Conf.ClusterProvider, "sync") @@ -397,7 +398,9 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { case azure_provider.AzureClusterProvider: // Pre-destroy hook for Azure case google_provider.GoogleClusterProvider: - // Pre-destroy hook for GCP + if err := cc.deleteGCPNATGateway(); err != nil { + return err + } } kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( diff --git a/cmd/cluster_capg.go b/cmd/cluster_capg.go index 57acdd7..6064e2d 100644 --- a/cmd/cluster_capg.go +++ b/cmd/cluster_capg.go @@ -3,11 +3,13 @@ package cmd import ( "os" "path/filepath" + "strings" corev1 "k8s.io/api/core/v1" v1 "k8s.io/client-go/applyconfigurations/core/v1" "rmk/providers/google_provider" + "rmk/util" ) const ( @@ -86,7 +88,45 @@ func (cc *ClusterCommands) getGCPClusterContext() ([]byte, error) { GetGCPClusterContext(cc.Conf.Name) } -//func (cc *ClusterCommands) createGCPNATGateway() error { -// return google_provider.NewGCPConfigure(cc.Ctx.Context, cc.Conf.GCPConfigure.AppCredentialsPath). -// CreateGateway() -//} +func (cc *ClusterCommands) createGCPNATGateway() error { + return google_provider.NewGCPConfigure(cc.Ctx.Context, cc.Conf.GCPConfigure.AppCredentialsPath). + CreateGCPCloudNATGateway(cc.Conf.GCPRegion) +} + +func (cc *ClusterCommands) deleteGCPNATGateway() error { + return google_provider.NewGCPConfigure(cc.Ctx.Context, cc.Conf.GCPConfigure.AppCredentialsPath). + DeleteGCPCloudNATGateway(cc.Conf.GCPRegion) +} + +func (cc *ClusterCommands) createGCPSecrets() error { + gcp := google_provider.NewGCPConfigure(cc.Ctx.Context, cc.Conf.GCPConfigure.AppCredentialsPath) + + secrets, err := gcp.GetGCPSecrets(cc.Conf.Tenant) + if err != nil { + return err + } + + if len(secrets) > 0 { + return nil + } + + walkMatch, err := util.WalkMatch(cc.Conf.SopsAgeKeys, cc.Conf.Tenant+"*"+util.SopsAgeKeyExt) + if err != nil { + return err + } + + for _, val := range walkMatch { + file, err := os.ReadFile(val) + if err != nil { + return err + } + + keyName := strings.TrimSuffix(filepath.Base(val), util.SopsAgeKeyExt) + + if err := gcp.SetGCPSecret(cc.Conf.Tenant, cc.Conf.GCPRegion, keyName, file); err != nil { + return err + } + } + + return nil +} diff --git a/cmd/cluster_capz.go b/cmd/cluster_capz.go index d0e5b4f..de1bae8 100644 --- a/cmd/cluster_capz.go +++ b/cmd/cluster_capz.go @@ -160,7 +160,7 @@ func (cc *ClusterCommands) createAzureSecrets(ac *azure_provider.AzureConfigure) } if len(secrets) > 0 { - return err + return nil } walkMatch, err := util.WalkMatch(cc.Conf.SopsAgeKeys, cc.Conf.Tenant+"*"+util.SopsAgeKeyExt) diff --git a/cmd/config.go b/cmd/config.go index 8cdd4ff..984ed10 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -382,7 +382,7 @@ func initAzureProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler. func initGCPProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.GitSpec) error { gcp := google_provider.NewGCPConfigure(c.Context, - util.GetHomePath(google_provider.GoogleHomeDir, google_provider.GooglePrefix+gitSpec.ID+".json")) + util.GetHomePath(google_provider.GoogleHomeDir, google_provider.GoogleCredentialsPrefix+gitSpec.ID+".json")) if c.IsSet("google-application-credentials") { gcp.AppCredentialsPath = c.String("google-application-credentials") @@ -399,8 +399,26 @@ func initGCPProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi } } + if !c.IsSet("gcp-region") { + return fmt.Errorf("GCP provider option gcp-region required") + } + + conf.GCPRegion = c.String("gcp-region") conf.GCPConfigure = gcp + secrets, err := gcp.GetGCPSecrets(conf.Tenant) + if err != nil { + return err + } + + for key, val := range secrets { + zap.S().Infof("download GCP secret %s to %s", + key, filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) + if err := os.WriteFile(filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { + return err + } + } + return nil } @@ -439,7 +457,7 @@ func configDeleteAction(conf *config.Config) cli.ActionFunc { } case c.String("cluster-provider") == google_provider.GoogleClusterProvider: if err := os.RemoveAll(util.GetHomePath(google_provider.GoogleHomeDir, - google_provider.GooglePrefix+conf.Name+".json")); err != nil { + google_provider.GoogleCredentialsPrefix+conf.Name+".json")); err != nil { return err } } diff --git a/cmd/flags.go b/cmd/flags.go index cc24b04..4d413e1 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -111,6 +111,15 @@ func flagsConfig() []cli.Flag { EnvVars: []string{"RMK_GITHUB_TOKEN"}, }, ), + altsrc.NewStringFlag( + &cli.StringFlag{ + Category: gcpFlagsCategory, + Name: "gcp-region", + Usage: "GCP region", + Aliases: []string{"gr"}, + EnvVars: []string{"RMK_GCP_REGION", "GCP_REGION"}, + }, + ), &cli.StringFlag{ Category: gcpFlagsCategory, Name: "google-application-credentials", diff --git a/cmd/release.go b/cmd/release.go index bd46fea..fb4a706 100644 --- a/cmd/release.go +++ b/cmd/release.go @@ -157,8 +157,10 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { switch rc.Conf.ClusterProvider { case aws_provider.AWSClusterProvider: envs = append(envs, - "AWS_PROFILE="+rc.Conf.Profile, + "AWS_ACCOUNT_ID="+rc.Conf.AccountID, "AWS_CONFIG_FILE="+strings.Join(rc.Conf.AWSSharedConfigFile(rc.Conf.Profile), ""), + "AWS_PROFILE="+rc.Conf.Profile, + "AWS_REGION="+rc.Conf.Region, "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(rc.Conf.AWSSharedCredentialsFile(rc.Conf.Profile), ""), ) case azure_provider.AzureClusterProvider: @@ -169,6 +171,7 @@ func (rc *ReleaseCommands) prepareHelmfile(args ...string) *util.SpecCMD { case google_provider.GoogleClusterProvider: envs = append(envs, "GCP_PROJECT_ID="+rc.Conf.GCPConfigure.ProjectID, + "GCP_REGION="+rc.Conf.GCPRegion, "GOOGLE_APPLICATION_CREDENTIALS="+rc.Conf.GCPConfigure.AppCredentialsPath, ) } diff --git a/cmd/secret.go b/cmd/secret.go index a5eabf3..0f58afc 100644 --- a/cmd/secret.go +++ b/cmd/secret.go @@ -177,6 +177,21 @@ func (sc *SecretCommands) DownloadKeys() error { return nil case google_provider.GoogleClusterProvider: + gcp := google_provider.NewGCPConfigure(sc.Ctx.Context, sc.Conf.GCPConfigure.AppCredentialsPath) + + secrets, err := gcp.GetGCPSecrets(sc.Conf.Tenant) + if err != nil { + return err + } + + for key, val := range secrets { + zap.S().Infof("download GCP secret %s to %s", + key, filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) + if err := os.WriteFile(filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { + return err + } + } + return nil default: return nil @@ -213,6 +228,26 @@ func (sc *SecretCommands) UploadKeys() error { return nil case google_provider.GoogleClusterProvider: + gcp := google_provider.NewGCPConfigure(sc.Ctx.Context, sc.Conf.GCPConfigure.AppCredentialsPath) + + walkMatch, err := util.WalkMatch(sc.Conf.SopsAgeKeys, sc.Conf.Tenant+"*"+util.SopsAgeKeyExt) + if err != nil { + return err + } + + for _, val := range walkMatch { + file, err := os.ReadFile(val) + if err != nil { + return err + } + + keyName := strings.TrimSuffix(filepath.Base(val), util.SopsAgeKeyExt) + + if err := gcp.SetGCPSecret(sc.Conf.Tenant, sc.Conf.GCPRegion, keyName, file); err != nil { + return err + } + } + return nil default: return nil diff --git a/config/config.go b/config/config.go index 8493676..0f05425 100644 --- a/config/config.go +++ b/config/config.go @@ -37,6 +37,7 @@ type Config struct { SopsAgeKeys string `yaml:"sops-age-keys,omitempty"` AWSMFAProfile string `yaml:"aws-mfa-profile,omitempty"` AWSMFATokenExpiration string `yaml:"aws-mfa-token-expiration,omitempty"` + GCPRegion string `yaml:"gcp-region,omitempty"` *aws_provider.AwsConfigure `yaml:"aws,omitempty"` *azure_provider.AzureConfigure `yaml:"azure,omitempty"` *google_provider.GCPConfigure `yaml:"gcp,omitempty"` diff --git a/go.mod b/go.mod index 699da08..9a4da6e 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ toolchain go1.22.8 require ( cloud.google.com/go/auth v0.10.2 + cloud.google.com/go/secretmanager v1.11.5 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0 @@ -27,6 +28,7 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/go-git/go-git/v5 v5.11.0 github.com/google/go-github v17.0.0+incompatible + github.com/googleapis/gax-go/v2 v2.12.3 github.com/hashicorp/go-getter v1.7.3 github.com/slack-go/slack v0.12.3 github.com/urfave/cli/v2 v2.27.1 @@ -98,7 +100,6 @@ require ( github.com/google/s2a-go v0.1.8 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect - github.com/googleapis/gax-go/v2 v2.12.3 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect diff --git a/go.sum b/go.sum index de55e31..72de160 100644 --- a/go.sum +++ b/go.sum @@ -156,6 +156,8 @@ cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92 cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.11.5 h1:82fpF5vBBvu9XW4qj0FU2C6qVMtj1RM/XHwKXUEAfYY= +cloud.google.com/go/secretmanager v1.11.5/go.mod h1:eAGv+DaCHkeVyQi0BeXgAHOU0RdrMeZIASKc+S7VqH4= cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= diff --git a/providers/azure_provider/azure.go b/providers/azure_provider/azure.go index 4395456..fe0921b 100644 --- a/providers/azure_provider/azure.go +++ b/providers/azure_provider/azure.go @@ -302,32 +302,6 @@ func (ac *AzureConfigure) GetAzureKeyVault(tenant string) (bool, error) { return true, nil } -func (ac *AzureConfigure) SetAzureSecret(keyName, value string) error { - client, err := azsecrets.NewClient(ac.KeyVaultURI, ac.Credentials, nil) - if err != nil { - return err - } - - params := azsecrets.SetSecretParameters{ - Value: to.Ptr(value), - } - - secret, err := client.SetSecret(ac.Ctx, keyName, params, nil) - if err != nil { - var respErr *azcore.ResponseError - if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permission denied to create Azure key vault secret: %s", keyName) - return nil - } - - return err - } - - zap.S().Infof("created Azure key vault secret: %s, %s", keyName, *secret.ID) - - return nil -} - func (ac *AzureConfigure) GetAzureSecrets() (map[string][]byte, error) { var secrets = make(map[string][]byte) @@ -374,3 +348,29 @@ func (ac *AzureConfigure) GetAzureSecrets() (map[string][]byte, error) { return secrets, nil } + +func (ac *AzureConfigure) SetAzureSecret(keyName, value string) error { + client, err := azsecrets.NewClient(ac.KeyVaultURI, ac.Credentials, nil) + if err != nil { + return err + } + + params := azsecrets.SetSecretParameters{ + Value: to.Ptr(value), + } + + secret, err := client.SetSecret(ac.Ctx, keyName, params, nil) + if err != nil { + var respErr *azcore.ResponseError + if errors.As(err, &respErr) && respErr.StatusCode == 403 { + zap.S().Warnf("permission denied to create Azure key vault secret: %s", keyName) + return nil + } + + return err + } + + zap.S().Infof("created Azure key vault secret: %s, %s", keyName, *secret.ID) + + return nil +} diff --git a/providers/google_provider/gcp.go b/providers/google_provider/gcp.go index d2e0345..2dbedcb 100644 --- a/providers/google_provider/gcp.go +++ b/providers/google_provider/gcp.go @@ -3,13 +3,23 @@ package google_provider import ( "context" "encoding/base64" + "errors" "fmt" + "hash/crc32" "os" + "path/filepath" "strings" "cloud.google.com/go/auth" "cloud.google.com/go/auth/credentials" + secretmanager "cloud.google.com/go/secretmanager/apiv1" + "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb" + "github.com/googleapis/gax-go/v2/apierror" + "go.uber.org/zap" + "google.golang.org/api/compute/v1" container "google.golang.org/api/container/v1beta1" + "google.golang.org/api/googleapi" + "google.golang.org/api/iterator" "google.golang.org/api/option" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" @@ -18,9 +28,15 @@ import ( ) const ( - GoogleClusterProvider = "gcp" - GoogleHomeDir = ".config/gcloud" - GooglePrefix = "gcp-credentials-" + GoogleClusterProvider = "gcp" + GoogleCredentialsPrefix = "gcp-credentials-" + GoogleHomeDir = ".config/gcloud" + + // List of APIs errors + apiErrorAlreadyExists = "alreadyExists" + apiErrorNotFound = "notFound" + gRPCErrorAlreadyExists = "AlreadyExists" + gRPCErrorPermissionDenied = "PermissionDenied" ) type GCPConfigure struct { @@ -35,6 +51,10 @@ func NewGCPConfigure(ctx context.Context, appCredentialsPath string) *GCPConfigu } func (gcp *GCPConfigure) ReadSACredentials() error { + if !util.IsExists(gcp.AppCredentialsPath, true) { + return fmt.Errorf("google application credentials JSON file for GCP not found") + } + data, err := os.ReadFile(gcp.AppCredentialsPath) if err != nil { return err @@ -58,40 +78,195 @@ func (gcp *GCPConfigure) CopySACredentials(fileSuffix string) error { return err } - gcp.AppCredentialsPath = util.GetHomePath(GoogleHomeDir, GooglePrefix+fileSuffix+".json") + gcp.AppCredentialsPath = util.GetHomePath(GoogleHomeDir, GoogleCredentialsPrefix+fileSuffix+".json") - return os.WriteFile(util.GetHomePath(GoogleHomeDir, GooglePrefix+fileSuffix+".json"), + return os.WriteFile(util.GetHomePath(GoogleHomeDir, GoogleCredentialsPrefix+fileSuffix+".json"), gcp.AppCredentials.JSON(), 0644) } -//func (gcp *GCPConfigure) CreateGateway() error { -// if err := gcp.ReadSACredentials(); err != nil { -// return err -// } -// -// client, err := compute.NewService(gcp.Ctx, option.WithAuthCredentials(gcp.AppCredentials)) -// if err != nil { -// return err -// } -// -// client.BasePath = compute.CloudPlatformScope + "/v1" -// -// fmt.Printf("%#v\n", *client) -// -// req := client.Routers.List(gcp.ProjectID, "europe-central2") -// if err := req.Pages(gcp.Ctx, func(page *compute.RouterList) error { -// for _, router := range page.Items { -// // process each `router` resource: -// fmt.Printf("%#v\n", router) -// // NAT Gateways are found in router.nats -// } -// return nil -// }); err != nil { -// log.Fatal(err) -// } -// -// return nil -//} +func (gcp *GCPConfigure) GetGCPSecrets(tenant string) (map[string][]byte, error) { + var secrets = make(map[string][]byte) + + if err := gcp.ReadSACredentials(); err != nil { + return nil, err + } + + client, err := secretmanager.NewClient(gcp.Ctx, option.WithCredentialsJSON(gcp.AppCredentials.JSON())) + if err != nil { + return nil, err + } + + defer client.Close() + + listReq := &secretmanagerpb.ListSecretsRequest{ + Parent: fmt.Sprintf("projects/%s", gcp.ProjectID), + Filter: "labels.resource-group=" + tenant + "-" + util.SopsRootName, + } + + it := client.ListSecrets(gcp.Ctx, listReq) + for { + resp, err := it.Next() + if errors.Is(err, iterator.Done) { + break + } + + if err != nil { + var respError *apierror.APIError + if errors.As(err, &respError) && respError.GRPCStatus().Code().String() == gRPCErrorPermissionDenied { + zap.S().Warnf("permission denied to list GCP secrets") + return nil, nil + } else { + return nil, err + } + } + + accReq := &secretmanagerpb.AccessSecretVersionRequest{Name: resp.Name + "/versions/latest"} + result, err := client.AccessSecretVersion(gcp.Ctx, accReq) + if err != nil { + var respError *apierror.APIError + if errors.As(err, &respError) && respError.GRPCStatus().Code().String() == gRPCErrorPermissionDenied { + zap.S().Warnf("permission denied to get access for GCP secrets values") + return nil, nil + } else { + return nil, err + } + } + + crc32c := crc32.MakeTable(crc32.Castagnoli) + checksum := int64(crc32.Checksum(result.Payload.Data, crc32c)) + if checksum != *result.Payload.DataCrc32C { + return nil, fmt.Errorf("data corruption detected for GCP secrets value: %s", resp.Name) + } + + secrets[filepath.Base(resp.Name)] = result.Payload.Data + } + + return secrets, nil +} + +func (gcp *GCPConfigure) SetGCPSecret(tenant, region, keyName string, value []byte) error { + if err := gcp.ReadSACredentials(); err != nil { + return err + } + + client, err := secretmanager.NewClient(gcp.Ctx, option.WithCredentialsJSON(gcp.AppCredentials.JSON())) + if err != nil { + return err + } + + defer client.Close() + + secretReq := &secretmanagerpb.CreateSecretRequest{ + Parent: fmt.Sprintf("projects/%s", gcp.ProjectID), + SecretId: keyName, + Secret: &secretmanagerpb.Secret{ + Replication: &secretmanagerpb.Replication{ + Replication: &secretmanagerpb.Replication_UserManaged_{ + UserManaged: &secretmanagerpb.Replication_UserManaged{ + Replicas: []*secretmanagerpb.Replication_UserManaged_Replica{{Location: region}}, + }, + }, + }, + Labels: map[string]string{"resource-group": tenant + "-" + util.SopsRootName}, + }, + } + + _, err = client.CreateSecret(gcp.Ctx, secretReq) + if err != nil { + var respError *apierror.APIError + if errors.As(err, &respError) && respError.GRPCStatus().Code().String() == gRPCErrorPermissionDenied { + zap.S().Warnf("permission denied to create GCP secret: %s", keyName) + } else if respError.GRPCStatus().Code().String() != gRPCErrorAlreadyExists { + return err + } + } + + secretVersionReq := &secretmanagerpb.AddSecretVersionRequest{ + Parent: fmt.Sprintf("projects/%s/secrets/%s", gcp.ProjectID, keyName), + Payload: &secretmanagerpb.SecretPayload{Data: value}, + } + + version, err := client.AddSecretVersion(gcp.Ctx, secretVersionReq) + if err != nil { + var respError *apierror.APIError + if errors.As(err, &respError) && respError.GRPCStatus().Code().String() == gRPCErrorPermissionDenied { + zap.S().Warnf("permission denied to add to secret %s value", keyName) + } else { + return err + } + } + + zap.S().Infof("created Azure key vault secret: %s, %s", keyName, version.Name) + + return nil +} + +func (gcp *GCPConfigure) CreateGCPCloudNATGateway(region string) error { + if err := gcp.ReadSACredentials(); err != nil { + return err + } + + client, err := compute.NewService(gcp.Ctx, option.WithCredentialsJSON(gcp.AppCredentials.JSON())) + if err != nil { + return err + } + + routerNat := &compute.RouterNat{ + AutoNetworkTier: "STANDARD", + EndpointTypes: []string{"ENDPOINT_TYPE_VM"}, + Name: "default-nat-" + region, + NatIpAllocateOption: "AUTO_ONLY", + SourceSubnetworkIpRangesToNat: "ALL_SUBNETWORKS_ALL_IP_RANGES", + Type: "PUBLIC", + } + + router := &compute.Router{ + Name: "default-router-" + region, + Nats: []*compute.RouterNat{routerNat}, + Network: "projects/" + gcp.ProjectID + "/global/networks/default", + } + + _, err = client.Routers.Insert(gcp.ProjectID, region, router).Context(gcp.Ctx).Do() + if err != nil { + var respError *googleapi.Error + if errors.As(err, &respError) && respError.Code == 409 && respError.Errors[0].Reason == apiErrorAlreadyExists { + zap.S().Infof("GCP router %s with router NAT %s already exists for region %s", + router.Name, routerNat.Name, region) + return nil + } + + return err + } + + zap.S().Infof("created GCP router %s with router NAT %s", router.Name, routerNat.Name) + + return nil +} + +func (gcp *GCPConfigure) DeleteGCPCloudNATGateway(region string) error { + if err := gcp.ReadSACredentials(); err != nil { + return err + } + + client, err := compute.NewService(gcp.Ctx, option.WithCredentialsJSON(gcp.AppCredentials.JSON())) + if err != nil { + return err + } + + _, err = client.Routers.Delete(gcp.ProjectID, region, "default-router-"+region).Context(gcp.Ctx).Do() + if err != nil { + var respError *googleapi.Error + if errors.As(err, &respError) && respError.Code == 404 && respError.Errors[0].Reason == apiErrorNotFound { + return nil + } + + return err + } + + zap.S().Infof("deleted GCP router %s for region %s", "default-router-"+region, region) + + return nil +} func (gcp *GCPConfigure) GetGCPClusterContext(clusterName string) ([]byte, error) { var cluster *container.Cluster @@ -100,7 +275,7 @@ func (gcp *GCPConfigure) GetGCPClusterContext(clusterName string) ([]byte, error return nil, err } - client, err := container.NewService(gcp.Ctx, option.WithAuthCredentials(gcp.AppCredentials)) + client, err := container.NewService(gcp.Ctx, option.WithCredentialsJSON(gcp.AppCredentials.JSON())) if err != nil { return nil, err } From 50d34cf629dae46f8cbe594084960d48fe2c2db7 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Mon, 16 Dec 2024 11:24:46 +0100 Subject: [PATCH 32/39] #29 - refactoring --- providers/google_provider/gcp.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/providers/google_provider/gcp.go b/providers/google_provider/gcp.go index 2dbedcb..87d8ce5 100644 --- a/providers/google_provider/gcp.go +++ b/providers/google_provider/gcp.go @@ -125,7 +125,7 @@ func (gcp *GCPConfigure) GetGCPSecrets(tenant string) (map[string][]byte, error) if err != nil { var respError *apierror.APIError if errors.As(err, &respError) && respError.GRPCStatus().Code().String() == gRPCErrorPermissionDenied { - zap.S().Warnf("permission denied to get access for GCP secrets values") + zap.S().Warnf("permission denied to get access to GCP secrets values") return nil, nil } else { return nil, err @@ -190,7 +190,7 @@ func (gcp *GCPConfigure) SetGCPSecret(tenant, region, keyName string, value []by if err != nil { var respError *apierror.APIError if errors.As(err, &respError) && respError.GRPCStatus().Code().String() == gRPCErrorPermissionDenied { - zap.S().Warnf("permission denied to add to secret %s value", keyName) + zap.S().Warnf("permission denied to add GCP secret %s value", keyName) } else { return err } From bf028e775ee02af842b740a4a85a21a79fe1ee2a Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Tue, 17 Dec 2024 12:07:59 +0100 Subject: [PATCH 33/39] #29 - implement support AWS secrets manager for store SOPS Age keys --- cmd/cluster.go | 4 + cmd/cluster_capa.go | 39 +++++++- cmd/config.go | 44 ++++++++- cmd/rmk.go | 2 +- cmd/secret.go | 48 ++++++++++ go.mod | 11 ++- go.sum | 22 +++-- providers/aws_provider/aws.go | 151 ++++++++++++++++++++++++++----- providers/google_provider/gcp.go | 2 +- 9 files changed, 280 insertions(+), 43 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index 264e045..10b1346 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -336,6 +336,10 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error { if err := cc.createAWSClusterSSHKey(); err != nil { return err } + + if err := cc.createAWSSecrets(); err != nil { + return err + } case azure_provider.AzureClusterProvider: if err := cc.createAzureSecrets(cc.Conf.AzureConfigure); err != nil { return err diff --git a/cmd/cluster_capa.go b/cmd/cluster_capa.go index b5d555a..dba5395 100644 --- a/cmd/cluster_capa.go +++ b/cmd/cluster_capa.go @@ -3,12 +3,14 @@ package cmd import ( "os" "path/filepath" + "strings" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/client-go/applyconfigurations/core/v1" "rmk/providers/aws_provider" + "rmk/util" ) const ( @@ -167,9 +169,42 @@ func (cc *ClusterCommands) getAWSClusterContext() ([]byte, error) { } func (cc *ClusterCommands) createAWSClusterSSHKey() error { - return aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile).CreateEC2SSHKey(cc.Conf.Name) + return aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile).CreateAWSEC2SSHKey(cc.Conf.Name) } func (cc *ClusterCommands) deleteAWSClusterSSHKey() error { - return aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile).DeleteEC2SSHKey(cc.Conf.Name) + return aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile).DeleteAWSEC2SSHKey(cc.Conf.Name) +} + +func (cc *ClusterCommands) createAWSSecrets() error { + a := aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile) + + secrets, err := a.GetAWSSecrets(cc.Conf.Tenant) + if err != nil { + return err + } + + if len(secrets) > 0 { + return nil + } + + walkMatch, err := util.WalkMatch(cc.Conf.SopsAgeKeys, cc.Conf.Tenant+"*"+util.SopsAgeKeyExt) + if err != nil { + return err + } + + for _, val := range walkMatch { + file, err := os.ReadFile(val) + if err != nil { + return err + } + + keyName := strings.TrimSuffix(filepath.Base(val), util.SopsAgeKeyExt) + + if err := a.SetAWSSecret(cc.Conf.Tenant, keyName, file); err != nil { + return err + } + } + + return nil } diff --git a/cmd/config.go b/cmd/config.go index 984ed10..782c641 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -46,6 +46,18 @@ func (c *ConfigCommands) configAws(profile string) error { ac.ConfigSource = strings.Join(ac.AWSSharedConfigFile(profile), "") ac.CredentialsSource = strings.Join(ac.AWSSharedCredentialsFile(profile), "") + if !util.IsExists(c.Ctx.String("config"), true) && + util.IsExists(ac.ConfigSource, true) && + util.IsExists(ac.CredentialsSource, true) { + if err := os.RemoveAll(ac.ConfigSource); err != nil { + return err + } + + if err := os.RemoveAll(ac.CredentialsSource); err != nil { + return err + } + } + if util.IsExists(ac.ConfigSource, true) { if err := ac.ReadAWSConfigProfile(); err != nil { return err @@ -104,7 +116,7 @@ func (c *ConfigCommands) configAwsMFA() error { c.Conf.AwsConfigure.Profile = c.Conf.AWSMFAProfile } - if err := c.Conf.GetMFADevicesSerialNumbers(); err != nil { + if err := c.Conf.GetAWSMFADevicesSerialNumbers(); err != nil { return err } @@ -154,7 +166,7 @@ func (c *ConfigCommands) configAwsMFA() error { } if len(c.Conf.MFADeviceSerialNumber) > 0 && currentTime.After(tokenExpiration) { - if err := c.Conf.GetMFASessionToken(); err != nil { + if err := c.Conf.GetAWSMFASessionToken(); err != nil { return err } @@ -296,6 +308,24 @@ func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi return err } + secrets, err := aws_provider.NewAwsConfigure(c.Context, conf.Profile).GetAWSSecrets(conf.Tenant) + if err != nil { + return err + } + + if len(secrets) == 0 { + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s secrets", + conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + } + + for key, val := range secrets { + zap.S().Infof("download AWS secret %s to %s", + key, filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) + if err := os.WriteFile(filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { + return err + } + } + return nil } @@ -368,6 +398,11 @@ func initAzureProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler. return err } + if len(secrets) == 0 { + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s key vault secrets", + conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + } + for key, val := range secrets { zap.S().Infof("download Azure key vault secret %s to %s", key, filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) @@ -411,6 +446,11 @@ func initGCPProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi return err } + if len(secrets) == 0 { + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s secrets", + conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + } + for key, val := range secrets { zap.S().Infof("download GCP secret %s to %s", key, filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) diff --git a/cmd/rmk.go b/cmd/rmk.go index 86016f7..38e792f 100644 --- a/cmd/rmk.go +++ b/cmd/rmk.go @@ -65,7 +65,7 @@ func docGenerateAction() cli.ActionFunc { func getRMKArtifactMetadata(keyPath string) (*RMKArtifactMetadata, error) { rmkArtifactMetadata := &RMKArtifactMetadata{} aws := &aws_provider.AwsConfigure{Region: util.RMKBucketRegion} - data, err := aws.GetFileData(util.RMKBucketName, util.RMKBin+"/"+keyPath+"/metadata.json") + data, err := aws.GetAWSBucketFileData(util.RMKBucketName, util.RMKBin+"/"+keyPath+"/metadata.json") if err != nil { return nil, err } diff --git a/cmd/secret.go b/cmd/secret.go index 0f58afc..0f1c8d3 100644 --- a/cmd/secret.go +++ b/cmd/secret.go @@ -156,6 +156,24 @@ func (sc *SecretCommands) CreateKeys() error { func (sc *SecretCommands) DownloadKeys() error { switch sc.Conf.ClusterProvider { case aws_provider.AWSClusterProvider: + secrets, err := aws_provider.NewAwsConfigure(sc.Ctx.Context, sc.Conf.Profile).GetAWSSecrets(sc.Conf.Tenant) + if err != nil { + return err + } + + if len(secrets) == 0 { + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s secrets", + sc.Conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + } + + for key, val := range secrets { + zap.S().Infof("download AWS secret %s to %s", + key, filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) + if err := os.WriteFile(filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { + return err + } + } + return nil case azure_provider.AzureClusterProvider: if err := sc.Conf.NewAzureClient(sc.Ctx.Context, sc.Conf.Name); err != nil { @@ -167,6 +185,11 @@ func (sc *SecretCommands) DownloadKeys() error { return err } + if len(secrets) == 0 { + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s secrets", + sc.Conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + } + for key, val := range secrets { zap.S().Infof("download Azure key vault secret %s to %s", key, filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) @@ -184,6 +207,11 @@ func (sc *SecretCommands) DownloadKeys() error { return err } + if len(secrets) == 0 { + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s secrets", + sc.Conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + } + for key, val := range secrets { zap.S().Infof("download GCP secret %s to %s", key, filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) @@ -201,6 +229,26 @@ func (sc *SecretCommands) DownloadKeys() error { func (sc *SecretCommands) UploadKeys() error { switch sc.Conf.ClusterProvider { case aws_provider.AWSClusterProvider: + a := aws_provider.NewAwsConfigure(sc.Ctx.Context, sc.Conf.Profile) + + walkMatch, err := util.WalkMatch(sc.Conf.SopsAgeKeys, sc.Conf.Tenant+"*"+util.SopsAgeKeyExt) + if err != nil { + return err + } + + for _, val := range walkMatch { + file, err := os.ReadFile(val) + if err != nil { + return err + } + + keyName := strings.TrimSuffix(filepath.Base(val), util.SopsAgeKeyExt) + + if err := a.SetAWSSecret(sc.Conf.Tenant, keyName, file); err != nil { + return err + } + } + return nil case azure_provider.AzureClusterProvider: if err := sc.Conf.NewAzureClient(sc.Ctx.Context, sc.Conf.Name); err != nil { diff --git a/go.mod b/go.mod index 9a4da6e..c7204b0 100644 --- a/go.mod +++ b/go.mod @@ -15,21 +15,22 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 github.com/Masterminds/semver v1.5.0 github.com/Masterminds/sprig/v3 v3.2.3 - github.com/aws/aws-sdk-go-v2 v1.32.3 + github.com/aws/aws-sdk-go-v2 v1.32.6 github.com/aws/aws-sdk-go-v2/config v1.26.3 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11 github.com/aws/aws-sdk-go-v2/service/ec2 v1.187.0 github.com/aws/aws-sdk-go-v2/service/eks v1.51.1 github.com/aws/aws-sdk-go-v2/service/iam v1.28.7 github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.7 github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 - github.com/aws/smithy-go v1.22.0 + github.com/aws/smithy-go v1.22.1 github.com/cheggaaa/pb v1.0.29 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-git/v5 v5.11.0 github.com/google/go-github v17.0.0+incompatible github.com/googleapis/gax-go/v2 v2.12.3 - github.com/hashicorp/go-getter v1.7.3 + github.com/hashicorp/go-getter v1.7.5 github.com/slack-go/slack v0.12.3 github.com/urfave/cli/v2 v2.27.1 go.uber.org/zap v1.27.0 @@ -63,8 +64,8 @@ require ( github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.16.14 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect diff --git a/go.sum b/go.sum index 72de160..442110b 100644 --- a/go.sum +++ b/go.sum @@ -243,8 +243,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.44.122 h1:p6mw01WBaNpbdP2xrisz5tIkcNwzj/HysobNoaAHjgo= github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk= -github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= +github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= +github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo= github.com/aws/aws-sdk-go-v2/config v1.26.3 h1:dKuc2jdp10y13dEEvPqWxqLoc0vF3Z9FC45MvuQSxOA= @@ -255,10 +255,10 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tC github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11 h1:I6lAa3wBWfCz/cKkOpAcumsETRkFAl70sWi8ItcMEsM= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11/go.mod h1:be1NIO30kJA23ORBLqPo1LttEM6tPNSEcjkd1eKzNW0= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 h1:5oE2WzJE56/mVveuDZPJESKlg/00AaS2pY2QZcnxg4M= @@ -279,14 +279,16 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 h1:KOxnQeWy5sXyS github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10/go.mod h1:jMx5INQFYFYB3lQD9W0D8Ohgq6Wnl7NYOJ2TQndbulI= github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 h1:PJTdBMsyvra6FtED7JZtDpQrIAflYDHFoZAu/sKYkwU= github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0/go.mod h1:4qXHrG1Ne3VGIMZPCB8OjH/pLFO94sKABIusjh0KWPU= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.7 h1:Nyfbgei75bohfmZNxgN27i528dGYVzqWJGlAO6lzXy8= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.7/go.mod h1:FG4p/DciRxPgjA+BEOlwRHN0iA8hX2h9g5buSy3cTDA= github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 h1:dGrs+Q/WzhsiUKh82SfTVN66QzyulXuMDTV/G8ZxOac= github.com/aws/aws-sdk-go-v2/service/sso v1.18.6/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 h1:Yf2MIo9x+0tyv76GljxzqA3WtC5mw7NmazD2chwjxE4= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= -github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM= -github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= +github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= @@ -497,8 +499,8 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.7.3 h1:bN2+Fw9XPFvOCjB0UOevFIMICZ7G2XSQHzfvLUyOM5E= -github.com/hashicorp/go-getter v1.7.3/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-getter v1.7.5 h1:dT58k9hQ/vbxNMwoI5+xFYAJuv6152UNvdHokfI5wE4= +github.com/hashicorp/go-getter v1.7.5/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= diff --git a/providers/aws_provider/aws.go b/providers/aws_provider/aws.go index 2e601ef..5aaeac7 100644 --- a/providers/aws_provider/aws.go +++ b/providers/aws_provider/aws.go @@ -26,7 +26,9 @@ import ( eksType "github.com/aws/aws-sdk-go-v2/service/eks/types" "github.com/aws/aws-sdk-go-v2/service/iam" "github.com/aws/aws-sdk-go-v2/service/s3" - s3type "github.com/aws/aws-sdk-go-v2/service/s3/types" + s3Type "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/aws/aws-sdk-go-v2/service/secretsmanager" + smType "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types" "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/aws/smithy-go" "go.uber.org/zap" @@ -51,6 +53,8 @@ aws_secret_access_key = {{ .AwsCredentialsProfile.SecretAccessKey }} aws_session_token = {{ .AwsCredentialsProfile.SessionToken }} {{- end }} ` + + apiErrorAccessDeniedException = "AccessDeniedException" ) type AwsConfigure struct { @@ -214,7 +218,7 @@ func (a *AwsConfigure) WriteAWSConfigProfile() error { return nil } -func (a *AwsConfigure) GetUserName() error { +func (a *AwsConfigure) GetAWSUserName() error { cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) if err != nil { return err @@ -230,7 +234,7 @@ func (a *AwsConfigure) GetUserName() error { return nil } -func (a *AwsConfigure) GetMFACredentials() error { +func (a *AwsConfigure) GetAWSMFACredentials() error { cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) if err != nil { return err @@ -243,14 +247,14 @@ func (a *AwsConfigure) GetMFACredentials() error { return nil } -func (a *AwsConfigure) GetMFADevicesSerialNumbers() error { +func (a *AwsConfigure) GetAWSMFADevicesSerialNumbers() error { var serialNumbers = make(map[string]string) - if err := a.GetUserName(); err != nil { + if err := a.GetAWSUserName(); err != nil { return err } - if err := a.GetMFACredentials(); err != nil { + if err := a.GetAWSMFACredentials(); err != nil { return err } @@ -283,13 +287,13 @@ func (a *AwsConfigure) GetMFADevicesSerialNumbers() error { return nil } -func (a *AwsConfigure) GetMFASessionToken() error { +func (a *AwsConfigure) GetAWSMFASessionToken() error { cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) if err != nil { return err } - if err := a.GetMFADevicesSerialNumbers(); err != nil { + if err := a.GetAWSMFADevicesSerialNumbers(); err != nil { return err } @@ -430,7 +434,7 @@ func (a *AwsConfigure) getKubeConfigUserName(clusterName string) string { return fmt.Sprintf("%s-capi-admin", clusterName) } -func (a *AwsConfigure) CreateEC2SSHKey(clusterName string) error { +func (a *AwsConfigure) CreateAWSEC2SSHKey(clusterName string) error { var respError smithy.APIError cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) @@ -466,7 +470,7 @@ func (a *AwsConfigure) CreateEC2SSHKey(clusterName string) error { return nil } -func (a *AwsConfigure) DeleteEC2SSHKey(clusterName string) error { +func (a *AwsConfigure) DeleteAWSEC2SSHKey(clusterName string) error { cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) if err != nil { return err @@ -490,13 +494,116 @@ func (a *AwsConfigure) DeleteEC2SSHKey(clusterName string) error { return nil } -func (a *AwsConfigure) CreateBucket(bucketName string) error { +func (a *AwsConfigure) GetAWSSecrets(tenant string) (map[string][]byte, error) { + var ( + secrets = make(map[string][]byte) + respError smithy.APIError + ) + + cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) + if err != nil { + return nil, err + } + + client := secretsmanager.NewFromConfig(cfg) + + params := &secretsmanager.BatchGetSecretValueInput{ + Filters: []smType.Filter{ + { + Key: smType.FilterNameStringTypeTagKey, + Values: []string{"resource-group"}, + }, + { + Key: smType.FilterNameStringTypeTagValue, + Values: []string{tenant + "-" + util.SopsRootName}, + }, + }, + } + + results, err := client.BatchGetSecretValue(a.Ctx, params) + if err != nil { + if errors.As(err, &respError) && respError.ErrorCode() == apiErrorAccessDeniedException { + zap.S().Warnf("permission denied to get AWS batch secret values") + + return nil, nil + } + + return nil, err + } + + for _, val := range results.SecretValues { + secrets[aws.ToString(val.Name)] = []byte(aws.ToString(val.SecretString)) + } + + return secrets, nil +} + +func (a *AwsConfigure) SetAWSSecret(tenant, keyName string, value []byte) error { + var ( + resourceExistsException *smType.ResourceExistsException + respError smithy.APIError + ) + + cfg, err := a.errorProxy(config.LoadDefaultConfig(a.Ctx, a.configOptions()...)) + if err != nil { + return err + } + + client := secretsmanager.NewFromConfig(cfg) + + createParams := &secretsmanager.CreateSecretInput{ + Name: aws.String(keyName), + Description: aws.String("SOPS Age privet key for Tenant: " + tenant), + ForceOverwriteReplicaSecret: true, + SecretString: aws.String(string(value)), + Tags: []smType.Tag{ + {Key: aws.String("resource-group"), Value: aws.String(tenant + "-" + util.SopsRootName)}, + }, + } + + createSecret, err := client.CreateSecret(a.Ctx, createParams) + if err != nil { + if errors.As(err, &resourceExistsException) { + updateParams := &secretsmanager.UpdateSecretInput{ + SecretId: aws.String(keyName), + SecretString: aws.String(string(value)), + } + + updateSecret, err := client.UpdateSecret(a.Ctx, updateParams) + if err != nil { + if errors.As(err, &respError) && respError.ErrorCode() == apiErrorAccessDeniedException { + zap.S().Warnf("permission denied to create AWS secret: %s", keyName) + + return nil + } + + return err + } + + zap.S().Infof("created AWS secret: %s, %s", keyName, aws.ToString(updateSecret.ARN)) + + return nil + } else if errors.As(err, &respError) && respError.ErrorCode() == apiErrorAccessDeniedException { + zap.S().Warnf("permission denied to create AWS secret: %s", keyName) + + return nil + } else { + return err + } + } + + zap.S().Infof("created AWS secret: %s, %s", keyName, aws.ToString(createSecret.ARN)) + + return nil +} + +func (a *AwsConfigure) CreateAWSBucket(bucketName string) error { var ( respError s3.ResponseError - bucketExist *s3type.BucketAlreadyExists - bucketOwner *s3type.BucketAlreadyOwnedByYou + bucketExist *s3Type.BucketAlreadyExists + bucketOwner *s3Type.BucketAlreadyOwnedByYou bucketParams s3.CreateBucketInput - bucketNotFound *s3type.NotFound + bucketNotFound *s3Type.NotFound ) ctx := context.TODO() @@ -522,8 +629,8 @@ func (a *AwsConfigure) CreateBucket(bucketName string) error { } else { bucketParams = s3.CreateBucketInput{ Bucket: aws.String(bucketName), - CreateBucketConfiguration: &s3type.CreateBucketConfiguration{ - LocationConstraint: s3type.BucketLocationConstraint(a.Region), + CreateBucketConfiguration: &s3Type.CreateBucketConfiguration{ + LocationConstraint: s3Type.BucketLocationConstraint(a.Region), }, } } @@ -550,7 +657,7 @@ func (a *AwsConfigure) CreateBucket(bucketName string) error { putParams := s3.PutPublicAccessBlockInput{ Bucket: aws.String(bucketName), - PublicAccessBlockConfiguration: &s3type.PublicAccessBlockConfiguration{ + PublicAccessBlockConfiguration: &s3Type.PublicAccessBlockConfiguration{ BlockPublicAcls: aws.Bool(true), BlockPublicPolicy: aws.Bool(true), IgnorePublicAcls: aws.Bool(true), @@ -572,7 +679,7 @@ func (a *AwsConfigure) CreateBucket(bucketName string) error { return nil } -func (a *AwsConfigure) BucketKeyExists(region, bucketName, key string) (bool, error) { +func (a *AwsConfigure) AWSBucketKeyExists(region, bucketName, key string) (bool, error) { if len(bucketName) == 0 { return false, nil } @@ -628,8 +735,8 @@ func GetObjects(c context.Context, api S3ListObjectsAPI, input *s3.ListObjectsV2 return api.ListObjectsV2(c, input) } -func (a *AwsConfigure) DownloadFromBucket(region, bucketName, localDir, filePrefix string) error { - var noSuchBucket *s3type.NoSuchBucket +func (a *AwsConfigure) DownloadFromAWSBucket(region, bucketName, localDir, filePrefix string) error { + var noSuchBucket *s3Type.NoSuchBucket downloadToFile := func(downloader *manager.Downloader, targetDirectory, bucket, key string) error { // Create the directories in the path @@ -707,7 +814,7 @@ func (a *AwsConfigure) DownloadFromBucket(region, bucketName, localDir, filePref return nil } -func (a *AwsConfigure) GetFileData(bucketName, key string) ([]byte, error) { +func (a *AwsConfigure) GetAWSBucketFileData(bucketName, key string) ([]byte, error) { var client *s3.Client ctx := context.TODO() if len(a.Profile) > 0 { @@ -734,7 +841,7 @@ func (a *AwsConfigure) GetFileData(bucketName, key string) ([]byte, error) { return io.ReadAll(resp.Body) } -func (a *AwsConfigure) UploadToBucket(bucketName, localDir, pattern string) error { +func (a *AwsConfigure) UploadToAWSBucket(bucketName, localDir, pattern string) error { ctx := context.TODO() cfg, err := a.errorProxy(config.LoadDefaultConfig(ctx, a.configOptions()...)) if err != nil { diff --git a/providers/google_provider/gcp.go b/providers/google_provider/gcp.go index 87d8ce5..10bb64c 100644 --- a/providers/google_provider/gcp.go +++ b/providers/google_provider/gcp.go @@ -196,7 +196,7 @@ func (gcp *GCPConfigure) SetGCPSecret(tenant, region, keyName string, value []by } } - zap.S().Infof("created Azure key vault secret: %s, %s", keyName, version.Name) + zap.S().Infof("created GCP secret: %s, %s", keyName, version.Name) return nil } From 20e0ccf9fd392b6f0045afa8703edaec2c2d596e Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Tue, 17 Dec 2024 13:10:12 +0100 Subject: [PATCH 34/39] #29 - refactoring --- cmd/config.go | 18 +++++++++--------- cmd/secret.go | 18 +++++++++--------- providers/aws_provider/aws.go | 14 +++++++------- providers/azure_provider/azure.go | 20 ++++++++++---------- providers/google_provider/gcp.go | 12 ++++++------ 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index 782c641..555b9cd 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -314,12 +314,12 @@ func initAWSProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi } if len(secrets) == 0 { - zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s secrets", - conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in AWS Secrets Manager secrets", + conf.Tenant) } for key, val := range secrets { - zap.S().Infof("download AWS secret %s to %s", + zap.S().Infof("download AWS Secrets Manager secret %s to %s", key, filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) if err := os.WriteFile(filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { return err @@ -399,12 +399,12 @@ func initAzureProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler. } if len(secrets) == 0 { - zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s key vault secrets", - conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in Azure Key Vault secrets", + conf.Tenant) } for key, val := range secrets { - zap.S().Infof("download Azure key vault secret %s to %s", + zap.S().Infof("download Azure Key Vault secret %s to %s", key, filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) if err := os.WriteFile(filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { return err @@ -447,12 +447,12 @@ func initGCPProfile(c *cli.Context, conf *config.Config, gitSpec *git_handler.Gi } if len(secrets) == 0 { - zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s secrets", - conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in GCP Secrets Manager secrets", + conf.Tenant) } for key, val := range secrets { - zap.S().Infof("download GCP secret %s to %s", + zap.S().Infof("download GCP Secrets Manager secret %s to %s", key, filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) if err := os.WriteFile(filepath.Join(conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { return err diff --git a/cmd/secret.go b/cmd/secret.go index 0f1c8d3..a46fd0a 100644 --- a/cmd/secret.go +++ b/cmd/secret.go @@ -162,12 +162,12 @@ func (sc *SecretCommands) DownloadKeys() error { } if len(secrets) == 0 { - zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s secrets", - sc.Conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in AWS Secrets Manager secrets", + sc.Conf.Tenant) } for key, val := range secrets { - zap.S().Infof("download AWS secret %s to %s", + zap.S().Infof("download AWS Secrets Manager secret %s to %s", key, filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) if err := os.WriteFile(filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { return err @@ -186,12 +186,12 @@ func (sc *SecretCommands) DownloadKeys() error { } if len(secrets) == 0 { - zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s secrets", - sc.Conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in Azure Key Vault secrets", + sc.Conf.Tenant) } for key, val := range secrets { - zap.S().Infof("download Azure key vault secret %s to %s", + zap.S().Infof("download Azure Key Vault secret %s to %s", key, filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) if err := os.WriteFile(filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { return err @@ -208,12 +208,12 @@ func (sc *SecretCommands) DownloadKeys() error { } if len(secrets) == 0 { - zap.S().Warnf("SOPS Age keys contents for tenant %s not found in %s secrets", - sc.Conf.Tenant, strings.ToUpper(aws_provider.AWSClusterProvider)) + zap.S().Warnf("SOPS Age keys contents for tenant %s not found in GCP Secrets Manager secrets", + sc.Conf.Tenant) } for key, val := range secrets { - zap.S().Infof("download GCP secret %s to %s", + zap.S().Infof("download GCP Secrets Manager secret %s to %s", key, filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt)) if err := os.WriteFile(filepath.Join(sc.Conf.SopsAgeKeys, key+util.SopsAgeKeyExt), val, 0644); err != nil { return err diff --git a/providers/aws_provider/aws.go b/providers/aws_provider/aws.go index 5aaeac7..ca975c8 100644 --- a/providers/aws_provider/aws.go +++ b/providers/aws_provider/aws.go @@ -464,7 +464,7 @@ func (a *AwsConfigure) CreateAWSEC2SSHKey(clusterName string) error { } if sshKey != nil { - zap.S().Infof("created SSHKey %s with id %s", aws.ToString(sshKey.KeyName), aws.ToString(sshKey.KeyPairId)) + zap.S().Infof("created AWS EC2 SSHKey %s with id %s", aws.ToString(sshKey.KeyName), aws.ToString(sshKey.KeyPairId)) } return nil @@ -488,7 +488,7 @@ func (a *AwsConfigure) DeleteAWSEC2SSHKey(clusterName string) error { } if sshKey.KeyPairId != nil { - zap.S().Infof("deleted SSHKey %s with id %s", clusterName, aws.ToString(sshKey.KeyPairId)) + zap.S().Infof("deleted AWS EC2 SSHKey %s with id %s", clusterName, aws.ToString(sshKey.KeyPairId)) } return nil @@ -523,7 +523,7 @@ func (a *AwsConfigure) GetAWSSecrets(tenant string) (map[string][]byte, error) { results, err := client.BatchGetSecretValue(a.Ctx, params) if err != nil { if errors.As(err, &respError) && respError.ErrorCode() == apiErrorAccessDeniedException { - zap.S().Warnf("permission denied to get AWS batch secret values") + zap.S().Warnf("permission denied to get AWS Secrets Manager batch secret values") return nil, nil } @@ -553,7 +553,7 @@ func (a *AwsConfigure) SetAWSSecret(tenant, keyName string, value []byte) error createParams := &secretsmanager.CreateSecretInput{ Name: aws.String(keyName), - Description: aws.String("SOPS Age privet key for Tenant: " + tenant), + Description: aws.String("SOPS Age private key for Tenant: " + tenant), ForceOverwriteReplicaSecret: true, SecretString: aws.String(string(value)), Tags: []smType.Tag{ @@ -572,7 +572,7 @@ func (a *AwsConfigure) SetAWSSecret(tenant, keyName string, value []byte) error updateSecret, err := client.UpdateSecret(a.Ctx, updateParams) if err != nil { if errors.As(err, &respError) && respError.ErrorCode() == apiErrorAccessDeniedException { - zap.S().Warnf("permission denied to create AWS secret: %s", keyName) + zap.S().Warnf("permission denied to create AWS Secrets Manager secret: %s", keyName) return nil } @@ -584,7 +584,7 @@ func (a *AwsConfigure) SetAWSSecret(tenant, keyName string, value []byte) error return nil } else if errors.As(err, &respError) && respError.ErrorCode() == apiErrorAccessDeniedException { - zap.S().Warnf("permission denied to create AWS secret: %s", keyName) + zap.S().Warnf("permission denied to create AWS Secrets Manager secret: %s", keyName) return nil } else { @@ -592,7 +592,7 @@ func (a *AwsConfigure) SetAWSSecret(tenant, keyName string, value []byte) error } } - zap.S().Infof("created AWS secret: %s, %s", keyName, aws.ToString(createSecret.ARN)) + zap.S().Infof("created AWS Secrets Manager secret: %s, %s", keyName, aws.ToString(createSecret.ARN)) return nil } diff --git a/providers/azure_provider/azure.go b/providers/azure_provider/azure.go index fe0921b..ca3f20f 100644 --- a/providers/azure_provider/azure.go +++ b/providers/azure_provider/azure.go @@ -195,7 +195,7 @@ func (ac *AzureConfigure) createKeyVaultResourceGroup(tenant string) error { if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permission denied to create Azure resource group: %s", tenant+"-"+util.SopsRootName) + zap.S().Warnf("permission denied to create Azure Resource Group: %s", tenant+"-"+util.SopsRootName) return nil } @@ -204,7 +204,7 @@ func (ac *AzureConfigure) createKeyVaultResourceGroup(tenant string) error { ac.ResourceGroupName = *update.Name - zap.S().Infof("created Azure resource group: %s", ac.ResourceGroupName) + zap.S().Infof("created Azure Resource Group: %s", ac.ResourceGroupName) return nil } @@ -214,7 +214,7 @@ func (ac *AzureConfigure) existsKeyVaultResourceGroup(tenant string) (bool, erro if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permission denied to check existence of Azure resource group: %s", + zap.S().Warnf("permission denied to check existence of Azure Resource Group: %s", tenant+"-"+util.SopsRootName) return false, nil } @@ -252,7 +252,7 @@ func (ac *AzureConfigure) CreateAzureKeyVault(tenant string) error { if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permission denied to create Azure key vault: %s", + zap.S().Warnf("permission denied to create Azure Key Vault: %s", ac.KeyVaultName) return nil } @@ -267,7 +267,7 @@ func (ac *AzureConfigure) CreateAzureKeyVault(tenant string) error { ac.KeyVaultURI = *result.Properties.VaultURI - zap.S().Infof("created Azure key vault: %s, %s", ac.KeyVaultName, ac.KeyVaultURI) + zap.S().Infof("created Azure Key Vault: %s, %s", ac.KeyVaultName, ac.KeyVaultURI) return nil } @@ -290,7 +290,7 @@ func (ac *AzureConfigure) GetAzureKeyVault(tenant string) (bool, error) { } if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permission denied to get Azure key vault: %s", ac.KeyVaultName) + zap.S().Warnf("permission denied to get Azure Key Vault: %s", ac.KeyVaultName) return false, nil } @@ -320,7 +320,7 @@ func (ac *AzureConfigure) GetAzureSecrets() (map[string][]byte, error) { if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permission denied to list Azure key vault secrets") + zap.S().Warnf("permission denied to list Azure Key Vault secrets") return nil, nil } @@ -335,7 +335,7 @@ func (ac *AzureConfigure) GetAzureSecrets() (map[string][]byte, error) { if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permission denied to get of Azure key vault secret: %s", name) + zap.S().Warnf("permission denied to get of Azure Key Vault secret: %s", name) return nil, nil } @@ -363,14 +363,14 @@ func (ac *AzureConfigure) SetAzureSecret(keyName, value string) error { if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) && respErr.StatusCode == 403 { - zap.S().Warnf("permission denied to create Azure key vault secret: %s", keyName) + zap.S().Warnf("permission denied to create Azure Key Vault secret: %s", keyName) return nil } return err } - zap.S().Infof("created Azure key vault secret: %s, %s", keyName, *secret.ID) + zap.S().Infof("created Azure Key Vault secret: %s, %s", keyName, *secret.ID) return nil } diff --git a/providers/google_provider/gcp.go b/providers/google_provider/gcp.go index 10bb64c..28c500a 100644 --- a/providers/google_provider/gcp.go +++ b/providers/google_provider/gcp.go @@ -113,7 +113,7 @@ func (gcp *GCPConfigure) GetGCPSecrets(tenant string) (map[string][]byte, error) if err != nil { var respError *apierror.APIError if errors.As(err, &respError) && respError.GRPCStatus().Code().String() == gRPCErrorPermissionDenied { - zap.S().Warnf("permission denied to list GCP secrets") + zap.S().Warnf("permission denied to list GCP Secrets Manager secrets") return nil, nil } else { return nil, err @@ -125,7 +125,7 @@ func (gcp *GCPConfigure) GetGCPSecrets(tenant string) (map[string][]byte, error) if err != nil { var respError *apierror.APIError if errors.As(err, &respError) && respError.GRPCStatus().Code().String() == gRPCErrorPermissionDenied { - zap.S().Warnf("permission denied to get access to GCP secrets values") + zap.S().Warnf("permission denied to get access to GCP Secrets Manager secrets values") return nil, nil } else { return nil, err @@ -135,7 +135,7 @@ func (gcp *GCPConfigure) GetGCPSecrets(tenant string) (map[string][]byte, error) crc32c := crc32.MakeTable(crc32.Castagnoli) checksum := int64(crc32.Checksum(result.Payload.Data, crc32c)) if checksum != *result.Payload.DataCrc32C { - return nil, fmt.Errorf("data corruption detected for GCP secrets value: %s", resp.Name) + return nil, fmt.Errorf("data corruption detected for GCP Secrets Manager secrets value: %s", resp.Name) } secrets[filepath.Base(resp.Name)] = result.Payload.Data @@ -175,7 +175,7 @@ func (gcp *GCPConfigure) SetGCPSecret(tenant, region, keyName string, value []by if err != nil { var respError *apierror.APIError if errors.As(err, &respError) && respError.GRPCStatus().Code().String() == gRPCErrorPermissionDenied { - zap.S().Warnf("permission denied to create GCP secret: %s", keyName) + zap.S().Warnf("permission denied to create GCP Secrets Manager secret: %s", keyName) } else if respError.GRPCStatus().Code().String() != gRPCErrorAlreadyExists { return err } @@ -190,13 +190,13 @@ func (gcp *GCPConfigure) SetGCPSecret(tenant, region, keyName string, value []by if err != nil { var respError *apierror.APIError if errors.As(err, &respError) && respError.GRPCStatus().Code().String() == gRPCErrorPermissionDenied { - zap.S().Warnf("permission denied to add GCP secret %s value", keyName) + zap.S().Warnf("permission denied to add GCP Secrets Manager secret %s value", keyName) } else { return err } } - zap.S().Infof("created GCP secret: %s, %s", keyName, version.Name) + zap.S().Infof("created GCP Secrets Manager secret: %s, %s", keyName, version.Name) return nil } From e1b43a7ac8b89a6a6b6f21cc8b0e1fe641752463 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Tue, 17 Dec 2024 15:09:34 +0100 Subject: [PATCH 35/39] #29 - add release notes --- docs/release-notes.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index cbd77a9..51db164 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,2 +1,19 @@ -- #33 - Added a new GitHub action for RMK commands documentation generation. -- #34 - Added support for Terraform outputs of a boolean type. +- #29 - Added support for AWS Secrets Manager to store SOPS Age keys. +- #29 - Added support for GCP Secrets Manager to store SOPS Age keys. +- #29 - Added support for Azure Key Vault to store SOPS Age keys. +- #29 - Added the ability to create an SSH key pair for the AWS provider. +- #29 - Added support for the AWS provider in Cluster API. +- #29 - Added GCP NAT route creation for the GCP provider. +- #29 - Added support for the GCP provider in Cluster API. +- #29 - Added support for the Azure provider in Cluster API. +- #29 - Added commands for managing CAPI clusters to the cluster category commands. +- #29 - Added configuration arguments for project generation. +- #29 - Fixed the output of the config list command for adjacent tenant names. +- #29 - Removed the artifact-mode functionality. +- #29 - Reworked the behavior of the config init command to align with each cluster provider. +- #29 - Reworked the approach to obtaining the KubeContext for all cluster providers. +- #29 - Refactored the environment variables provisioning for all cluster providers in release-category commands. +- #29 - Deprecated Terraform commands under the cluster category. +- #29 - Refactored the k3d list command. +- #29 - Changed root-domain configuration to be managed via the project.yaml file. +- #29 - Restructured code files for improved organization. From 8b0d70fcd29a9b840c1bf80669ce30fee37d46d7 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Tue, 17 Dec 2024 20:41:13 +0100 Subject: [PATCH 36/39] #29 - refactoring release notes --- docs/release-notes.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 51db164..95ef96e 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,19 +1,19 @@ - #29 - Added support for AWS Secrets Manager to store SOPS Age keys. - #29 - Added support for GCP Secrets Manager to store SOPS Age keys. - #29 - Added support for Azure Key Vault to store SOPS Age keys. -- #29 - Added the ability to create an SSH key pair for the AWS provider. - #29 - Added support for the AWS provider in Cluster API. -- #29 - Added GCP NAT route creation for the GCP provider. - #29 - Added support for the GCP provider in Cluster API. - #29 - Added support for the Azure provider in Cluster API. -- #29 - Added commands for managing CAPI clusters to the cluster category commands. -- #29 - Added configuration arguments for project generation. +- #29 - Added the ability to create an SSH key pair for the AWS provider. +- #29 - Added GCP NAT router creation for the GCP provider. +- #29 - Added commands for managing Cluster API clusters to the cluster category commands. +- #29 - Added new configuration arguments for project generation. - #29 - Fixed the output of the config list command for adjacent tenant names. -- #29 - Removed the artifact-mode functionality. -- #29 - Reworked the behavior of the config init command to align with each cluster provider. -- #29 - Reworked the approach to obtaining the KubeContext for all cluster providers. -- #29 - Refactored the environment variables provisioning for all cluster providers in release-category commands. -- #29 - Deprecated Terraform commands under the cluster category. +- #29 - Removed the artifact-mode flag and the functionality. +- #29 - Reworked the behaviour of the config init command to align with each cluster provider. +- #29 - Reworked the approach to obtaining the Kubernetes context for all cluster providers. +- #29 - Refactored the environment variables provisioning for all cluster providers in release category commands. - #29 - Refactored the k3d list command. -- #29 - Changed root-domain configuration to be managed via the project.yaml file. - #29 - Restructured code files for improved organization. +- #29 - Changed root-domain configuration to be managed via the project.yaml file. +- #29 - Deprecated Terraform commands under the cluster category. From 6c9c60f0430e3cdce84a67498d8d16f0752439aa Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 18 Dec 2024 12:59:17 +0100 Subject: [PATCH 37/39] #29 - fix project generate behavior --- cmd/commands.go | 4 ++++ cmd/project.go | 15 ++++++++++++--- cmd/project_generation.go | 23 ++++------------------- go.mod | 1 + 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/cmd/commands.go b/cmd/commands.go index 09cb140..8a143c2 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -517,6 +517,10 @@ func readInputSourceWithContext(gitSpec *git_handler.GitSpec, conf *config.Confi return err } + if ctx.Command.Name == "generate" && !util.IsExists(util.GetPwdPath(util.TenantProjectFile), true) { + return nil + } + configPath := util.GetHomePath(util.RMKDir, util.RMKConfig, gitSpec.ID+".yaml") if err := conf.ReadConfigFile(configPath); err != nil { zap.S().Errorf(util.ConfigNotInitializedErrorText) diff --git a/cmd/project.go b/cmd/project.go index a371cf8..9dd8dc3 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -11,6 +11,7 @@ import ( "strings" "text/template" + "github.com/google/go-cmp/cmp" "github.com/urfave/cli/v2" "go.uber.org/zap" "gopkg.in/yaml.v3" @@ -55,7 +56,15 @@ type projectSpec struct { owners string } -func newProjectCommand(conf *config.Config, ctx *cli.Context, workDir string) *ProjectCommands { +func newProjectCommand(conf *config.Config, ctx *cli.Context, workDir string, gitSpec *git_handler.GitSpec) *ProjectCommands { + emptyConfig := &config.Config{} + if cmp.Equal(conf, emptyConfig) { + conf.Name = gitSpec.ID + conf.Tenant = gitSpec.RepoPrefixName + conf.Environment = gitSpec.DefaultBranch + conf.SopsAgeKeys = util.GetHomePath(util.RMKDir, util.SopsRootName, conf.Tenant) + } + return &ProjectCommands{ &parseContent{ TenantName: conf.Tenant, @@ -450,7 +459,7 @@ func projectGenerateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cl return err } - return newProjectCommand(conf, c, util.GetPwdPath()).generateProject(gitSpec) + return newProjectCommand(conf, c, util.GetPwdPath(), gitSpec).generateProject(gitSpec) } } @@ -460,6 +469,6 @@ func projectUpdateAction(conf *config.Config, gitSpec *git_handler.GitSpec) cli. return err } - return newProjectCommand(conf, c, util.GetPwdPath()).updateProjectFile(gitSpec) + return newProjectCommand(conf, c, util.GetPwdPath(), gitSpec).updateProjectFile(gitSpec) } } diff --git a/cmd/project_generation.go b/cmd/project_generation.go index 5fa94c2..1238667 100644 --- a/cmd/project_generation.go +++ b/cmd/project_generation.go @@ -102,20 +102,10 @@ helmfiles: ` + escapeOpen + `{{ env "HELMFILE_` + escapeClose + `{{ .TenantNameE ` helmfileReleases = `releases: - # TODO: Releases from group 1 are needed to deploy Cluster API or K3D clusters. + # TODO: Releases from group 1 are needed to deploy K3D clusters. # TODO: If you do not inherit upstream repositories, you can leave these releases as is, # TODO: or make sure that upstream repositories do not have the same releases to avoid conflicts. # Group 1 - - name: capi-cluster - namespace: kube-system - chart: "{{"{{` + escape + `{{ .Release.Labels.repo }}` + escape + `}}"}}/k3d-cluster" - version: 0.1.0 - labels: - cluster: capi - installed: ` + escapeOpen + `{{ eq (env "CAPI_CLUSTER" | default "false") "true" }}` + escapeClose + ` - inherit: - - template: release - - name: k3d-cluster namespace: kube-system chart: "{{"{{` + escape + `{{ .Release.Labels.repo }}` + escape + `}}"}}/k3d-cluster" @@ -159,14 +149,9 @@ Detailed information about requirements and installation instructions can be fou ### Requirements -- AWS CLI >= 2.9 -- AWS IAM user security credentials (access key pair) - Git -- GitHub PAT to access the following repositories to list in the sections ` + "`project.yaml`" + `: - * clusters - * hooks +- GitHub PAT to access the following repositories to list in the section ` + "`project.yaml dependencies`" + ` - Note: K3D v5.x.x requires at least Docker v20.10.5 (runc >= v1.0.0-rc93) to work properly -- Python >= 3.9 - [RMK CLI](https://edenlabllc.github.io/rmk/latest) ### GitLab flow strategy @@ -214,9 +199,9 @@ This example shows how the following options are configured and interact with ea ` + "```" + `shell rmk project generate \ - --environment=develop \ + --environment=develop.root-domain=localhost \ --owners=user \ - --scopes=rmt-test + --scopes={{ .TenantName }} ` + "```" + ` #### Initialization configuration diff --git a/go.mod b/go.mod index c7204b0..aea77d4 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( github.com/cheggaaa/pb v1.0.29 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-git/v5 v5.11.0 + github.com/google/go-cmp v0.6.0 github.com/google/go-github v17.0.0+incompatible github.com/googleapis/gax-go/v2 v2.12.3 github.com/hashicorp/go-getter v1.7.5 From cc25491543fcd3223d2868d3872b4c01156dc355 Mon Sep 17 00:00:00 2001 From: Aliaksandr Panasiuk Date: Wed, 18 Dec 2024 14:31:06 +0100 Subject: [PATCH 38/39] #29 - refactoring --- cmd/project_generation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/project_generation.go b/cmd/project_generation.go index 1238667..a4b9a08 100644 --- a/cmd/project_generation.go +++ b/cmd/project_generation.go @@ -150,7 +150,7 @@ Detailed information about requirements and installation instructions can be fou ### Requirements - Git -- GitHub PAT to access the following repositories to list in the section ` + "`project.yaml dependencies`" + ` +- GitHub PAT to access the repositories listed in the ` + "`dependencies`" + ` section of ` + "`project.yaml`" + ` - Note: K3D v5.x.x requires at least Docker v20.10.5 (runc >= v1.0.0-rc93) to work properly - [RMK CLI](https://edenlabllc.github.io/rmk/latest) From e9e75b9dbd1f6b7df8e057b900303913a08cc364 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 19 Dec 2024 09:22:04 +0000 Subject: [PATCH 39/39] [skip docs commands update] Update commands documentation --- docs/commands.md | 100 +++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 55 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index 4baab80..cd90314 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -18,8 +18,8 @@ rmk Command line tool for reduced management of the provision of Kubernetes clusters in different environments and management of service releases. **BuiltBy:** goreleaser
-**Commit:** 6e50d9f
-**Date:** 2024-09-04T08:25:29Z
+**Commit:** 6e0a217
+**Date:** 2024-12-19T09:18:59Z
**Target:** linux_amd64 **Usage**: @@ -45,27 +45,35 @@ rmk [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] Cluster management -#### container-registry, c +#### capi, c -Container registry management +CAPI cluster management -##### login +##### create, c -Log in to container registry +Create CAPI management cluster -**--get-token, -g**: get ECR token for authentication +**--k3d-volume-host-path, --kv**="": host local directory path for mount into K3D cluster (default: present working directory) -##### logout +##### delete, d -Log out from container registry +Delete CAPI management cluster -#### destroy, d +##### destroy -Destroy AWS cluster using Terraform +Destroy K8S target (workload) cluster -#### list, l +##### list, l + +List CAPI management clusters + +##### provision, p -List all Terraform available workspaces +Provision K8S target (workload) cluster + +##### update, u + +Update CAPI management cluster #### k3d, k @@ -99,30 +107,6 @@ Start K3D cluster Stop K3D cluster -#### provision, p - -Provision AWS cluster using Terraform - -**--plan, -p**: creates an execution Terraform plan - -#### state, t - -State cluster management using Terraform - -##### delete, d - -Delete resource from Terraform state - -**--resource-address, --ra**="": resource address for delete from Terraform state - -##### list, l - -List resources from Terraform state - -##### refresh, r - -Update state file for AWS cluster using Terraform - #### switch, s Switch Kubernetes context for tenant cluster @@ -145,37 +129,39 @@ Configuration management Initialize configuration for current tenant and selected environment -**--artifact-mode, --am**="": choice of artifact usage model, available: none, online (default: "none") +**--aws-access-key-id, --awid**="": AWS access key ID for IAM user -**--aws-ecr-host, --aeh**="": AWS ECR host (default: "288509344804.dkr.ecr.eu-north-1.amazonaws.com") +**--aws-region, --awr**="": AWS region for current AWS account -**--aws-ecr-region, --aer**="": AWS region for specific ECR host (default: "eu-north-1") +**--aws-secret-access-key, --awsk**="": AWS secret access key for IAM user -**--aws-ecr-user-name, --aeun**="": AWS ECR user name (default: "AWS") +**--aws-session-token, --awst**="": AWS session token for IAM user -**--aws-reconfigure, -r**: force AWS profile creation +**--azure-client-id, --azid**="": Azure client ID for Service Principal -**--aws-reconfigure-artifact-license, -l**: force AWS profile creation for artifact license, used only if RMK config option artifact-mode has values: online, offline +**--azure-client-secret, --azp**="": Azure client secret for Service Principal -**--cloudflare-token, --cft**="": Cloudflare API token for provision NS records +**--azure-location, --azl**="": Azure location -**--cluster-provider, --cp**="": select cluster provider to provision clusters (default: "aws") +**--azure-service-principle, --azsp**: Azure service principal STDIN content -**--cluster-provisioner-state-locking, -c**: disable or enable cluster provisioner state locking +**--azure-subscription-id, --azs**="": Azure subscription ID for current platform domain -**--config-from-environment, --cfe**="": inheritance of RMK config credentials from environments: develop, staging, production +**--azure-tenant-id, --azt**="": Azure tenant ID for Service Principal -**--github-token, --ght**="": personal access token for download GitHub artifacts +**--cluster-provider, --cp**="": cluster provider for provisioning (default: "k3d") -**--progress-bar, -p**: globally disable or enable progress bar for download process +**--gcp-region, --gr**="": GCP region + +**--github-token, --ght**="": GitHub personal access token, required when using private repositories -**--root-domain, --rd**="": domain name for external access to app services via ingress controller +**--google-application-credentials, --gac**="": path to GCP service account credentials JSON file -**--s3-charts-repo-region, --scrr**="": location constraint region of S3 charts repo (default: "eu-north-1") +**--progress-bar, -p**: globally disable or enable progress bar for download process -**--slack-channel, --sc**="": channel name for Slack notification +**--slack-channel, --sc**="": channel name for Slack notifications -**--slack-message-details, --smd**="": additional information for body of Slack message +**--slack-message-details, --smd**="": additional information for body of Slack messages **--slack-notifications, -n**: enable Slack notifications @@ -225,6 +211,12 @@ Generate project directories and files structure **--create-sops-age-keys, -c**: create SOPS age keys for generated project structure +**--environments, -e**="": list project environments. Root domain can take form of .root-domain= + +**--owners, -o**="": list project owners + +**--scopes, -s**="": list project scopes + #### update, u Update project file with specific dependencies version @@ -261,8 +253,6 @@ Destroy releases **--helmfile-log-level, --hll**="": Helmfile log level severity, available: debug, info, warn, error (default: "error") -**--output, -o**="": output format, available: short, yaml (default: "short") - **--selector, -l**="": only run using releases that match labels. Labels can take form of foo=bar or foo!=bar **--skip-context-switch, -s**: skip context switch for not provisioned cluster