Skip to content

Commit

Permalink
feat(azure): add azure beta command
Browse files Browse the repository at this point in the history
  • Loading branch information
mrsimonemms committed Sep 23, 2024
1 parent 508bac1 commit b2c0ade
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 0 deletions.
140 changes: 140 additions & 0 deletions cmd/azure/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
Copyright (C) 2021-2024, Kubefirst
This program is licensed under MIT.
See the LICENSE file for more details.
*/

package azure

import (
"fmt"

"github.com/konstructio/kubefirst-api/pkg/constants"
"github.com/konstructio/kubefirst/internal/common"
"github.com/konstructio/kubefirst/internal/progress"
"github.com/spf13/cobra"
)

var (
// Create
alertsEmailFlag string
ciFlag bool
cloudRegionFlag string
clusterNameFlag string
clusterTypeFlag string
dnsProviderFlag string
domainNameFlag string
subdomainNameFlag string
githubOrgFlag string
gitlabGroupFlag string
gitProviderFlag string
gitProtocolFlag string
gitopsTemplateURLFlag string
gitopsTemplateBranchFlag string
useTelemetryFlag bool
forceDestroyFlag bool
nodeTypeFlag string
nodeCountFlag string
installCatalogApps string
installKubefirstProFlag bool

// RootCredentials
copyArgoCDPasswordToClipboardFlag bool
copyKbotPasswordToClipboardFlag bool
copyVaultPasswordToClipboardFlag bool

// Supported providers
supportedDNSProviders = []string{"azure", "cloudflare"}
supportedGitProviders = []string{"github", "gitlab"}

// Supported git providers
supportedGitProtocolOverride = []string{"https", "ssh"}
)

func NewCommand() *cobra.Command {
azureCmd := &cobra.Command{
Use: "azure",
Short: "kubefirst Azure installation",
Long: "kubefirst azure",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("To learn more about azure in kubefirst, run:")
fmt.Println(" kubefirst beta azure --help")

if progress.Progress != nil {
progress.Progress.Quit()
}
},
}

// on error, doesnt show helper/usage
azureCmd.SilenceUsage = true

// wire up new commands
azureCmd.AddCommand(Create(), Destroy(), RootCredentials())

return azureCmd
}

func Create() *cobra.Command {
createCmd := &cobra.Command{
Use: "create",
Short: "create the kubefirst platform running on GCP kubernetes",
TraverseChildren: true,
RunE: createAzure,
}

azureDefaults := constants.GetCloudDefaults().Azure

// 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", "eastus", "the GCP region to provision infrastructure in")
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().StringVar(&nodeCountFlag, "node-count", azureDefaults.NodeCount, "the node count for the cluster")
createCmd.Flags().StringVar(&nodeTypeFlag, "node-type", azureDefaults.InstanceSize, "the instance size of the cluster to create")
createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "azure", fmt.Sprintf("the dns provider - one of: %s", supportedDNSProviders))
createCmd.Flags().StringVar(&subdomainNameFlag, "subdomain", "", "the subdomain to use for DNS records (Cloudflare)")
createCmd.Flags().StringVar(&domainNameFlag, "domain-name", "", "the Azure DNS zone to use for DNS records (i.e. your-domain.com|subdomain.your-domain.com) (required)")
createCmd.MarkFlagRequired("domain-name")
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/konstructio/gitops-template.git", "the fully qualified url to the gitops-template repository to clone")
createCmd.Flags().StringVar(&installCatalogApps, "install-catalog-apps", "", "comma separated values to install after provision")
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)")
createCmd.Flags().BoolVar(&installKubefirstProFlag, "install-kubefirst-pro", true, "whether or not to install kubefirst pro")

return createCmd
}

func Destroy() *cobra.Command {
destroyCmd := &cobra.Command{
Use: "destroy",
Short: "destroy the kubefirst platform",
Long: "destroy the kubefirst platform running in Azure and remove all resources",
RunE: common.Destroy,
}

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(&copyArgoCDPasswordToClipboardFlag, "argocd", false, "copy the argocd password to the clipboard (optional)")
authCmd.Flags().BoolVar(&copyKbotPasswordToClipboardFlag, "kbot", false, "copy the kbot password to the clipboard (optional)")
authCmd.Flags().BoolVar(&copyVaultPasswordToClipboardFlag, "vault", false, "copy the vault password to the clipboard (optional)")

return authCmd
}
141 changes: 141 additions & 0 deletions cmd/azure/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
Copyright (C) 2021-2024, Kubefirst
This program is licensed under MIT.
See the LICENSE file for more details.
*/
package azure

