Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Onboarding CLI | Flux bootstrapping and git authentication credentials #3578

Merged
merged 21 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions cmd/gitops/app/bootstrap/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,21 @@ type bootstrapFlags struct {
domainType string
domain string

// private key flags
// git auth type
gitAuthType string

// private key (ssh-auth) flags
privateKeyPath string
privateKeyPassword string
sshRepoURL string

// https git auth flags
gitUsername string
gitToken string
httpsRepoURL string

branch string
repoPath string

// oidc flags
discoveryURL string
Expand All @@ -67,6 +79,13 @@ func Command(opts *config.Options) *cobra.Command {
cmd.Flags().StringVarP(&flags.domainType, "domain-type", "t", "", "dashboard domain type: could be 'localhost' or 'externaldns'")
cmd.Flags().StringVarP(&flags.domain, "domain", "d", "", "indicate the domain to use in case of using `externaldns`")
cmd.Flags().StringVarP(&flags.version, "version", "v", "", "version of Weave GitOps Enterprise (should be from the latest 3 versions)")
cmd.PersistentFlags().StringVarP(&flags.gitAuthType, "git-auth-type", "g", "", "git authentication type. choose between (ssh, https)")
cmd.PersistentFlags().StringVarP(&flags.gitUsername, "git-username", "", "", "git username used in https authentication type")
cmd.PersistentFlags().StringVarP(&flags.gitToken, "git-token", "", "", "git token used in https authentication type")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be token or password? i could see https://fluxcd.io/flux/cmd/flux_bootstrap_git/ is password

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah in flux is password but it's very confusing as adding the git https basic auth with password doesn't work anymore and only accept token

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes but i think that flux gets it from http where basic auth is username and password https://en.wikipedia.org/wiki/Basic_access_authentication

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh we add --token-auth to flux configuration so that it can handle it as a token

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess flux is correct calling it password

https://fluxcd.io/flux/components/source/gitrepositories/#basic-access-authentication

as it is how is defined in the standard https://datatracker.ietf.org/doc/html/rfc7617#section-2

we could review this in the catchup too

cmd.PersistentFlags().StringVarP(&flags.branch, "git-branch", "b", "", "git branch for your flux repository (example: main)")
cmd.PersistentFlags().StringVarP(&flags.repoPath, "git-repo-path", "r", "", "git path for your flux repository (example: clusters/my-cluster)")
cmd.PersistentFlags().StringVarP(&flags.sshRepoURL, "ssh-repo-url", "", "", "ssh git url for you flux repository (example: ssh://[email protected]/my-org-name/my-repo-name)")
cmd.PersistentFlags().StringVarP(&flags.sshRepoURL, "https-repo-url", "", "", "https git url for you flux repository (example: https://github.com/my-org-name/my-repo-name)")
enekofb marked this conversation as resolved.
Show resolved Hide resolved
cmd.PersistentFlags().StringVarP(&flags.privateKeyPath, "private-key", "k", "", "private key path. This key will be used to push the Weave GitOps Enterprise's resources to the default cluster repository")
cmd.PersistentFlags().StringVarP(&flags.privateKeyPassword, "private-key-password", "c", "", "private key password. If the private key is encrypted using password")
cmd.PersistentFlags().StringVarP(&flags.discoveryURL, "discovery-url", "", "", "OIDC discovery URL")
Expand All @@ -92,7 +111,16 @@ func getBootstrapCmdRun(opts *config.Options) func(*cobra.Command, []string) err
WithVersion(flags.version).
WithDomainType(flags.domainType).
WithDomain(flags.domain).
WithPrivateKey(flags.privateKeyPath, flags.privateKeyPassword).
WithGitAuthentication(flags.gitAuthType,
flags.privateKeyPath,
flags.privateKeyPassword,
flags.sshRepoURL,
flags.httpsRepoURL,
flags.gitUsername,
flags.gitToken,
flags.branch,
flags.repoPath,
).
WithOIDCConfig(flags.discoveryURL, flags.clientID, flags.clientSecret, true).
Build()

Expand Down
11 changes: 10 additions & 1 deletion cmd/gitops/app/bootstrap/cmd_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,16 @@ func getAuthCmdRun(opts *config.Options) func(*cobra.Command, []string) error {
c, err := steps.NewConfigBuilder().
WithLogWriter(cliLogger).
WithKubeconfig(opts.Kubeconfig).
WithPrivateKey(flags.privateKeyPath, flags.privateKeyPassword).
WithGitAuthentication(flags.gitAuthType,
flags.privateKeyPath,
flags.privateKeyPassword,
flags.sshRepoURL,
flags.httpsRepoURL,
flags.gitUsername,
flags.gitToken,
flags.branch,
flags.repoPath,
).
WithOIDCConfig(flags.discoveryURL, flags.clientID, flags.clientSecret, false).
Build()

Expand Down
5 changes: 4 additions & 1 deletion pkg/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ func Bootstrap(config steps.Config) error {
// TODO have a single workflow source of truth and documented in https://docs.gitops.weave.works/docs/0.33.0/enterprise/getting-started/install-enterprise/
var steps = []steps.BootstrapStep{
steps.VerifyFluxInstallation,
steps.NewAskBootstrapFluxStep(config),
steps.NewSelectGitAuthType(config),
steps.NewBootstrapFluxUsingSSH(config),
steps.NewBootstrapFluxUsingHTTPS(config),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please share the design considerations used for selecting using these steps over other design like having a single step called fluxinstall. please shar

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to be

steps.NewAskBootstrapFluxStep(config),
steps.NewFluxGitRepositoryConfig(config),
steps.NewBootstrapFlux(config),

the currently implemented logic, process all the inputs first, then execute the step function
meanwhile some inputs rely on other inputs from the previous step and could change accordingly

for example in case of passing a repo-url with ssh scheme, we need to only ask about private key and private key password

and in case of passing a repo-url with https scheme, then we need to ask about username and token

and since we handle input first, then process, then out. if we don't pass the value to this step it'll ask all of them anyway

steps.CheckEntitlementSecret,
steps.NewAskPrivateKeyStep(config),
steps.NewSelectWgeVersionStep(config),
steps.NewAskAdminCredsSecretStep(config),
steps.NewSelectDomainType(config),
Expand Down
3 changes: 2 additions & 1 deletion pkg/bootstrap/bootstrap_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ func bootstrapOIDC(config steps.Config) error {
var steps = []steps.BootstrapStep{
steps.VerifyFluxInstallation,
steps.CheckEntitlementSecret,
steps.NewAskPrivateKeyStep(config),
steps.NewBootstrapFluxUsingSSH(config),
steps.NewBootstrapFluxUsingHTTPS(config),
steps.NewInstallOIDCStep(config),
steps.NewOIDCConfigStep(config),
}
Expand Down
22 changes: 8 additions & 14 deletions pkg/bootstrap/steps/admin_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ var getUsernameInput = StepInput{
Type: stringInput,
Msg: adminUsernameMsg,
DefaultValue: defaultAdminUsername,
Valuesfn: canAskForCreds,
Enabled: canAskForCreds,
}

var getPasswordInput = StepInput{
Name: Password,
Type: passwordInput,
Msg: adminPasswordMsg,
DefaultValue: defaultAdminPassword,
Valuesfn: canAskForCreds,
Enabled: canAskForCreds,
Required: true,
}

Expand All @@ -53,7 +53,7 @@ func NewAskAdminCredsSecretStep(config Config) BootstrapStep {
Type: confirmInput,
Msg: existingCredsMsg,
DefaultValue: "",
Valuesfn: isExistingAdminSecret,
Enabled: isExistingAdminSecret,
StepInformation: fmt.Sprintf(adminSecretExistsMsgFormat, adminSecretName, WGEDefaultNamespace),
},
}
Expand Down Expand Up @@ -97,7 +97,7 @@ func createCredentials(input []StepInput, c *Config) ([]StepOutput, error) {
}
}

if existing, _ := isExistingAdminSecret(input, c); existing.(bool) {
if existing := isExistingAdminSecret(input, c); existing {
if continueWithExistingCreds != confirmYes {
return []StepOutput{}, fmt.Errorf(existingCredsExitMsg, adminSecretName, WGEDefaultNamespace)
} else {
Expand Down Expand Up @@ -138,17 +138,11 @@ func createCredentials(input []StepInput, c *Config) ([]StepOutput, error) {
// isExistingAdminSecret checks for admin secret on management cluster
// returns true if admin secret is already on the cluster
// returns false if no admin secret on the cluster
func isExistingAdminSecret(input []StepInput, c *Config) (interface{}, error) {
func isExistingAdminSecret(input []StepInput, c *Config) bool {
enekofb marked this conversation as resolved.
Show resolved Hide resolved
_, err := utils.GetSecret(c.KubernetesClient, adminSecretName, WGEDefaultNamespace)
if err != nil {
return false, nil
}
return true, nil
return err == nil
}

func canAskForCreds(input []StepInput, c *Config) (interface{}, error) {
if ask, _ := isExistingAdminSecret(input, c); ask.(bool) {
return false, nil
}
return true, nil
func canAskForCreds(input []StepInput, c *Config) bool {
return !isExistingAdminSecret(input, c)
enekofb marked this conversation as resolved.
Show resolved Hide resolved
}
44 changes: 44 additions & 0 deletions pkg/bootstrap/steps/ask_bootstrap_flux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package steps

import "fmt"

const (
bootstrapFluxMsg = "do you want to bootstrap flux using the generic way"
)

var (
bootstrapFLuxQuestion = StepInput{
Name: bootstrapFlux,
Type: confirmInput,
Msg: bootstrapFluxMsg,
Enabled: canAskForGitConfig,
}
)

func NewAskBootstrapFluxStep(config Config) BootstrapStep {
return BootstrapStep{
Name: "bootstrap flux",
Input: []StepInput{
bootstrapFLuxQuestion,
},
Step: askBootstrapFlux,
}
}

func askBootstrapFlux(input []StepInput, c *Config) ([]StepOutput, error) {
if !canAskForGitConfig(input, c) {
return []StepOutput{}, nil
}
for _, param := range input {
if param.Name == bootstrapFlux {
fluxBootstrapRes, ok := param.Value.(string)
if ok {
if fluxBootstrapRes != "y" {
return []StepOutput{}, fmt.Errorf("flux bootstrapped error: %s", fluxRecoverMsg)
}

}
}
}
return []StepOutput{}, nil
}
173 changes: 173 additions & 0 deletions pkg/bootstrap/steps/bootstrap_flux_https.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package steps

import (
"fmt"

"github.com/weaveworks/weave-gitops/pkg/runner"
)

const (
// https authentication
gitHttpsRepoURLMsg = "please enter your git repository url (example: https://github.com/my-org-name/my-repo-name)"
gitUserNameMsg = "please enter your git username"
gitTokenMsg = "please enter your git authentication token with valid creds"
)

const (
httpsAuthStepName = "git https config"
)

var (
getHttpsRepoURL = StepInput{
Name: httpsRepoURL,
Type: stringInput,
Msg: gitHttpsRepoURLMsg,
DefaultValue: "",
Enabled: canAskForHttpsGitConfig,
}

getHttpsRepoBranch = StepInput{
Name: branch,
Type: stringInput,
Msg: gitRepoBranchMsg,
DefaultValue: defaultBranch,
Enabled: canAskForHttpsGitConfig,
}

getHttpsRepoPath = StepInput{
Name: repoPath,
Type: stringInput,
Msg: gitRepoPathMsg,
DefaultValue: defaultPath,
Enabled: canAskForHttpsGitConfig,
}

getGitUsername = StepInput{
Name: UserName,
Type: stringInput,
Msg: gitUserNameMsg,
DefaultValue: "",
Enabled: canAskForHttpsGitCreds,
}

getGitToken = StepInput{
Name: gitToken,
Type: passwordInput,
Msg: gitTokenMsg,
DefaultValue: "",
Enabled: canAskForHttpsGitCreds,
Required: true,
}
)

// NewBootstrapFluxUsingHTTPS step to bootstrap flux and configuring git using https
func NewBootstrapFluxUsingHTTPS(config Config) BootstrapStep {
// create steps
inputs := []StepInput{}
if config.HttpsRepoURL == "" {
inputs = append(inputs, getHttpsRepoURL)
}

if config.Branch == "" {
inputs = append(inputs, getHttpsRepoBranch)
}

if config.RepoPath == "" {
inputs = append(inputs, getHttpsRepoPath)
}

if config.GitUsername == "" {
inputs = append(inputs, getGitUsername)
}
if config.GitToken == "" {
inputs = append(inputs, getGitToken)
}

return BootstrapStep{
Name: httpsAuthStepName,
Input: inputs,
Step: createGitHttpsConfig,
}
}

func createGitHttpsConfig(input []StepInput, c *Config) ([]StepOutput, error) {
for _, param := range input {
if param.Name == httpsRepoURL {
repoURL, ok := param.Value.(string)
if ok {
c.HttpsRepoURL = repoURL
}
}
if param.Name == branch {
repoBranch, ok := param.Value.(string)
if ok {
c.Branch = repoBranch
}
}

if param.Name == repoPath {
path, ok := param.Value.(string)
if ok {
c.RepoPath = path
}
}

if param.Name == gitUserName {
username, ok := param.Value.(string)
if ok {
c.GitUsername = username
}
}

if param.Name == gitToken {
token, ok := param.Value.(string)
if ok {
c.GitToken = token
}
}
}
if !canAskForHttpsGitConfig(input, c) {
return []StepOutput{}, nil
}
c.Logger.Waitingf("bootstrapping flux ...")
err := bootstrapFluxHttps(c)
if err != nil {
return []StepOutput{}, fmt.Errorf("failed to bootstrap flux: %v", err)
}

return []StepOutput{}, nil
}

func bootstrapFluxHttps(c *Config) error {
var runner runner.CLIRunner
out, err := runner.Run("flux",
"bootstrap",
"git",
"--url", c.HttpsRepoURL,
"--branch", c.Branch,
"--path", c.RepoPath,
"--username", c.GitUsername,
"--password", c.GitToken,
"--token-auth", "true",
"-s",
)

if err != nil {
return fmt.Errorf("%v:%v", err, string(out))
}
c.Logger.Successf("successfully bootstrapped flux!")
return nil
}

// canAskForHttpsGitConfig check when ask for gitconfig
func canAskForHttpsGitConfig(input []StepInput, c *Config) bool {
if !c.FluxInstallated {
return c.GitAuthType == httpsAuthType
}
return false
}

// canAskForHttpsGitCreds check when ask for gitconfig
func canAskForHttpsGitCreds(input []StepInput, c *Config) bool {
return c.GitAuthType == httpsAuthType
}
Loading