From 5c7f74cdd9734dd4ab2a2875855757cbaeee85bc Mon Sep 17 00:00:00 2001 From: DrummyFloyd Date: Sat, 28 Oct 2023 19:25:07 +0200 Subject: [PATCH] feat: add k3s cmd Signed-off-by: DrummyFloyd --- cmd/beta.go | 2 + cmd/k3s/command.go | 134 ++++++++++++++++++++++++++++++++ cmd/k3s/create.go | 117 ++++++++++++++++++++++++++++ internal/progress/message.go | 3 + internal/types/flags.go | 5 ++ internal/utilities/flags.go | 45 +++++++++++ internal/utilities/utilities.go | 10 ++- 7 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 cmd/k3s/command.go create mode 100644 cmd/k3s/create.go diff --git a/cmd/beta.go b/cmd/beta.go index 6833ab11c..2bf670435 100644 --- a/cmd/beta.go +++ b/cmd/beta.go @@ -10,6 +10,7 @@ import ( "fmt" "github.com/kubefirst/kubefirst/cmd/google" + "github.com/kubefirst/kubefirst/cmd/k3s" "github.com/kubefirst/kubefirst/cmd/vultr" "github.com/kubefirst/kubefirst/internal/progress" "github.com/spf13/cobra" @@ -33,6 +34,7 @@ var betaCmd = &cobra.Command{ func init() { cobra.OnInitialize() betaCmd.AddCommand( + k3s.NewCommand(), google.NewCommand(), vultr.NewCommand(), ) diff --git a/cmd/k3s/command.go b/cmd/k3s/command.go new file mode 100644 index 000000000..6e198f700 --- /dev/null +++ b/cmd/k3s/command.go @@ -0,0 +1,134 @@ +/* +Copyright (C) 2021-2023, Kubefirst + +This program is licensed under MIT. +See the LICENSE file for more details. +*/ +package k3s + +import ( + "fmt" + + "github.com/kubefirst/kubefirst/internal/common" + "github.com/spf13/cobra" +) + +var ( + // Create + // TODO: add ssh key flag to connect on k3s targets + alertsEmailFlag string + ciFlag bool + cloudRegionFlag string + nodeTypeFlag string + nodeCountFlag string + clusterNameFlag string + clusterTypeFlag string + k3sServersPrivateIpsFlag []string + k3sServersPublicIpsFlag []string + k3sSshUserflag string + k3sSshPrivateKeyflag string + K3sServersArgsFlags []string + dnsProviderFlag string + domainNameFlag string + githubOrgFlag string + gitlabGroupFlag string + gitProviderFlag string + gitProtocolFlag string + gitopsTemplateURLFlag string + gitopsTemplateBranchFlag string + useTelemetryFlag bool + forceDestroyFlag bool + + // RootCredentials + copyArgoCDPasswordToClipboardFlag bool + copyKbotPasswordToClipboardFlag bool + copyVaultPasswordToClipboardFlag bool + + // Supported providers + supportedDNSProviders = []string{"cloudflare"} + supportedGitProviders = []string{"github", "gitlab"} + + // Supported git providers + supportedGitProtocolOverride = []string{"https", "ssh"} +) + +func NewCommand() *cobra.Command { + k3sCmd := &cobra.Command{ + Use: "k3s", + Short: "kubefirst K3s installation", + Long: "kubefirst k3s on premises installation", + } + + // on error, doesnt show helper/usage + k3sCmd.SilenceUsage = true + + // wire up new commands + k3sCmd.AddCommand(Create(), Destroy(), RootCredentials()) + + return k3sCmd +} + +func Create() *cobra.Command { + createCmd := &cobra.Command{ + Use: "create", + Short: "create the kubefirst platform running on premise", + TraverseChildren: true, + RunE: createK3s, + // PreRun: common.CheckDocker, + } + + // todo review defaults and update descriptions + createCmd.Flags().StringVar(&alertsEmailFlag, "alerts-email", "", "email address for let's encrypt certificate notifications (required)") + createCmd.MarkFlagRequired("alerts-email") + createCmd.Flags().BoolVar(&ciFlag, "ci", false, "if running kubefirst in ci, set this flag to disable interactive features") + createCmd.Flags().StringVar(&cloudRegionFlag, "cloud-region", "on-premise", "NOT USED, PRESENT FOR COMPATIBILITY ISSUE") + createCmd.Flags().StringVar(&nodeTypeFlag, "node-type", "on-premise", "NOT USED, PRESENT FOR COMPATIBILITY ISSUE") + createCmd.Flags().StringVar(&nodeCountFlag, "node-count", "3", "NOT USED, PRESENT FOR COMPATIBILITY ISSUE") + createCmd.Flags().StringVar(&clusterNameFlag, "cluster-name", "kubefirst", "the name of the cluster to create") + createCmd.Flags().StringVar(&clusterTypeFlag, "cluster-type", "mgmt", "the type of cluster to create (i.e. mgmt|workload)") + createCmd.Flags().StringSliceVar(&k3sServersPrivateIpsFlag, "servers-private-ips", []string{}, "the list of k3s (servers) private ip x.x.x.x,y.y.y.y comma separated (required)") + createCmd.MarkFlagRequired("servers-private-ips") + createCmd.Flags().StringSliceVar(&k3sServersPublicIpsFlag, "servers-public-ips", []string{}, "the list of k3s (servers) public ip x.x.x.x,y.y.y.y comma separated (required)") + createCmd.Flags().StringSliceVar(&K3sServersArgsFlags, "servers-args", []string{"--disable traefik", "--write-kubeconfig-mode 644"}, "list of k3s extras args to add to the k3s server installation,comma separated in between quote, if --servers-publis-ips --tls-san is added to default --servers-args") + createCmd.Flags().StringVar(&k3sSshUserflag, "ssh-user", "root", "the user used to log into servers with ssh connection") + createCmd.Flags().StringVar(&k3sSshPrivateKeyflag, "ssh-privatekey", "", "the private key used to log into servers with ssh connection") + createCmd.MarkFlagRequired("ssh-privatekey") + createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "cloudflare", fmt.Sprintf("the dns provider - one of: %s", supportedDNSProviders)) + createCmd.Flags().StringVar(&domainNameFlag, "domain-name", "", "the cloudProvider DNS Name to use for DNS records (i.e. your-domain.com|subdomain.your-domain.com) (required)") + createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %s", supportedGitProviders)) + createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %s", supportedGitProtocolOverride)) + createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "the GitHub organization for the new gitops and metaphor repositories - required if using github") + createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "the GitLab group for the new gitops and metaphor projects - required if using gitlab") + createCmd.Flags().StringVar(&gitopsTemplateBranchFlag, "gitops-template-branch", "", "the branch to clone for the gitops-template repository") + createCmd.Flags().StringVar(&gitopsTemplateURLFlag, "gitops-template-url", "https://github.com/kubefirst/gitops-template.git", "the fully qualified url to the gitops-template repository to clone") + createCmd.Flags().BoolVar(&useTelemetryFlag, "use-telemetry", true, "whether to emit telemetry") + createCmd.Flags().BoolVar(&forceDestroyFlag, "force-destroy", false, "allows force destruction on objects (helpful for test environments, defaults to false)") + return createCmd +} + +func Destroy() *cobra.Command { + destroyCmd := &cobra.Command{ + Use: "destroy", + Short: "destroy the kubefirst platform", + Long: "destroy the kubefirst platform running in k3s cluster", + RunE: common.Destroy, + // PreRun: common.CheckDocker, + } + + return destroyCmd +} + +func RootCredentials() *cobra.Command { + authCmd := &cobra.Command{ + Use: "root-credentials", + Short: "retrieve root authentication information for platform components", + Long: "retrieve root authentication information for platform components", + RunE: common.GetRootCredentials, + } + + authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the argocd password to the clipboard (optional)") + authCmd.Flags().BoolVar(©KbotPasswordToClipboardFlag, "kbot", false, "copy the kbot password to the clipboard (optional)") + authCmd.Flags().BoolVar(©VaultPasswordToClipboardFlag, "vault", false, "copy the vault password to the clipboard (optional)") + + return authCmd +} diff --git a/cmd/k3s/create.go b/cmd/k3s/create.go new file mode 100644 index 000000000..ed0b28df5 --- /dev/null +++ b/cmd/k3s/create.go @@ -0,0 +1,117 @@ +/* +Copyright (C) 2021-2023, Kubefirst + +This program is licensed under MIT. +See the LICENSE file for more details. +*/ +package k3s + +import ( + "fmt" + + "github.com/rs/zerolog/log" + + "github.com/kubefirst/kubefirst/internal/cluster" + "github.com/kubefirst/kubefirst/internal/gitShim" + "github.com/kubefirst/kubefirst/internal/launch" + "github.com/kubefirst/kubefirst/internal/progress" + "github.com/kubefirst/kubefirst/internal/provision" + "github.com/kubefirst/kubefirst/internal/utilities" + "github.com/kubefirst/runtime/pkg" + internalssh "github.com/kubefirst/runtime/pkg/ssh" + "github.com/spf13/cobra" + "github.com/spf13/viper" + _ "k8s.io/client-go/plugin/pkg/client/auth" +) + +func createK3s(cmd *cobra.Command, args []string) error { + cliFlags, err := utilities.GetFlags(cmd, "k3s") + if err != nil { + progress.Error(err.Error()) + return nil + } + + progress.DisplayLogHints(20) + + err = ValidateProvidedFlags(cliFlags.GitProvider) + if err != nil { + progress.Error(err.Error()) + return nil + } + + // If cluster setup is complete, return + clusterSetupComplete := viper.GetBool("kubefirst-checks.cluster-install-complete") + if clusterSetupComplete { + err = fmt.Errorf("this cluster install process has already completed successfully") + progress.Error(err.Error()) + return nil + } + + utilities.CreateK1ClusterDirectory(clusterNameFlag) + + gitAuth, err := gitShim.ValidateGitCredentials(cliFlags.GitProvider, cliFlags.GithubOrg, cliFlags.GitlabGroup) + if err != nil { + progress.Error(err.Error()) + return nil + } + + executionControl := viper.GetBool(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider)) + if !executionControl { + newRepositoryNames := []string{"gitops", "metaphor"} + newTeamNames := []string{"admins", "developers"} + + initGitParameters := gitShim.GitInitParameters{ + GitProvider: gitProviderFlag, + GitToken: gitAuth.Token, + GitOwner: gitAuth.Owner, + Repositories: newRepositoryNames, + Teams: newTeamNames, + } + err = gitShim.InitializeGitProvider(&initGitParameters) + if err != nil { + progress.Error(err.Error()) + return nil + } + } + viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider), true) + viper.WriteConfig() + + k3dClusterCreationComplete := viper.GetBool("launch.deployed") + if !k3dClusterCreationComplete { + launch.Up(nil, true, cliFlags.UseTelemetry) + } + + err = pkg.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") + if err != nil { + progress.Error("unable to start kubefirst api") + } + + provision.CreateMgmtCluster(gitAuth, cliFlags) + + return nil +} + +func ValidateProvidedFlags(gitProvider string) error { + progress.AddStep("Validate provided flags") + + switch gitProvider { + case "github": + key, err := internalssh.GetHostKey("github.com") + if err != nil { + return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy") + } else { + log.Info().Msgf("%s %s\n", "github.com", key.Type()) + } + case "gitlab": + key, err := internalssh.GetHostKey("gitlab.com") + if err != nil { + return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy") + } else { + log.Info().Msgf("%s %s\n", "gitlab.com", key.Type()) + } + } + + progress.CompleteStep("Validate provided flags") + + return nil +} diff --git a/internal/progress/message.go b/internal/progress/message.go index ffb33b524..9de44b1ae 100644 --- a/internal/progress/message.go +++ b/internal/progress/message.go @@ -104,6 +104,9 @@ func DisplaySuccessMessage(cluster types.Cluster) successMsg { case "vultr": cloudCliKubeconfig = fmt.Sprintf("vultr-cli kubernetes config %s", cluster.ClusterName) + + case "k3s": + cloudCliKubeconfig = fmt.Sprint(("use the kubeconfig file outputed from terraform to acces to the cluster")) break } diff --git a/internal/types/flags.go b/internal/types/flags.go index e4910b57d..da566aaf7 100644 --- a/internal/types/flags.go +++ b/internal/types/flags.go @@ -28,4 +28,9 @@ type CliFlags struct { NodeType string NodeCount string InstallCatalogApps string + K3sSshUser string + K3sSshPrivateKey string + K3sServersPrivateIps []string + K3sServersPublicIps []string + K3sServersArgs []string } diff --git a/internal/utilities/flags.go b/internal/utilities/flags.go index 20afd4c72..c7d15270e 100644 --- a/internal/utilities/flags.go +++ b/internal/utilities/flags.go @@ -136,6 +136,44 @@ func GetFlags(cmd *cobra.Command, cloudProvider string) (types.CliFlags, error) cliFlags.GoogleProject = googleProject } + // TODO: reafactor this part + if cloudProvider == "k3s" { + k3sServersPrivateIps, err := cmd.Flags().GetStringSlice("servers-private-ips") + if err != nil { + progress.Error(err.Error()) + return cliFlags, err + } + cliFlags.K3sServersPrivateIps = k3sServersPrivateIps + + k3sServersPublicIps, err := cmd.Flags().GetStringSlice("servers-public-ips") + if err != nil { + progress.Error(err.Error()) + return cliFlags, err + } + cliFlags.K3sServersPublicIps = k3sServersPublicIps + + k3sSshUserFlag, err := cmd.Flags().GetString("ssh-user") + if err != nil { + progress.Error(err.Error()) + return cliFlags, err + } + cliFlags.K3sSshUser = k3sSshUserFlag + + k3sSshPrivateKeyFlag, err := cmd.Flags().GetString("ssh-privatekey") + if err != nil { + progress.Error(err.Error()) + return cliFlags, err + } + cliFlags.K3sSshPrivateKey = k3sSshPrivateKeyFlag + + K3sServersArgsFlags, err := cmd.Flags().GetStringSlice("servers-args") + if err != nil { + progress.Error(err.Error()) + return cliFlags, err + } + cliFlags.K3sServersArgs = K3sServersArgsFlags + } + cliFlags.AlertsEmail = alertsEmailFlag cliFlags.CloudRegion = cloudRegionFlag cliFlags.ClusterName = clusterNameFlag @@ -162,6 +200,13 @@ func GetFlags(cmd *cobra.Command, cloudProvider string) (types.CliFlags, error) viper.Set("flags.git-protocol", cliFlags.GitProtocol) viper.Set("flags.cloud-region", cliFlags.CloudRegion) viper.Set("kubefirst.cloud-provider", cloudProvider) + if cloudProvider == "k3s" { + viper.Set("flags.servers-private-ips", cliFlags.K3sServersPrivateIps) + viper.Set("flags.servers-public-ips", cliFlags.K3sServersPublicIps) + viper.Set("flags.ssh-user", cliFlags.K3sSshUser) + viper.Set("flags.ssh-privatekey", cliFlags.K3sSshPrivateKey) + viper.Set("flags.servers-args", cliFlags.K3sServersArgs) + } viper.WriteConfig() return cliFlags, nil diff --git a/internal/utilities/utilities.go b/internal/utilities/utilities.go index 8412ab5b8..fe9222c2a 100644 --- a/internal/utilities/utilities.go +++ b/internal/utilities/utilities.go @@ -109,7 +109,7 @@ func CreateClusterRecordFromRaw( case "civo": cl.CivoAuth.Token = os.Getenv("CIVO_TOKEN") case "aws": - //ToDo: where to get credentials? + // ToDo: where to get credentials? cl.AWSAuth.AccessKeyID = viper.GetString("kubefirst.state-store-creds.access-key-id") cl.AWSAuth.SecretAccessKey = viper.GetString("kubefirst.state-store-creds.secret-access-key-id") cl.AWSAuth.SessionToken = viper.GetString("kubefirst.state-store-creds.token") @@ -192,7 +192,7 @@ func CreateClusterDefinitionRecordFromRaw(gitAuth apiTypes.GitAuth, cliFlags typ case "civo": cl.CivoAuth.Token = os.Getenv("CIVO_TOKEN") case "aws": - //ToDo: where to get credentials? + // ToDo: where to get credentials? cl.AWSAuth.AccessKeyID = viper.GetString("kubefirst.state-store-creds.access-key-id") cl.AWSAuth.SecretAccessKey = viper.GetString("kubefirst.state-store-creds.secret-access-key-id") cl.AWSAuth.SessionToken = viper.GetString("kubefirst.state-store-creds.token") @@ -203,6 +203,12 @@ func CreateClusterDefinitionRecordFromRaw(gitAuth apiTypes.GitAuth, cliFlags typ cl.DigitaloceanAuth.SpacesSecret = os.Getenv("DO_SPACES_SECRET") case "vultr": cl.VultrAuth.Token = os.Getenv("VULTR_API_KEY") + case "k3s": + cl.K3sAuth.K3sServersPrivateIps = viper.GetStringSlice("flags.servers-private-ips") + cl.K3sAuth.K3sServersPublicIps = viper.GetStringSlice("flags.servers-public-ips") + cl.K3sAuth.K3sSshUser = viper.GetString("flags.ssh-user") + cl.K3sAuth.K3sSshPrivateKey = viper.GetString("flags.ssh-privatekey") + cl.K3sAuth.K3sServersArgs = viper.GetStringSlice("flags.servers-args") case "google": jsonFilePath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")