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{
@ -46,21 +35,36 @@ 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
registry string requiredComponents []string
imagePullSecret string
branch 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 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
}
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,
} }
tmpBaseDir, err := ioutil.TempDir("", "flux-manifests-")
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
}
if _, err = kustomization.WriteFile(tmpDir); err != nil {
return "", err return "", err
} }
return tmpBaseDir, nil
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{
@ -71,19 +71,21 @@ the bootstrap command will perform an upgrade if needed.`,
} }
type githubFlags struct { type githubFlags struct {
owner string owner string
repository string repository string
interval time.Duration interval time.Duration
personal bool personal bool
private bool private bool
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,
provider := &git.GithubProvider{ Token: ghToken,
IsPrivate: githubArgs.private,
IsPersonal: githubArgs.personal,
} }
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)
// 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")
} }
defer os.RemoveAll(tmpDir)
// determine if repository synchronization is working gitClient := gogit.New(tmpDir, &http.BasicAuth{
isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace) Username: githubArgs.owner,
Password: ghToken,
if isInstall { })
// apply install manifests
logger.Actionf("installing components in %s namespace", rootArgs.namespace) // Install manifest config
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil { installOptions := install.Options{
return err BaseURL: rootArgs.defaults.BaseURL,
} Version: bootstrapArgs.version,
logger.Successf("install completed") Namespace: rootArgs.namespace,
} Components: bootstrapComponents(),
Registry: bootstrapArgs.registry,
repoURL := repository.GetSSH() 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
} }
} 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 { if bootstrapArgs.sshHostname != "" {
keyName := "flux" secretOpts.SSHHostname = bootstrapArgs.sshHostname
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")
}
} }
} }
// 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 {
// apply manifests and waiting for sync bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
logger.Actionf("applying sync manifests")
if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil {
return err
} }
if withErrors { // Setup bootstrapper with constructed configs
return fmt.Errorf("bootstrap completed with errors") b, err := bootstrap.NewGitProviderBootstrapper(gitClient, providerClient, kubeClient, bootstrapOpts...)
if err != nil {
return err
} }
logger.Successf("bootstrap finished") // Run
return nil return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
} }

@ -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,18 +69,22 @@ the bootstrap command will perform an upgrade if needed.`,
} }
const ( 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 { type gitlabFlags struct {
owner string owner string
repository string repository string
interval time.Duration interval time.Duration
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,29 +92,29 @@ 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 {
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 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 { if err := bootstrapValidate(); err != nil {
return err return err
@ -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")
} }
defer os.RemoveAll(tmpDir)
// clone repository and checkout the master branch gitClient := gogit.New(tmpDir, &http.BasicAuth{
if err := repository.Checkout(ctx, bootstrapArgs.branch, tmpDir); err != nil { Username: gitlabArgs.owner,
return err Password: glToken,
} })
logger.Successf("repository cloned")
// Install manifest config
// generate install manifests installOptions := install.Options{
logger.Generatef("generating manifests") BaseURL: rootArgs.defaults.BaseURL,
installManifest, err := generateInstallManifests( Version: bootstrapArgs.version,
gitlabArgs.path.String(), Namespace: rootArgs.namespace,
rootArgs.namespace, Components: bootstrapComponents(),
tmpDir, Registry: bootstrapArgs.registry,
bootstrapArgs.manifestsPath, ImagePullSecret: bootstrapArgs.imagePullSecret,
) WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
if err != nil { NetworkPolicy: bootstrapArgs.networkPolicy,
return err LogLevel: bootstrapArgs.logLevel.String(),
} NotificationController: rootArgs.defaults.NotificationController,
ManifestFile: rootArgs.defaults.ManifestFile,
// stage install manifests Timeout: rootArgs.timeout,
changed, err = repository.Commit( TargetPath: gitlabArgs.path.String(),
ctx, ClusterDomain: bootstrapArgs.clusterDomain,
path.Join(gitlabArgs.path.String(), rootArgs.namespace), TolerationKeys: bootstrapArgs.tolerationKeys,
fmt.Sprintf("Add flux %s components manifests", bootstrapArgs.version), }
) if customBaseURL := bootstrapArgs.manifestsPath; customBaseURL != "" {
if err != nil { installOptions.BaseURL = customBaseURL
return err }
}
// Source generation and secret config
// 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()
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
} }
} 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 { if bootstrapArgs.privateKeyFile != "" {
keyName := "flux" secretOpts.PrivateKeyPath = bootstrapArgs.privateKeyFile
if gitlabArgs.path != "" { }
keyName = fmt.Sprintf("flux-%s", gitlabArgs.path) if bootstrapArgs.sshHostname != "" {
} secretOpts.SSHHostname = bootstrapArgs.sshHostname
if changed, err := provider.AddDeployKey(ctx, repository, ppk, keyName); err != nil {
return err
} 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: 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, if !gitlabArgs.private {
path.Join(gitlabArgs.path.String(), rootArgs.namespace), bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
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")
} }
// 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,19 +12,28 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
### Options ### Options
``` ```
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main") --author-email string author email for Git commits
--cluster-domain string internal cluster domain (default "cluster.local") --author-name string author name for Git commits (default "Flux")
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main")
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values --ca-file string path to TLS CA file used for validating self-signed certificates
-h, --help help for bootstrap --cluster-domain string internal cluster domain (default "cluster.local")
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
--log-level logLevel log level, available options are: (debug, info, error) (default info) --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true) -h, --help help for bootstrap
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--token-auth when enabled, the personal access token will be used instead of SSH deploy key --log-level logLevel log level, available options are: (debug, info, error) (default info)
--toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints --network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
-v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases --private-key-file string path to a private key file used for authenticating to the Git SSH server
--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) --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 ### Options inherited from parent commands

