Implement `bootstrap` package in commands

This includes making a lot of things configurable (e.g. SSH key
algorithm, RSA bit size, etc.) that used to be static.

Signed-off-by: Hidde Beydals <hello@hidde.co>
pull/968/head
Hidde Beydals 4 years ago
parent 1d3a381389
commit f57ce14754

@ -17,26 +17,15 @@ limitations under the License.
package main package main
import ( import (
"context" "crypto/elliptic"
"fmt" "fmt"
"path/filepath" "io/ioutil"
"time"
"github.com/spf13/cobra" "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/flags"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install" "github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
kus "github.com/fluxcd/flux2/pkg/manifestgen/kustomization"
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
"github.com/fluxcd/flux2/pkg/status"
) )
var bootstrapCmd = &cobra.Command{ var bootstrapCmd = &cobra.Command{
@ -47,20 +36,35 @@ var bootstrapCmd = &cobra.Command{
type bootstrapFlags struct { type bootstrapFlags struct {
version string version string
arch flags.Arch
logLevel flags.LogLevel
branch string
manifestsPath string
defaultComponents []string defaultComponents []string
extraComponents []string extraComponents []string
requiredComponents []string
registry string registry string
imagePullSecret string imagePullSecret string
branch string
secretName string
tokenAuth bool
keyAlgorithm flags.PublicKeyAlgorithm
keyRSABits flags.RSAKeyBits
keyECDSACurve flags.ECDSACurve
sshHostname string
caFile string
privateKeyFile string
watchAllNamespaces bool watchAllNamespaces bool
networkPolicy bool networkPolicy bool
manifestsPath string
arch flags.Arch
logLevel flags.LogLevel
requiredComponents []string
tokenAuth bool
clusterDomain string clusterDomain string
tolerationKeys []string tolerationKeys []string
authorName string
authorEmail string
} }
const ( const (
@ -72,17 +76,21 @@ var bootstrapArgs = NewBootstrapFlags()
func init() { func init() {
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", "", bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", "",
"toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases") "toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components, bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components,
"list of components, accepts comma-separated values") "list of components, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil, bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil,
"list of components in addition to those supplied or defaulted, accepts comma-separated values") "list of components in addition to those supplied or defaulted, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd", bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd",
"container registry where the toolkit images are published") "container registry where the toolkit images are published")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.imagePullSecret, "image-pull-secret", "", bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.imagePullSecret, "image-pull-secret", "",
"Kubernetes secret name used for pulling the toolkit images from a private registry") "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, bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.branch, "branch", bootstrapDefaultBranch,
"default branch (for GitHub this must match the default branch setting for the organization)") "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, 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") "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, bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.networkPolicy, "network-policy", true,
@ -90,12 +98,25 @@ func init() {
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.tokenAuth, "token-auth", false, bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.tokenAuth, "token-auth", false,
"when enabled, the personal access token will be used instead of SSH deploy key") "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().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().StringVar(&bootstrapArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.tolerationKeys, "toleration-keys", nil, bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.tolerationKeys, "toleration-keys", nil,
"list of toleration keys used to schedule the components pods onto nodes with matching taints") "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().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
rootCmd.AddCommand(bootstrapCmd) rootCmd.AddCommand(bootstrapCmd)
} }
@ -103,6 +124,9 @@ func NewBootstrapFlags() bootstrapFlags {
return bootstrapFlags{ return bootstrapFlags{
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel), logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
requiredComponents: []string{"source-controller", "kustomize-controller"}, 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...) return append(bootstrapArgs.defaultComponents, bootstrapArgs.extraComponents...)
} }
func bootstrapValidate() error { func buildEmbeddedManifestBase() (string, error) {
components := bootstrapComponents() if !isEmbeddedVersion(bootstrapArgs.version) {
for _, component := range bootstrapArgs.requiredComponents { return "", nil
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
} }
tmpBaseDir, err := ioutil.TempDir("", "flux-manifests-")
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,
}
manifest, err := sync.Generate(opts)
if err != nil {
return "", fmt.Errorf("generating install manifests failed: %w", err)
}
output, err := manifest.WriteFile(tmpDir)
if err != nil { if err != nil {
return "", err return "", err
} }
outputDir := filepath.Dir(output) if err := writeEmbeddedManifests(tmpBaseDir); err != nil {
kusOpts := kus.MakeDefaultOptions()
kusOpts.BaseDir = tmpDir
kusOpts.TargetPath = filepath.Dir(manifest.Path)
kustomization, err := kus.Generate(kusOpts)
if err != nil {
return "", err return "", err
} }
if _, err = kustomization.WriteFile(tmpDir); err != nil { return tmpBaseDir, nil
return "", err
}
return outputDir, nil
} }
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, manifestsPath string) error { func bootstrapValidate() error {
kubectlArgs := []string{"apply", "-k", manifestsPath} components := bootstrapComponents()
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil { for _, component := range bootstrapArgs.requiredComponents {
return err if !utils.ContainsItemString(components, component) {
return fmt.Errorf("component %s is required", component)
} }
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
} }
var kustomization kustomizev1.Kustomization if err := utils.ValidateComponents(components); err != nil {
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isKustomizationReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &kustomization)); err != nil {
return err return err
} }
return nil return nil
} }
func shouldInstallManifests(ctx context.Context, kubeClient client.Client, namespace string) bool { func mapTeamSlice(s []string, defaultPermission string) map[string]string {
namespacedName := types.NamespacedName{ m := make(map[string]string, len(s))
Namespace: namespace, for _, v := range s {
Name: namespace, m[v] = defaultPermission
}
var kustomization kustomizev1.Kustomization
if err := kubeClient.Get(ctx, namespacedName, &kustomization); err != nil {
return true
} }
return m
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
} }