import (
"fmt"
"os"
"strings"

internalssh "github.com/konstructio/kubefirst-api/pkg/ssh"
utils "github.com/konstructio/kubefirst-api/pkg/utils"
"github.com/konstructio/kubefirst/internal/catalog"
"github.com/konstructio/kubefirst/internal/cluster"
"github.com/konstructio/kubefirst/internal/gitShim"
"github.com/konstructio/kubefirst/internal/launch"
"github.com/konstructio/kubefirst/internal/progress"
"github.com/konstructio/kubefirst/internal/provision"
"github.com/konstructio/kubefirst/internal/utilities"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// Environment variables required for authentication. This should be a
// service principal - the Terraform provider docs detail how to create
// one
// @link https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_client_secret.html
var envvarSecrets = []string{
"ARM_CLIENT_ID",
"ARM_CLIENT_SECRET",
"ARM_TENANT_ID",
"ARM_SUBSCRIPTION_ID",
}

func createAzure(cmd *cobra.Command, args []string) error {
cliFlags, err := utilities.GetFlags(cmd, "azure")
if err != nil {
progress.Error(err.Error())
return nil
}

progress.DisplayLogHints(20)

isValid, catalogApps, err := catalog.ValidateCatalogApps(cliFlags.InstallCatalogApps)
if !isValid {
return err
}

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")
isK1Debug := strings.ToLower(os.Getenv("K1_LOCAL_DEBUG")) == "true"

if !k3dClusterCreationComplete && !isK1Debug {
launch.Up(nil, true, cliFlags.UseTelemetry)
}

err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngressURL()), "kubefirst api")
if err != nil {
progress.Error("unable to start kubefirst api")
}

provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps)

return nil
}

func ValidateProvidedFlags(gitProvider string) error {
progress.AddStep("Validate provided flags")

for _, env := range envvarSecrets {
if os.Getenv(env) == "" {
return fmt.Errorf("your %s is not set - please set and re-run your last command", env)
}
}

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())
}
}

return nil
}
2 changes: 2 additions & 0 deletions cmd/beta.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"

"github.com/konstructio/kubefirst/cmd/akamai"
"github.com/konstructio/kubefirst/cmd/azure"
"github.com/konstructio/kubefirst/cmd/google"
"github.com/konstructio/kubefirst/cmd/k3s"
"github.com/konstructio/kubefirst/cmd/vultr"
Expand All @@ -36,6 +37,7 @@ func init() {
cobra.OnInitialize()
betaCmd.AddCommand(
akamai.NewCommand(),
azure.NewCommand(),
k3s.NewCommand(),
google.NewCommand(),
vultr.NewCommand(),
Expand Down
10 changes: 10 additions & 0 deletions internal/utilities/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ func CreateClusterRecordFromRaw(
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")
case "azure":
cl.AzureAuth.ClientID = os.Getenv("ARM_CLIENT_ID")
cl.AzureAuth.ClientSecret = os.Getenv("ARM_CLIENT_SECRET")
cl.AzureAuth.TenantID = os.Getenv("ARM_TENANT_ID")
cl.AzureAuth.SubscriptionID = os.Getenv("ARM_SUBSCRIPTION_ID")
case "digitalocean":
cl.DigitaloceanAuth.Token = os.Getenv("DO_TOKEN")
cl.DigitaloceanAuth.SpacesKey = os.Getenv("DO_SPACES_KEY")
Expand Down Expand Up @@ -191,6 +196,11 @@ func CreateClusterDefinitionRecordFromRaw(gitAuth apiTypes.GitAuth, cliFlags typ
cl.AWSAuth.SecretAccessKey = viper.GetString("kubefirst.state-store-creds.secret-access-key-id")
cl.AWSAuth.SessionToken = viper.GetString("kubefirst.state-store-creds.token")
cl.ECR = cliFlags.ECR
case "azure":
cl.AzureAuth.ClientID = os.Getenv("ARM_CLIENT_ID")
cl.AzureAuth.ClientSecret = os.Getenv("ARM_CLIENT_SECRET")
cl.AzureAuth.TenantID = os.Getenv("ARM_TENANT_ID")
cl.AzureAuth.SubscriptionID = os.Getenv("ARM_SUBSCRIPTION_ID")
case "civo":
cl.CivoAuth.Token = os.Getenv("CIVO_TOKEN")
case "digitalocean":
Expand Down

0 comments on commit b2c0ade

Please sign in to comment.