@ -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 --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
``` ```
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main") --author-email string author email for Git commits
--cluster-domain string internal cluster domain (default "cluster.local") --author-name string author name for Git commits (default "Flux")
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main")
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values --ca-file string path to TLS CA file used for validating self-signed certificates
--context string kubernetes context to use --cluster-domain string internal cluster domain (default "cluster.local")
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
--kubeconfig string absolute path to the kubeconfig file --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
--log-level logLevel log level, available options are: (debug, info, error) (default info) --context string kubernetes context to use
-n, --namespace string the namespace scope for this operation (default "flux-system") --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true) --kubeconfig string absolute path to the kubeconfig file
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --log-level logLevel log level, available options are: (debug, info, error) (default info)
--timeout duration timeout for this operation (default 5m0s) -n, --namespace string the namespace scope for this operation (default "flux-system")
--token-auth when enabled, the personal access token will be used instead of SSH deploy key --network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
--toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints --private-key-file string path to a private key file used for authenticating to the Git SSH server
--verbose print generated objects --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
-v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases --secret-name string name of the secret the sync credentials can be found in or stored to (default "flux-system")
--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) --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 ### SEE ALSO

@ -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 --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
``` ```
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main") --author-email string author email for Git commits
--cluster-domain string internal cluster domain (default "cluster.local") --author-name string author name for Git commits (default "Flux")
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main")
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values --ca-file string path to TLS CA file used for validating self-signed certificates
--context string kubernetes context to use --cluster-domain string internal cluster domain (default "cluster.local")
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
--kubeconfig string absolute path to the kubeconfig file --components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
--log-level logLevel log level, available options are: (debug, info, error) (default info) --context string kubernetes context to use
-n, --namespace string the namespace scope for this operation (default "flux-system") --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true) --kubeconfig string absolute path to the kubeconfig file
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --log-level logLevel log level, available options are: (debug, info, error) (default info)
--timeout duration timeout for this operation (default 5m0s) -n, --namespace string the namespace scope for this operation (default "flux-system")
--token-auth when enabled, the personal access token will be used instead of SSH deploy key --network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
--toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints --private-key-file string path to a private key file used for authenticating to the Git SSH server
--verbose print generated objects --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
-v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases --secret-name string name of the secret the sync credentials can be found in or stored to (default "flux-system")
--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) --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 ### SEE ALSO

@ -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