diff --git a/cmd/flux/bootstrap.go b/cmd/flux/bootstrap.go index ba84a91d..46d7fe38 100644 --- a/cmd/flux/bootstrap.go +++ b/cmd/flux/bootstrap.go @@ -17,26 +17,15 @@ limitations under the License. package main import ( - "context" + "crypto/elliptic" "fmt" - "path/filepath" - "time" + "io/ioutil" "github.com/spf13/cobra" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "sigs.k8s.io/controller-runtime/pkg/client" - - kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" - sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" "github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/utils" - "github.com/fluxcd/flux2/pkg/manifestgen/install" - kus "github.com/fluxcd/flux2/pkg/manifestgen/kustomization" - "github.com/fluxcd/flux2/pkg/manifestgen/sync" - "github.com/fluxcd/flux2/pkg/status" + "github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret" ) var bootstrapCmd = &cobra.Command{ @@ -46,21 +35,36 @@ var bootstrapCmd = &cobra.Command{ } type bootstrapFlags struct { - version string + version string + arch flags.Arch + logLevel flags.LogLevel + + branch string + manifestsPath string + defaultComponents []string extraComponents []string - registry string - imagePullSecret string - branch string + requiredComponents []string + + registry string + imagePullSecret string + + secretName string + tokenAuth bool + keyAlgorithm flags.PublicKeyAlgorithm + keyRSABits flags.RSAKeyBits + keyECDSACurve flags.ECDSACurve + sshHostname string + caFile string + privateKeyFile string + watchAllNamespaces bool networkPolicy bool - manifestsPath string - arch flags.Arch - logLevel flags.LogLevel - requiredComponents []string - tokenAuth bool clusterDomain string tolerationKeys []string + + authorName string + authorEmail string } const ( @@ -72,17 +76,21 @@ var bootstrapArgs = NewBootstrapFlags() func init() { bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", "", "toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases") + bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components, "list of components, accepts comma-separated values") bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil, "list of components in addition to those supplied or defaulted, accepts comma-separated values") + bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd", "container registry where the toolkit images are published") bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.imagePullSecret, "image-pull-secret", "", "Kubernetes secret name used for pulling the toolkit images from a private registry") - bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.arch, "arch", bootstrapArgs.arch.Description()) + bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.branch, "branch", bootstrapDefaultBranch, "default branch (for GitHub this must match the default branch setting for the organization)") + bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.manifestsPath, "manifests", "", "path to the manifest directory") + bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.watchAllNamespaces, "watch-all-namespaces", true, "watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed") bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.networkPolicy, "network-policy", true, @@ -90,12 +98,25 @@ func init() { bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.tokenAuth, "token-auth", false, "when enabled, the personal access token will be used instead of SSH deploy key") bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.logLevel, "log-level", bootstrapArgs.logLevel.Description()) - bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.manifestsPath, "manifests", "", "path to the manifest directory") bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain") bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.tolerationKeys, "toleration-keys", nil, "list of toleration keys used to schedule the components pods onto nodes with matching taints") - bootstrapCmd.PersistentFlags().MarkHidden("manifests") + + bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.secretName, "secret-name", rootArgs.defaults.Namespace, "name of the secret the sync credentials can be found in or stored to") + bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.keyAlgorithm, "ssh-key-algorithm", bootstrapArgs.keyAlgorithm.Description()) + bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.keyRSABits, "ssh-rsa-bits", bootstrapArgs.keyRSABits.Description()) + bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.keyECDSACurve, "ssh-ecdsa-curve", bootstrapArgs.keyECDSACurve.Description()) + bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.sshHostname, "ssh-hostname", "", "SSH hostname, to be used when the SSH host differs from the HTTPS one") + bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates") + bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.privateKeyFile, "private-key-file", "", "path to a private key file used for authenticating to the Git SSH server") + + bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.authorName, "author-name", "Flux", "author name for Git commits") + bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.authorEmail, "author-email", "", "author email for Git commits") + + bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.arch, "arch", bootstrapArgs.arch.Description()) bootstrapCmd.PersistentFlags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64") + bootstrapCmd.PersistentFlags().MarkHidden("manifests") + rootCmd.AddCommand(bootstrapCmd) } @@ -103,6 +124,9 @@ func NewBootstrapFlags() bootstrapFlags { return bootstrapFlags{ logLevel: flags.LogLevel(rootArgs.defaults.LogLevel), requiredComponents: []string{"source-controller", "kustomize-controller"}, + keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.RSAPrivateKeyAlgorithm), + keyRSABits: 2048, + keyECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()}, } } @@ -110,194 +134,39 @@ func bootstrapComponents() []string { return append(bootstrapArgs.defaultComponents, bootstrapArgs.extraComponents...) } -func bootstrapValidate() error { - components := bootstrapComponents() - for _, component := range bootstrapArgs.requiredComponents { - if !utils.ContainsItemString(components, component) { - return fmt.Errorf("component %s is required", component) - } - } - - if err := utils.ValidateComponents(components); err != nil { - return err - } - - return nil -} - -func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) { - if ver, err := getVersion(bootstrapArgs.version); err != nil { - return "", err - } else { - bootstrapArgs.version = ver - } - - manifestsBase := "" - if isEmbeddedVersion(bootstrapArgs.version) { - if err := writeEmbeddedManifests(tmpDir); err != nil { - return "", err - } - manifestsBase = tmpDir - } - - opts := install.Options{ - BaseURL: localManifests, - Version: bootstrapArgs.version, - Namespace: namespace, - Components: bootstrapComponents(), - Registry: bootstrapArgs.registry, - ImagePullSecret: bootstrapArgs.imagePullSecret, - WatchAllNamespaces: bootstrapArgs.watchAllNamespaces, - NetworkPolicy: bootstrapArgs.networkPolicy, - LogLevel: bootstrapArgs.logLevel.String(), - NotificationController: rootArgs.defaults.NotificationController, - ManifestFile: rootArgs.defaults.ManifestFile, - Timeout: rootArgs.timeout, - TargetPath: targetPath, - ClusterDomain: bootstrapArgs.clusterDomain, - TolerationKeys: bootstrapArgs.tolerationKeys, - } - - if localManifests == "" { - opts.BaseURL = rootArgs.defaults.BaseURL - } - - output, err := install.Generate(opts, manifestsBase) - if err != nil { - return "", fmt.Errorf("generating install manifests failed: %w", err) - } - - filePath, err := output.WriteFile(tmpDir) - if err != nil { - return "", fmt.Errorf("generating install manifests failed: %w", err) - } - return filePath, nil -} - -func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error { - kubectlArgs := []string{"apply", "-f", manifestPath} - if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil { - return fmt.Errorf("install failed: %w", err) - } - kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext) - if err != nil { - return fmt.Errorf("install failed: %w", err) - } - statusChecker, err := status.NewStatusChecker(kubeConfig, time.Second, rootArgs.timeout, logger) - if err != nil { - return fmt.Errorf("install failed: %w", err) - } - componentRefs, err := buildComponentObjectRefs(components...) - if err != nil { - return fmt.Errorf("install failed: %w", err) - } - logger.Waitingf("verifying installation") - if err := statusChecker.Assess(componentRefs...); err != nil { - return fmt.Errorf("install failed") - } - return nil -} - -func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) (string, error) { - opts := sync.Options{ - Name: name, - Namespace: namespace, - URL: url, - Branch: branch, - Interval: interval, - Secret: namespace, - TargetPath: targetPath, - ManifestFile: sync.MakeDefaultOptions().ManifestFile, +func buildEmbeddedManifestBase() (string, error) { + if !isEmbeddedVersion(bootstrapArgs.version) { + return "", nil } - - manifest, err := sync.Generate(opts) - if err != nil { - return "", fmt.Errorf("generating install manifests failed: %w", err) - } - - output, err := manifest.WriteFile(tmpDir) + tmpBaseDir, err := ioutil.TempDir("", "flux-manifests-") if err != nil { return "", err } - outputDir := filepath.Dir(output) - - kusOpts := kus.MakeDefaultOptions() - kusOpts.BaseDir = tmpDir - kusOpts.TargetPath = filepath.Dir(manifest.Path) - - kustomization, err := kus.Generate(kusOpts) - if err != nil { - return "", err - } - if _, err = kustomization.WriteFile(tmpDir); err != nil { + if err := writeEmbeddedManifests(tmpBaseDir); err != nil { return "", err } - - return outputDir, nil + return tmpBaseDir, nil } -func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, manifestsPath string) error { - kubectlArgs := []string{"apply", "-k", manifestsPath} - if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil { - return err - } - - logger.Waitingf("waiting for cluster sync") - - var gitRepository sourcev1.GitRepository - if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout, - isGitRepositoryReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &gitRepository)); err != nil { - return err +func bootstrapValidate() error { + components := bootstrapComponents() + for _, component := range bootstrapArgs.requiredComponents { + if !utils.ContainsItemString(components, component) { + return fmt.Errorf("component %s is required", component) + } } - var kustomization kustomizev1.Kustomization - if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout, - isKustomizationReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &kustomization)); err != nil { + if err := utils.ValidateComponents(components); err != nil { return err } return nil } -func shouldInstallManifests(ctx context.Context, kubeClient client.Client, namespace string) bool { - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: namespace, - } - var kustomization kustomizev1.Kustomization - if err := kubeClient.Get(ctx, namespacedName, &kustomization); err != nil { - return true +func mapTeamSlice(s []string, defaultPermission string) map[string]string { + m := make(map[string]string, len(s)) + for _, v := range s { + m[v] = defaultPermission } - - return kustomization.Status.LastAppliedRevision == "" -} - -func shouldCreateDeployKey(ctx context.Context, kubeClient client.Client, namespace string) bool { - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: namespace, - } - - var existing corev1.Secret - if err := kubeClient.Get(ctx, namespacedName, &existing); err != nil { - return true - } - return false -} - -func checkIfBootstrapPathDiffers(ctx context.Context, kubeClient client.Client, namespace string, path string) (string, bool) { - namespacedName := types.NamespacedName{ - Name: namespace, - Namespace: namespace, - } - var fluxSystemKustomization kustomizev1.Kustomization - err := kubeClient.Get(ctx, namespacedName, &fluxSystemKustomization) - if err != nil { - return "", false - } - if fluxSystemKustomization.Spec.Path == path { - return "", false - } - - return fluxSystemKustomization.Spec.Path, true + return m } diff --git a/cmd/flux/bootstrap_github.go b/cmd/flux/bootstrap_github.go index 7ccd4907..747d9829 100644 --- a/cmd/flux/bootstrap_github.go +++ b/cmd/flux/bootstrap_github.go @@ -20,20 +20,20 @@ import ( "context" "fmt" "io/ioutil" - "net/url" "os" - "path" - "path/filepath" "time" - "github.com/fluxcd/pkg/git" + "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/spf13/cobra" - corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/yaml" + "github.com/fluxcd/flux2/internal/bootstrap" + "github.com/fluxcd/flux2/internal/bootstrap/git/gogit" + "github.com/fluxcd/flux2/internal/bootstrap/provider" "github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/utils" + "github.com/fluxcd/flux2/pkg/manifestgen/install" "github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret" + "github.com/fluxcd/flux2/pkg/manifestgen/sync" ) var bootstrapGitHubCmd = &cobra.Command{ @@ -71,19 +71,21 @@ the bootstrap command will perform an upgrade if needed.`, } type githubFlags struct { - owner string - repository string - interval time.Duration - personal bool - private bool - hostname string - path flags.SafeRelativePath - teams []string - sshHostname string + owner string + repository string + interval time.Duration + personal bool + private bool + hostname string + path flags.SafeRelativePath + teams []string + readWriteKey bool } const ( ghDefaultPermission = "maintain" + ghDefaultDomain = "github.com" + ghTokenEnvVar = "GITHUB_TOKEN" ) var githubArgs githubFlags @@ -95,17 +97,17 @@ func init() { bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.personal, "personal", false, "if true, the owner is assumed to be a GitHub user; otherwise an org") bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.private, "private", true, "if true, the repository is assumed to be private") bootstrapGitHubCmd.Flags().DurationVar(&githubArgs.interval, "interval", time.Minute, "sync interval") - bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname") - bootstrapGitHubCmd.Flags().StringVar(&githubArgs.sshHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one") + bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", ghDefaultDomain, "GitHub hostname") bootstrapGitHubCmd.Flags().Var(&githubArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path") + bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.readWriteKey, "read-write-key", false, "if true, the deploy key is configured with read/write permissions") bootstrapCmd.AddCommand(bootstrapGitHubCmd) } func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error { - ghToken := os.Getenv(git.GitHubTokenName) + ghToken := os.Getenv(ghTokenEnvVar) if ghToken == "" { - return fmt.Errorf("%s environment variable not found", git.GitHubTokenName) + return fmt.Errorf("%s environment variable not found", ghTokenEnvVar) } if err := bootstrapValidate(); err != nil { @@ -120,205 +122,124 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error { return err } - usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers( - ctx, - kubeClient, - rootArgs.namespace, - filepath.ToSlash(githubArgs.path.String()), - ) - - if bootstrapPathDiffers { - return fmt.Errorf("cluster already bootstrapped to %v path", usedPath) + // Manifest base + if ver, err := getVersion(bootstrapArgs.version); err == nil { + bootstrapArgs.version = ver } - - repository, err := git.NewRepository( - githubArgs.repository, - githubArgs.owner, - githubArgs.hostname, - ghToken, - "flux", - githubArgs.owner+"@users.noreply.github.com", - ) + manifestsBase, err := buildEmbeddedManifestBase() if err != nil { return err } + defer os.RemoveAll(manifestsBase) - if githubArgs.sshHostname != "" { - repository.SSHHost = githubArgs.sshHostname - } - - provider := &git.GithubProvider{ - IsPrivate: githubArgs.private, - IsPersonal: githubArgs.personal, + // Build GitHub provider + providerCfg := provider.Config{ + Provider: provider.GitProviderGitHub, + Hostname: githubArgs.hostname, + Token: ghToken, } - - tmpDir, err := ioutil.TempDir("", rootArgs.namespace) + providerClient, err := provider.BuildGitProvider(providerCfg) if err != nil { return err } - defer os.RemoveAll(tmpDir) - // create GitHub repository if doesn't exists - logger.Actionf("connecting to %s", githubArgs.hostname) - changed, err := provider.CreateRepository(ctx, repository) + // Lazy go-git repository + tmpDir, err := ioutil.TempDir("", "flux-bootstrap-") if err != nil { - return err - } - if changed { - logger.Successf("repository created") - } - - withErrors := false - // add teams to org repository - if !githubArgs.personal { - for _, team := range githubArgs.teams { - if changed, err := provider.AddTeam(ctx, repository, team, ghDefaultPermission); err != nil { - logger.Failuref(err.Error()) - withErrors = true - } else if changed { - logger.Successf("%s team access granted", team) - } - } - } - - // clone repository and checkout the main branch - if err := repository.Checkout(ctx, bootstrapArgs.branch, tmpDir); err != nil { - return err - } - logger.Successf("repository cloned") - - // generate install manifests - logger.Generatef("generating manifests") - installManifest, err := generateInstallManifests( - githubArgs.path.String(), - rootArgs.namespace, - tmpDir, - bootstrapArgs.manifestsPath, - ) - if err != nil { - return err - } - - // stage install manifests - changed, err = repository.Commit( - ctx, - path.Join(githubArgs.path.String(), rootArgs.namespace), - fmt.Sprintf("Add flux %s components manifests", bootstrapArgs.version), - ) - if err != nil { - return err - } - - // push install manifests - if changed { - if err := repository.Push(ctx); err != nil { - return err - } - logger.Successf("components manifests pushed") - } else { - logger.Successf("components are up to date") + return fmt.Errorf("failed to create temporary working dir: %w", err) } - - // determine if repository synchronization is working - isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace) - - if isInstall { - // apply install manifests - logger.Actionf("installing components in %s namespace", rootArgs.namespace) - if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil { - return err - } - logger.Successf("install completed") - } - - repoURL := repository.GetSSH() + defer os.RemoveAll(tmpDir) + gitClient := gogit.New(tmpDir, &http.BasicAuth{ + Username: githubArgs.owner, + Password: ghToken, + }) + + // Install manifest config + installOptions := install.Options{ + BaseURL: rootArgs.defaults.BaseURL, + Version: bootstrapArgs.version, + Namespace: rootArgs.namespace, + Components: bootstrapComponents(), + Registry: bootstrapArgs.registry, + ImagePullSecret: bootstrapArgs.imagePullSecret, + WatchAllNamespaces: bootstrapArgs.watchAllNamespaces, + NetworkPolicy: bootstrapArgs.networkPolicy, + LogLevel: bootstrapArgs.logLevel.String(), + NotificationController: rootArgs.defaults.NotificationController, + ManifestFile: rootArgs.defaults.ManifestFile, + Timeout: rootArgs.timeout, + TargetPath: githubArgs.path.String(), + ClusterDomain: bootstrapArgs.clusterDomain, + TolerationKeys: bootstrapArgs.tolerationKeys, + } + if customBaseURL := bootstrapArgs.manifestsPath; customBaseURL != "" { + installOptions.BaseURL = customBaseURL + } + + // Source generation and secret config secretOpts := sourcesecret.Options{ - Name: rootArgs.namespace, - Namespace: rootArgs.namespace, + Name: bootstrapArgs.secretName, + Namespace: rootArgs.namespace, + TargetPath: githubArgs.path.String(), + ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile, } if bootstrapArgs.tokenAuth { - // Setup HTTPS token auth - repoURL = repository.GetURL() secretOpts.Username = "git" secretOpts.Password = ghToken - } else if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) { - // Setup SSH auth - u, err := url.Parse(repoURL) - if err != nil { - return fmt.Errorf("git URL parse failed: %w", err) - } - secretOpts.SSHHostname = u.Host - secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm - secretOpts.RSAKeyBits = 2048 - } - secret, err := sourcesecret.Generate(secretOpts) - if err != nil { - return err - } - var s corev1.Secret - if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil { - return err - } - if len(s.StringData) > 0 { - logger.Actionf("configuring deploy key") - if err := upsertSecret(ctx, kubeClient, s); err != nil { - return err + if bootstrapArgs.caFile != "" { + secretOpts.CAFilePath = bootstrapArgs.caFile } + } else { + secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm) + secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits) + secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve + secretOpts.SSHHostname = githubArgs.hostname - if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok { - keyName := "flux" - if githubArgs.path != "" { - keyName = fmt.Sprintf("flux-%s", githubArgs.path) - } - - if changed, err := provider.AddDeployKey(ctx, repository, ppk, keyName); err != nil { - return err - } else if changed { - logger.Successf("deploy key configured") - } + if bootstrapArgs.sshHostname != "" { + secretOpts.SSHHostname = bootstrapArgs.sshHostname } } - // configure repository synchronization - logger.Actionf("generating sync manifests") - syncManifests, err := generateSyncManifests( - repoURL, - bootstrapArgs.branch, - rootArgs.namespace, - rootArgs.namespace, - filepath.ToSlash(githubArgs.path.String()), - tmpDir, - githubArgs.interval, - ) - if err != nil { - return err + // Sync manifest config + syncOpts := sync.Options{ + Interval: githubArgs.interval, + Name: rootArgs.namespace, + Namespace: rootArgs.namespace, + Branch: bootstrapArgs.branch, + Secret: bootstrapArgs.secretName, + TargetPath: githubArgs.path.String(), + ManifestFile: sync.MakeDefaultOptions().ManifestFile, + GitImplementation: sourceGitArgs.gitImplementation.String(), + } + + // Bootstrap config + bootstrapOpts := []bootstrap.GitProviderOption{ + bootstrap.WithProviderRepository(githubArgs.owner, githubArgs.repository, githubArgs.personal), + bootstrap.WithBranch(bootstrapArgs.branch), + bootstrap.WithBootstrapTransportType("https"), + bootstrap.WithAuthor(bootstrapArgs.authorName, bootstrapArgs.authorEmail), + bootstrap.WithProviderTeamPermissions(mapTeamSlice(githubArgs.teams, ghDefaultPermission)), + bootstrap.WithReadWriteKeyPermissions(githubArgs.readWriteKey), + bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext), + bootstrap.WithLogger(logger), + } + if bootstrapArgs.sshHostname != "" { + bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname)) } - - // commit and push manifests - if changed, err = repository.Commit( - ctx, - path.Join(githubArgs.path.String(), rootArgs.namespace), - fmt.Sprintf("Add flux %s sync manifests", bootstrapArgs.version), - ); err != nil { - return err - } else if changed { - if err := repository.Push(ctx); err != nil { - return err - } - logger.Successf("sync manifests pushed") + if bootstrapArgs.tokenAuth { + bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https")) } - - // apply manifests and waiting for sync - logger.Actionf("applying sync manifests") - if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil { - return err + if !githubArgs.private { + bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public")) } - if withErrors { - return fmt.Errorf("bootstrap completed with errors") + // Setup bootstrapper with constructed configs + b, err := bootstrap.NewGitProviderBootstrapper(gitClient, providerClient, kubeClient, bootstrapOpts...) + if err != nil { + return err } - logger.Successf("bootstrap finished") - return nil + // Run + return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout) } diff --git a/cmd/flux/bootstrap_gitlab.go b/cmd/flux/bootstrap_gitlab.go index f1f36554..48768c80 100644 --- a/cmd/flux/bootstrap_gitlab.go +++ b/cmd/flux/bootstrap_gitlab.go @@ -20,22 +20,21 @@ import ( "context" "fmt" "io/ioutil" - "net/url" "os" - "path" - "path/filepath" "regexp" "time" + "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/spf13/cobra" - corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/yaml" - - "github.com/fluxcd/pkg/git" + "github.com/fluxcd/flux2/internal/bootstrap" + "github.com/fluxcd/flux2/internal/bootstrap/git/gogit" + "github.com/fluxcd/flux2/internal/bootstrap/provider" "github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/utils" + "github.com/fluxcd/flux2/pkg/manifestgen/install" "github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret" + "github.com/fluxcd/flux2/pkg/manifestgen/sync" ) var bootstrapGitLabCmd = &cobra.Command{ @@ -70,18 +69,22 @@ the bootstrap command will perform an upgrade if needed.`, } const ( - gitlabProjectRegex = `\A[[:alnum:]\x{00A9}-\x{1f9ff}_][[:alnum:]\p{Pd}\x{00A9}-\x{1f9ff}_\.]*\z` + glDefaultPermission = "maintain" + glDefaultDomain = "gitlab.com" + glTokenEnvVar = "GITLAB_TOKEN" + gitlabProjectRegex = `\A[[:alnum:]\x{00A9}-\x{1f9ff}_][[:alnum:]\p{Pd}\x{00A9}-\x{1f9ff}_\.]*\z` ) type gitlabFlags struct { - owner string - repository string - interval time.Duration - personal bool - private bool - hostname string - sshHostname string - path flags.SafeRelativePath + owner string + repository string + interval time.Duration + personal bool + private bool + hostname string + path flags.SafeRelativePath + teams []string + readWriteKey bool } var gitlabArgs gitlabFlags @@ -89,29 +92,29 @@ var gitlabArgs gitlabFlags func init() { bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.owner, "owner", "", "GitLab user or group name") bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.repository, "repository", "", "GitLab repository name") + bootstrapGitLabCmd.Flags().StringArrayVar(&gitlabArgs.teams, "team", []string{}, "GitLab teams to be given maintainer access") bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.personal, "personal", false, "if true, the owner is assumed to be a GitLab user; otherwise a group") bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.private, "private", true, "if true, the repository is assumed to be private") bootstrapGitLabCmd.Flags().DurationVar(&gitlabArgs.interval, "interval", time.Minute, "sync interval") - bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname") - bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.sshHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one") + bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", glDefaultDomain, "GitLab hostname") bootstrapGitLabCmd.Flags().Var(&gitlabArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path") + bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.readWriteKey, "read-write-key", false, "if true, the deploy key is configured with read/write permissions") bootstrapCmd.AddCommand(bootstrapGitLabCmd) } func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error { - glToken := os.Getenv(git.GitLabTokenName) + glToken := os.Getenv(glTokenEnvVar) if glToken == "" { - return fmt.Errorf("%s environment variable not found", git.GitLabTokenName) + return fmt.Errorf("%s environment variable not found", glTokenEnvVar) } - projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, gitlabArgs.repository) - if err != nil { + if projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, gitlabArgs.repository); err != nil || !projectNameIsValid { + if err == nil { + err = fmt.Errorf("%s is an invalid project name for gitlab.\nIt can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.", gitlabArgs.repository) + } return err } - if !projectNameIsValid { - return fmt.Errorf("%s is an invalid project name for gitlab.\nIt can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.", gitlabArgs.repository) - } if err := bootstrapValidate(); err != nil { return err @@ -125,183 +128,127 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error { return err } - usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, rootArgs.namespace, filepath.ToSlash(gitlabArgs.path.String())) - - if bootstrapPathDiffers { - return fmt.Errorf("cluster already bootstrapped to %v path", usedPath) + // Manifest base + if ver, err := getVersion(bootstrapArgs.version); err == nil { + bootstrapArgs.version = ver } - - repository, err := git.NewRepository( - gitlabArgs.repository, - gitlabArgs.owner, - gitlabArgs.hostname, - glToken, - "flux", - gitlabArgs.owner+"@users.noreply.gitlab.com", - ) + manifestsBase, err := buildEmbeddedManifestBase() if err != nil { return err } + defer os.RemoveAll(manifestsBase) - if gitlabArgs.sshHostname != "" { - repository.SSHHost = gitlabArgs.sshHostname + // Build GitLab provider + providerCfg := provider.Config{ + Provider: provider.GitProviderGitLab, + Hostname: gitlabArgs.hostname, + Token: glToken, } - - tmpDir, err := ioutil.TempDir("", rootArgs.namespace) + providerClient, err := provider.BuildGitProvider(providerCfg) if err != nil { return err } - defer os.RemoveAll(tmpDir) - - provider := &git.GitLabProvider{ - IsPrivate: gitlabArgs.private, - IsPersonal: gitlabArgs.personal, - } - // create GitLab project if doesn't exists - logger.Actionf("connecting to %s", gitlabArgs.hostname) - changed, err := provider.CreateRepository(ctx, repository) + // Lazy go-git repository + tmpDir, err := ioutil.TempDir("", "flux-bootstrap-") if err != nil { - return err - } - if changed { - logger.Successf("repository created") + return fmt.Errorf("failed to create temporary working dir: %w", err) } - - // clone repository and checkout the master branch - if err := repository.Checkout(ctx, bootstrapArgs.branch, tmpDir); err != nil { - return err - } - logger.Successf("repository cloned") - - // generate install manifests - logger.Generatef("generating manifests") - installManifest, err := generateInstallManifests( - gitlabArgs.path.String(), - rootArgs.namespace, - tmpDir, - bootstrapArgs.manifestsPath, - ) - if err != nil { - return err - } - - // stage install manifests - changed, err = repository.Commit( - ctx, - path.Join(gitlabArgs.path.String(), rootArgs.namespace), - fmt.Sprintf("Add flux %s components manifests", bootstrapArgs.version), - ) - if err != nil { - return err - } - - // push install manifests - if changed { - if err := repository.Push(ctx); err != nil { - return err - } - logger.Successf("components manifests pushed") - } else { - logger.Successf("components are up to date") - } - - // determine if repository synchronization is working - isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace) - - if isInstall { - // apply install manifests - logger.Actionf("installing components in %s namespace", rootArgs.namespace) - if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil { - return err - } - logger.Successf("install completed") - } - - repoURL := repository.GetSSH() + defer os.RemoveAll(tmpDir) + gitClient := gogit.New(tmpDir, &http.BasicAuth{ + Username: gitlabArgs.owner, + Password: glToken, + }) + + // Install manifest config + installOptions := install.Options{ + BaseURL: rootArgs.defaults.BaseURL, + Version: bootstrapArgs.version, + Namespace: rootArgs.namespace, + Components: bootstrapComponents(), + Registry: bootstrapArgs.registry, + ImagePullSecret: bootstrapArgs.imagePullSecret, + WatchAllNamespaces: bootstrapArgs.watchAllNamespaces, + NetworkPolicy: bootstrapArgs.networkPolicy, + LogLevel: bootstrapArgs.logLevel.String(), + NotificationController: rootArgs.defaults.NotificationController, + ManifestFile: rootArgs.defaults.ManifestFile, + Timeout: rootArgs.timeout, + TargetPath: gitlabArgs.path.String(), + ClusterDomain: bootstrapArgs.clusterDomain, + TolerationKeys: bootstrapArgs.tolerationKeys, + } + if customBaseURL := bootstrapArgs.manifestsPath; customBaseURL != "" { + installOptions.BaseURL = customBaseURL + } + + // Source generation and secret config secretOpts := sourcesecret.Options{ - Name: rootArgs.namespace, - Namespace: rootArgs.namespace, + Name: bootstrapArgs.secretName, + Namespace: rootArgs.namespace, + TargetPath: gitlabArgs.path.String(), + ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile, } if bootstrapArgs.tokenAuth { - // Setup HTTPS token auth - repoURL = repository.GetURL() secretOpts.Username = "git" secretOpts.Password = glToken - } else if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) { - // Setup SSH auth - u, err := url.Parse(repoURL) - if err != nil { - return fmt.Errorf("git URL parse failed: %w", err) - } - secretOpts.SSHHostname = u.Host - secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm - secretOpts.RSAKeyBits = 2048 - } - secret, err := sourcesecret.Generate(secretOpts) - if err != nil { - return err - } - var s corev1.Secret - if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil { - return err - } - if len(s.StringData) > 0 { - logger.Actionf("configuring deploy key") - if err := upsertSecret(ctx, kubeClient, s); err != nil { - return err + if bootstrapArgs.caFile != "" { + secretOpts.CAFilePath = bootstrapArgs.caFile } + } else { + secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm) + secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits) + secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve + secretOpts.SSHHostname = githubArgs.hostname - if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok { - keyName := "flux" - if gitlabArgs.path != "" { - keyName = fmt.Sprintf("flux-%s", gitlabArgs.path) - } - - if changed, err := provider.AddDeployKey(ctx, repository, ppk, keyName); err != nil { - return err - } else if changed { - logger.Successf("deploy key configured") - } + if bootstrapArgs.privateKeyFile != "" { + secretOpts.PrivateKeyPath = bootstrapArgs.privateKeyFile + } + if bootstrapArgs.sshHostname != "" { + secretOpts.SSHHostname = bootstrapArgs.sshHostname } } - // configure repository synchronization - logger.Actionf("generating sync manifests") - syncManifests, err := generateSyncManifests( - repoURL, - bootstrapArgs.branch, - rootArgs.namespace, - rootArgs.namespace, - filepath.ToSlash(gitlabArgs.path.String()), - tmpDir, - gitlabArgs.interval, - ) - if err != nil { - return err + // Sync manifest config + syncOpts := sync.Options{ + Interval: gitlabArgs.interval, + Name: rootArgs.namespace, + Namespace: rootArgs.namespace, + Branch: bootstrapArgs.branch, + Secret: bootstrapArgs.secretName, + TargetPath: gitlabArgs.path.String(), + ManifestFile: sync.MakeDefaultOptions().ManifestFile, + GitImplementation: sourceGitArgs.gitImplementation.String(), + } + + // Bootstrap config + bootstrapOpts := []bootstrap.GitProviderOption{ + bootstrap.WithProviderRepository(gitlabArgs.owner, gitlabArgs.repository, gitlabArgs.personal), + bootstrap.WithBranch(bootstrapArgs.branch), + bootstrap.WithBootstrapTransportType("https"), + bootstrap.WithAuthor(bootstrapArgs.authorName, bootstrapArgs.authorEmail), + bootstrap.WithProviderTeamPermissions(mapTeamSlice(gitlabArgs.teams, glDefaultPermission)), + bootstrap.WithReadWriteKeyPermissions(gitlabArgs.readWriteKey), + bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext), + bootstrap.WithLogger(logger), + } + if bootstrapArgs.sshHostname != "" { + bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname)) } - - // commit and push manifests - if changed, err = repository.Commit( - ctx, - path.Join(gitlabArgs.path.String(), rootArgs.namespace), - fmt.Sprintf("Add flux %s sync manifests", bootstrapArgs.version), - ); err != nil { - return err - } else if changed { - if err := repository.Push(ctx); err != nil { - return err - } - logger.Successf("sync manifests pushed") + if bootstrapArgs.tokenAuth { + bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https")) + } + if !gitlabArgs.private { + bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public")) } - // apply manifests and waiting for sync - logger.Actionf("applying sync manifests") - if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil { + // Setup bootstrapper with constructed configs + b, err := bootstrap.NewGitProviderBootstrapper(gitClient, providerClient, kubeClient, bootstrapOpts...) + if err != nil { return err } - logger.Successf("bootstrap finished") - return nil + // Run + return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout) } diff --git a/docs/cmd/flux_bootstrap.md b/docs/cmd/flux_bootstrap.md index 26dbec70..78760620 100644 --- a/docs/cmd/flux_bootstrap.md +++ b/docs/cmd/flux_bootstrap.md @@ -12,19 +12,28 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git ### Options ``` - --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main") - --cluster-domain string internal cluster domain (default "cluster.local") - --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) - --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values - -h, --help help for bootstrap - --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry - --log-level logLevel log level, available options are: (debug, info, error) (default info) - --network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true) - --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") - --token-auth when enabled, the personal access token will be used instead of SSH deploy key - --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints - -v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases - --watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true) + --author-email string author email for Git commits + --author-name string author name for Git commits (default "Flux") + --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main") + --ca-file string path to TLS CA file used for validating self-signed certificates + --cluster-domain string internal cluster domain (default "cluster.local") + --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) + --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values + -h, --help help for bootstrap + --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry + --log-level logLevel log level, available options are: (debug, info, error) (default info) + --network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true) + --private-key-file string path to a private key file used for authenticating to the Git SSH server + --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") + --secret-name string name of the secret the sync credentials can be found in or stored to (default "flux-system") + --ssh-ecdsa-curve ecdsaCurve SSH ECDSA public key curve (p256, p384, p521) (default p384) + --ssh-hostname string SSH hostname, to be used when the SSH host differs from the HTTPS one + --ssh-key-algorithm publicKeyAlgorithm SSH public key algorithm (rsa, ecdsa, ed25519) (default rsa) + --ssh-rsa-bits rsaKeyBits SSH RSA public key bit size (multiplies of 8) (default 2048) + --token-auth when enabled, the personal access token will be used instead of SSH deploy key + --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints + -v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases + --watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true) ``` ### Options inherited from parent commands diff --git a/docs/cmd/flux_bootstrap_github.md b/docs/cmd/flux_bootstrap_github.md index 80ed707e..cde0605d 100644 --- a/docs/cmd/flux_bootstrap_github.md +++ b/docs/cmd/flux_bootstrap_github.md @@ -55,31 +55,40 @@ flux bootstrap github [flags] --path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path --personal if true, the owner is assumed to be a GitHub user; otherwise an org --private if true, the repository is assumed to be private (default true) + --read-write-key if true, the deploy key is configured with read/write permissions --repository string GitHub repository name - --ssh-hostname string GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one --team stringArray GitHub team to be given maintainer access ``` ### Options inherited from parent commands ``` - --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main") - --cluster-domain string internal cluster domain (default "cluster.local") - --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) - --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values - --context string kubernetes context to use - --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry - --kubeconfig string absolute path to the kubeconfig file - --log-level logLevel log level, available options are: (debug, info, error) (default info) - -n, --namespace string the namespace scope for this operation (default "flux-system") - --network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true) - --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") - --timeout duration timeout for this operation (default 5m0s) - --token-auth when enabled, the personal access token will be used instead of SSH deploy key - --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints - --verbose print generated objects - -v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases - --watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true) + --author-email string author email for Git commits + --author-name string author name for Git commits (default "Flux") + --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main") + --ca-file string path to TLS CA file used for validating self-signed certificates + --cluster-domain string internal cluster domain (default "cluster.local") + --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) + --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values + --context string kubernetes context to use + --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry + --kubeconfig string absolute path to the kubeconfig file + --log-level logLevel log level, available options are: (debug, info, error) (default info) + -n, --namespace string the namespace scope for this operation (default "flux-system") + --network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true) + --private-key-file string path to a private key file used for authenticating to the Git SSH server + --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") + --secret-name string name of the secret the sync credentials can be found in or stored to (default "flux-system") + --ssh-ecdsa-curve ecdsaCurve SSH ECDSA public key curve (p256, p384, p521) (default p384) + --ssh-hostname string SSH hostname, to be used when the SSH host differs from the HTTPS one + --ssh-key-algorithm publicKeyAlgorithm SSH public key algorithm (rsa, ecdsa, ed25519) (default rsa) + --ssh-rsa-bits rsaKeyBits SSH RSA public key bit size (multiplies of 8) (default 2048) + --timeout duration timeout for this operation (default 5m0s) + --token-auth when enabled, the personal access token will be used instead of SSH deploy key + --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints + --verbose print generated objects + -v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases + --watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true) ``` ### SEE ALSO diff --git a/docs/cmd/flux_bootstrap_gitlab.md b/docs/cmd/flux_bootstrap_gitlab.md index 16acf645..3d0b9c43 100644 --- a/docs/cmd/flux_bootstrap_gitlab.md +++ b/docs/cmd/flux_bootstrap_gitlab.md @@ -52,30 +52,40 @@ flux bootstrap gitlab [flags] --path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path --personal if true, the owner is assumed to be a GitLab user; otherwise a group --private if true, the repository is assumed to be private (default true) + --read-write-key if true, the deploy key is configured with read/write permissions --repository string GitLab repository name - --ssh-hostname string GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one + --team stringArray GitLab teams to be given maintainer access ``` ### Options inherited from parent commands ``` - --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main") - --cluster-domain string internal cluster domain (default "cluster.local") - --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) - --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values - --context string kubernetes context to use - --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry - --kubeconfig string absolute path to the kubeconfig file - --log-level logLevel log level, available options are: (debug, info, error) (default info) - -n, --namespace string the namespace scope for this operation (default "flux-system") - --network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true) - --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") - --timeout duration timeout for this operation (default 5m0s) - --token-auth when enabled, the personal access token will be used instead of SSH deploy key - --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints - --verbose print generated objects - -v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases - --watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true) + --author-email string author email for Git commits + --author-name string author name for Git commits (default "Flux") + --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main") + --ca-file string path to TLS CA file used for validating self-signed certificates + --cluster-domain string internal cluster domain (default "cluster.local") + --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) + --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values + --context string kubernetes context to use + --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry + --kubeconfig string absolute path to the kubeconfig file + --log-level logLevel log level, available options are: (debug, info, error) (default info) + -n, --namespace string the namespace scope for this operation (default "flux-system") + --network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true) + --private-key-file string path to a private key file used for authenticating to the Git SSH server + --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") + --secret-name string name of the secret the sync credentials can be found in or stored to (default "flux-system") + --ssh-ecdsa-curve ecdsaCurve SSH ECDSA public key curve (p256, p384, p521) (default p384) + --ssh-hostname string SSH hostname, to be used when the SSH host differs from the HTTPS one + --ssh-key-algorithm publicKeyAlgorithm SSH public key algorithm (rsa, ecdsa, ed25519) (default rsa) + --ssh-rsa-bits rsaKeyBits SSH RSA public key bit size (multiplies of 8) (default 2048) + --timeout duration timeout for this operation (default 5m0s) + --token-auth when enabled, the personal access token will be used instead of SSH deploy key + --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints + --verbose print generated objects + -v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases + --watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true) ``` ### SEE ALSO diff --git a/go.mod b/go.mod index eab2d3b5..ffdbc1a7 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/fluxcd/kustomize-controller/api v0.10.0 github.com/fluxcd/notification-controller/api v0.11.0 github.com/fluxcd/pkg/apis/meta v0.8.0 - github.com/fluxcd/pkg/git v0.3.0 github.com/fluxcd/pkg/runtime v0.10.1 github.com/fluxcd/pkg/ssh v0.0.5 github.com/fluxcd/pkg/untar v0.0.5 @@ -24,6 +23,7 @@ require ( github.com/olekukonko/tablewriter v0.0.4 github.com/spf13/cobra v1.1.1 github.com/spf13/pflag v1.0.5 + github.com/xanzy/go-gitlab v0.43.0 // indirect golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 k8s.io/api v0.20.2 k8s.io/apiextensions-apiserver v0.20.2 diff --git a/go.sum b/go.sum index 60fa4d6a..aa8adafd 100644 --- a/go.sum +++ b/go.sum @@ -204,8 +204,6 @@ github.com/fluxcd/pkg/apis/kustomize v0.0.1 h1:TkA80R0GopRY27VJqzKyS6ifiKIAfwBd7 github.com/fluxcd/pkg/apis/kustomize v0.0.1/go.mod h1:JAFPfnRmcrAoG1gNiA8kmEXsnOBuDyZ/F5X4DAQcVV0= github.com/fluxcd/pkg/apis/meta v0.8.0 h1:wqWpUsxhKHB1ZztcvOz+vnyhdKW9cWmjFp8Vci/XOdk= github.com/fluxcd/pkg/apis/meta v0.8.0/go.mod h1:yHuY8kyGHYz22I0jQzqMMGCcHViuzC/WPdo9Gisk8Po= -github.com/fluxcd/pkg/git v0.3.0 h1:nrKZWZ/ymDevud3Wf1LEieO/QcNPnqz1/MrkZBFcg9o= -github.com/fluxcd/pkg/git v0.3.0/go.mod h1:ZwG0iLOqNSyNw6lsPIAO+v6+BqqCXyV+r1Oq6Lm+slg= github.com/fluxcd/pkg/runtime v0.10.1 h1:NV0pe6lFzodKBIz0dT3xkoR0wJnTCicXwM/v/d5T0+Y= github.com/fluxcd/pkg/runtime v0.10.1/go.mod h1:JD0eZIn5xkTeHHQUWXSqJPIh/ecO0d0qrUKbSVHnpnw= github.com/fluxcd/pkg/ssh v0.0.5 h1:rnbFZ7voy2JBlUfMbfyqArX2FYaLNpDhccGFC3qW83A= @@ -389,8 +387,6 @@ github.com/google/go-containerregistry v0.2.0 h1:cWFYx+kOkKdyOET0pcp7GMCmxj7da40 github.com/google/go-containerregistry v0.2.0/go.mod h1:Ts3Wioz1r5ayWx8sS6vLcWltWcM1aqFjd/eVrkFhrWM= github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= -github.com/google/go-github/v33 v33.0.0 h1:qAf9yP0qc54ufQxzwv+u9H0tiVOnPJxo0lI/JXqw3ZM= -github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=