@ -20,20 +20,20 @@ import (
"context" "context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/url"
"os" "os"
"path"
"path/filepath"
"time" "time"
"github.com/fluxcd/pkg/git" "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/spf13/cobra" "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/flags"
"github.com/fluxcd/flux2/internal/utils" "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/sourcesecret"
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
) )
var bootstrapGitHubCmd = &cobra.Command{ var bootstrapGitHubCmd = &cobra.Command{
@ -79,11 +79,13 @@ type githubFlags struct {
hostname string hostname string
path flags.SafeRelativePath path flags.SafeRelativePath
teams []string teams []string
sshHostname string readWriteKey bool
} }
const ( const (
ghDefaultPermission = "maintain" ghDefaultPermission = "maintain"
ghDefaultDomain = "github.com"
ghTokenEnvVar = "GITHUB_TOKEN"
) )
var githubArgs githubFlags 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.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().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().DurationVar(&githubArgs.interval, "interval", time.Minute, "sync interval")
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname") bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", ghDefaultDomain, "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().Var(&githubArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path") 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) bootstrapCmd.AddCommand(bootstrapGitHubCmd)
} }
func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error { func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
ghToken := os.Getenv(git.GitHubTokenName) ghToken := os.Getenv(ghTokenEnvVar)
if ghToken == "" { 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 { if err := bootstrapValidate(); err != nil {
@ -120,205 +122,124 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers( // Manifest base
ctx, if ver, err := getVersion(bootstrapArgs.version); err == nil {
kubeClient, bootstrapArgs.version = ver
rootArgs.namespace,
filepath.ToSlash(githubArgs.path.String()),
)
if bootstrapPathDiffers {
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
} }
manifestsBase, err := buildEmbeddedManifestBase()
repository, err := git.NewRepository(
githubArgs.repository,
githubArgs.owner,
githubArgs.hostname,
ghToken,
"flux",
githubArgs.owner+"@users.noreply.github.com",
)
if err != nil { if err != nil {
return err return err
} }
defer os.RemoveAll(manifestsBase)
if githubArgs.sshHostname != "" { // Build GitHub provider
repository.SSHHost = githubArgs.sshHostname providerCfg := provider.Config{
Provider: provider.GitProviderGitHub,
Hostname: githubArgs.hostname,
Token: ghToken,
} }
providerClient, err := provider.BuildGitProvider(providerCfg)
provider := &git.GithubProvider{
IsPrivate: githubArgs.private,
IsPersonal: githubArgs.personal,
}
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
if err != nil { if err != nil {
return err return err
} }
defer os.RemoveAll(tmpDir)
// create GitHub repository if doesn't exists // Lazy go-git repository
logger.Actionf("connecting to %s", githubArgs.hostname) tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
changed, err := provider.CreateRepository(ctx, repository)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to create temporary working dir: %w", 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")
}
// 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")
} }
defer os.RemoveAll(tmpDir)
repoURL := repository.GetSSH() 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{ secretOpts := sourcesecret.Options{
Name: rootArgs.namespace, Name: bootstrapArgs.secretName,
Namespace: rootArgs.namespace, Namespace: rootArgs.namespace,
TargetPath: githubArgs.path.String(),
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
if bootstrapArgs.tokenAuth { if bootstrapArgs.tokenAuth {
// Setup HTTPS token auth
repoURL = repository.GetURL()
secretOpts.Username = "git" secretOpts.Username = "git"
secretOpts.Password = ghToken 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 bootstrapArgs.caFile != "" {
if err != nil { secretOpts.CAFilePath = bootstrapArgs.caFile
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 ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
keyName := "flux"
if githubArgs.path != "" {
keyName = fmt.Sprintf("flux-%s", githubArgs.path)
} }
} else {
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
secretOpts.SSHHostname = githubArgs.hostname
if changed, err := provider.AddDeployKey(ctx, repository, ppk, keyName); err != nil { if bootstrapArgs.sshHostname != "" {
return err secretOpts.SSHHostname = bootstrapArgs.sshHostname
} else if changed {
logger.Successf("deploy key configured")
}
} }
} }
// configure repository synchronization // Sync manifest config
logger.Actionf("generating sync manifests") syncOpts := sync.Options{
syncManifests, err := generateSyncManifests( Interval: githubArgs.interval,
repoURL, Name: rootArgs.namespace,
bootstrapArgs.branch, Namespace: rootArgs.namespace,
rootArgs.namespace, Branch: bootstrapArgs.branch,
rootArgs.namespace, Secret: bootstrapArgs.secretName,
filepath.ToSlash(githubArgs.path.String()), TargetPath: githubArgs.path.String(),
tmpDir, ManifestFile: sync.MakeDefaultOptions().ManifestFile,
githubArgs.interval, GitImplementation: sourceGitArgs.gitImplementation.String(),
) }
if err != nil {
return err // 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))
} }
if bootstrapArgs.tokenAuth {
// commit and push manifests bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
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 !githubArgs.private {
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
} }
// apply manifests and waiting for sync // Setup bootstrapper with constructed configs
logger.Actionf("applying sync manifests") b, err := bootstrap.NewGitProviderBootstrapper(gitClient, providerClient, kubeClient, bootstrapOpts...)
if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil { if err != nil {
return err return err
} }
if withErrors { // Run
return fmt.Errorf("bootstrap completed with errors") return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
}
logger.Successf("bootstrap finished")
return nil
} }

@ -20,22 +20,21 @@ import (
"context" "context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/url"
"os" "os"
"path"
"path/filepath"
"regexp" "regexp"
"time" "time"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/spf13/cobra" "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/flags"
"github.com/fluxcd/flux2/internal/utils" "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/sourcesecret"
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
) )
var bootstrapGitLabCmd = &cobra.Command{ var bootstrapGitLabCmd = &cobra.Command{
@ -70,6 +69,9 @@ the bootstrap command will perform an upgrade if needed.`,
} }
const ( const (
glDefaultPermission = "maintain"
glDefaultDomain = "gitlab.com"
glTokenEnvVar = "GITLAB_TOKEN"
gitlabProjectRegex = `\A[[:alnum:]\x{00A9}-\x{1f9ff}_][[:alnum:]\p{Pd}\x{00A9}-\x{1f9ff}_\.]*\z` gitlabProjectRegex = `\A[[:alnum:]\x{00A9}-\x{1f9ff}_][[:alnum:]\p{Pd}\x{00A9}-\x{1f9ff}_\.]*\z`
) )
@ -80,8 +82,9 @@ type gitlabFlags struct {
personal bool personal bool
private bool private bool
hostname string hostname string
sshHostname string
path flags.SafeRelativePath path flags.SafeRelativePath
teams []string
readWriteKey bool
} }
var gitlabArgs gitlabFlags var gitlabArgs gitlabFlags
@ -89,28 +92,28 @@ var gitlabArgs gitlabFlags
func init() { func init() {
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.owner, "owner", "", "GitLab user or group name") bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.owner, "owner", "", "GitLab user or group name")
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.repository, "repository", "", "GitLab repository 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.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().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().DurationVar(&gitlabArgs.interval, "interval", time.Minute, "sync interval")
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname") bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", glDefaultDomain, "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().Var(&gitlabArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path") 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) bootstrapCmd.AddCommand(bootstrapGitLabCmd)
} }
func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error { func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
glToken := os.Getenv(git.GitLabTokenName) glToken := os.Getenv(glTokenEnvVar)
if glToken == "" { 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 projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, gitlabArgs.repository); err != nil || !projectNameIsValid {
if err != nil { if err == nil {
return err 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)
} }
if !projectNameIsValid { return err
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 { if err := bootstrapValidate(); err != nil {
@ -125,183 +128,127 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, rootArgs.namespace, filepath.ToSlash(gitlabArgs.path.String())) // Manifest base
if ver, err := getVersion(bootstrapArgs.version); err == nil {
if bootstrapPathDiffers { bootstrapArgs.version = ver
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
} }
manifestsBase, err := buildEmbeddedManifestBase()
repository, err := git.NewRepository(
gitlabArgs.repository,
gitlabArgs.owner,
gitlabArgs.hostname,
glToken,
"flux",
gitlabArgs.owner+"@users.noreply.gitlab.com",
)
if err != nil { if err != nil {
return err return err
} }
defer os.RemoveAll(manifestsBase)
if gitlabArgs.sshHostname != "" { // Build GitLab provider
repository.SSHHost = gitlabArgs.sshHostname providerCfg := provider.Config{
Provider: provider.GitProviderGitLab,
Hostname: gitlabArgs.hostname,
Token: glToken,
} }
providerClient, err := provider.BuildGitProvider(providerCfg)
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
if err != nil { if err != nil {
return err return err
} }
defer os.RemoveAll(tmpDir)
provider := &git.GitLabProvider{
IsPrivate: gitlabArgs.private,
IsPersonal: gitlabArgs.personal,
}
// create GitLab project if doesn't exists // Lazy go-git repository
logger.Actionf("connecting to %s", gitlabArgs.hostname) tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
changed, err := provider.CreateRepository(ctx, repository)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to create temporary working dir: %w", err)
}
if changed {
logger.Successf("repository created")
}
// 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")
} }
defer os.RemoveAll(tmpDir)
repoURL := repository.GetSSH() 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{ secretOpts := sourcesecret.Options{
Name: rootArgs.namespace, Name: bootstrapArgs.secretName,
Namespace: rootArgs.namespace, Namespace: rootArgs.namespace,
TargetPath: gitlabArgs.path.String(),
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
if bootstrapArgs.tokenAuth { if bootstrapArgs.tokenAuth {
// Setup HTTPS token auth
repoURL = repository.GetURL()
secretOpts.Username = "git" secretOpts.Username = "git"
secretOpts.Password = glToken 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 bootstrapArgs.caFile != "" {
if err != nil { secretOpts.CAFilePath = bootstrapArgs.caFile
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 ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
keyName := "flux"
if gitlabArgs.path != "" {
keyName = fmt.Sprintf("flux-%s", gitlabArgs.path)
} }
} else {
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
secretOpts.SSHHostname = githubArgs.hostname
if changed, err := provider.AddDeployKey(ctx, repository, ppk, keyName); err != nil { if bootstrapArgs.privateKeyFile != "" {
return err secretOpts.PrivateKeyPath = bootstrapArgs.privateKeyFile
} else if changed {
logger.Successf("deploy key configured")
} }
if bootstrapArgs.sshHostname != "" {
secretOpts.SSHHostname = bootstrapArgs.sshHostname
} }
} }
// configure repository synchronization // Sync manifest config
logger.Actionf("generating sync manifests") syncOpts := sync.Options{
syncManifests, err := generateSyncManifests( Interval: gitlabArgs.interval,
repoURL, Name: rootArgs.namespace,
bootstrapArgs.branch, Namespace: rootArgs.namespace,
rootArgs.namespace, Branch: bootstrapArgs.branch,
rootArgs.namespace, Secret: bootstrapArgs.secretName,
filepath.ToSlash(gitlabArgs.path.String()), TargetPath: gitlabArgs.path.String(),
tmpDir, ManifestFile: sync.MakeDefaultOptions().ManifestFile,
gitlabArgs.interval, GitImplementation: sourceGitArgs.gitImplementation.String(),
) }
if err != nil {
return err // 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))
} }
if bootstrapArgs.tokenAuth {
// commit and push manifests bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
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 !gitlabArgs.private {
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
} }
// apply manifests and waiting for sync // Setup bootstrapper with constructed configs
logger.Actionf("applying sync manifests") b, err := bootstrap.NewGitProviderBootstrapper(gitClient, providerClient, kubeClient, bootstrapOpts...)
if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil { if err != nil {
return err return err
} }
logger.Successf("bootstrap finished") // Run
return nil return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
} }

@ -12,7 +12,10 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
### Options ### Options
``` ```
--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") --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") --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 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 --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
@ -20,7 +23,13 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry --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) --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) --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") --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 --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 --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 -v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases

@ -55,15 +55,18 @@ flux bootstrap github [flags]
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path --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 --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) --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 --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 --team stringArray GitHub team to be given maintainer access
``` ```
### Options inherited from parent commands ### Options inherited from parent commands
``` ```
--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") --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") --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 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 --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
@ -73,7 +76,13 @@ flux bootstrap github [flags]
--log-level logLevel log level, available options are: (debug, info, error) (default info) --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") -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) --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") --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) --timeout duration timeout for this operation (default 5m0s)
--token-auth when enabled, the personal access token will be used instead of SSH deploy key --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 --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints

@ -52,14 +52,18 @@ flux bootstrap gitlab [flags]
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path --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 --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) --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 --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 ### Options inherited from parent commands
``` ```
--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") --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") --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 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 --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
@ -69,7 +73,13 @@ flux bootstrap gitlab [flags]
--log-level logLevel log level, available options are: (debug, info, error) (default info) --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") -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) --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") --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) --timeout duration timeout for this operation (default 5m0s)
--token-auth when enabled, the personal access token will be used instead of SSH deploy key --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 --toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints

@ -12,7 +12,6 @@ require (
github.com/fluxcd/kustomize-controller/api v0.10.0 github.com/fluxcd/kustomize-controller/api v0.10.0
github.com/fluxcd/notification-controller/api v0.11.0 github.com/fluxcd/notification-controller/api v0.11.0
github.com/fluxcd/pkg/apis/meta v0.8.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/runtime v0.10.1
github.com/fluxcd/pkg/ssh v0.0.5 github.com/fluxcd/pkg/ssh v0.0.5
github.com/fluxcd/pkg/untar 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/olekukonko/tablewriter v0.0.4
github.com/spf13/cobra v1.1.1 github.com/spf13/cobra v1.1.1
github.com/spf13/pflag v1.0.5 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 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
k8s.io/api v0.20.2 k8s.io/api v0.20.2
k8s.io/apiextensions-apiserver v0.20.2 k8s.io/apiextensions-apiserver v0.20.2

@ -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/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 h1:wqWpUsxhKHB1ZztcvOz+vnyhdKW9cWmjFp8Vci/XOdk=
github.com/fluxcd/pkg/apis/meta v0.8.0/go.mod h1:yHuY8kyGHYz22I0jQzqMMGCcHViuzC/WPdo9Gisk8Po= 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 h1:NV0pe6lFzodKBIz0dT3xkoR0wJnTCicXwM/v/d5T0+Y=
github.com/fluxcd/pkg/runtime v0.10.1/go.mod h1:JD0eZIn5xkTeHHQUWXSqJPIh/ecO0d0qrUKbSVHnpnw= github.com/fluxcd/pkg/runtime v0.10.1/go.mod h1:JD0eZIn5xkTeHHQUWXSqJPIh/ecO0d0qrUKbSVHnpnw=
github.com/fluxcd/pkg/ssh v0.0.5 h1:rnbFZ7voy2JBlUfMbfyqArX2FYaLNpDhccGFC3qW83A= 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-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 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= 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 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 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= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=

Loading…
Cancel
Save