mirror of
https://github.com/fluxcd/flux2.git
synced 2025-10-29 15:28:04 +08:00
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>
This commit is contained in:
@ -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,6 +134,20 @@ func bootstrapComponents() []string {
|
|||||||
return append(bootstrapArgs.defaultComponents, bootstrapArgs.extraComponents...)
|
return append(bootstrapArgs.defaultComponents, bootstrapArgs.extraComponents...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildEmbeddedManifestBase() (string, error) {
|
||||||
|
if !isEmbeddedVersion(bootstrapArgs.version) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
tmpBaseDir, err := ioutil.TempDir("", "flux-manifests-")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if err := writeEmbeddedManifests(tmpBaseDir); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return tmpBaseDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
func bootstrapValidate() error {
|
func bootstrapValidate() error {
|
||||||
components := bootstrapComponents()
|
components := bootstrapComponents()
|
||||||
for _, component := range bootstrapArgs.requiredComponents {
|
for _, component := range bootstrapArgs.requiredComponents {
|
||||||
@ -125,179 +163,10 @@ func bootstrapValidate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
|
func mapTeamSlice(s []string, defaultPermission string) map[string]string {
|
||||||
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
m := make(map[string]string, len(s))
|
||||||
return "", err
|
for _, v := range s {
|
||||||
} else {
|
m[v] = defaultPermission
|
||||||
bootstrapArgs.version = ver
|
|
||||||
}
|
}
|
||||||
|
return m
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
outputDir := filepath.Dir(output)
|
|
||||||
|
|
||||||
kusOpts := kus.MakeDefaultOptions()
|
|
||||||
kusOpts.BaseDir = tmpDir
|
|
||||||
kusOpts.TargetPath = filepath.Dir(manifest.Path)
|
|
||||||
|
|
||||||
kustomization, err := kus.Generate(kusOpts)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if _, err = kustomization.WriteFile(tmpDir); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return outputDir, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, manifestsPath string) error {
|
|
||||||
kubectlArgs := []string{"apply", "-k", manifestsPath}
|
|
||||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for cluster sync")
|
|
||||||
|
|
||||||
var gitRepository sourcev1.GitRepository
|
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
|
||||||
isGitRepositoryReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &gitRepository)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var kustomization kustomizev1.Kustomization
|
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
|
||||||
isKustomizationReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &kustomization)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func shouldInstallManifests(ctx context.Context, kubeClient client.Client, namespace string) bool {
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: namespace,
|
|
||||||
}
|
|
||||||
var kustomization kustomizev1.Kustomization
|
|
||||||
if err := kubeClient.Get(ctx, namespacedName, &kustomization); err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(manifestsBase)
|
||||||
|
|
||||||
repository, err := git.NewRepository(
|
// Build GitHub provider
|
||||||
githubArgs.repository,
|
providerCfg := provider.Config{
|
||||||
githubArgs.owner,
|
Provider: provider.GitProviderGitHub,
|
||||||
githubArgs.hostname,
|
Hostname: githubArgs.hostname,
|
||||||
ghToken,
|
Token: ghToken,
|
||||||
"flux",
|
}
|
||||||
githubArgs.owner+"@users.noreply.github.com",
|
providerClient, err := provider.BuildGitProvider(providerCfg)
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if githubArgs.sshHostname != "" {
|
// Lazy go-git repository
|
||||||
repository.SSHHost = githubArgs.sshHostname
|
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
|
||||||
}
|
|
||||||
|
|
||||||
provider := &git.GithubProvider{
|
|
||||||
IsPrivate: githubArgs.private,
|
|
||||||
IsPersonal: githubArgs.personal,
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
gitClient := gogit.New(tmpDir, &http.BasicAuth{
|
||||||
|
Username: githubArgs.owner,
|
||||||
|
Password: ghToken,
|
||||||
|
})
|
||||||
|
|
||||||
// create GitHub repository if doesn't exists
|
// Install manifest config
|
||||||
logger.Actionf("connecting to %s", githubArgs.hostname)
|
installOptions := install.Options{
|
||||||
changed, err := provider.CreateRepository(ctx, repository)
|
BaseURL: rootArgs.defaults.BaseURL,
|
||||||
if err != nil {
|
Version: bootstrapArgs.version,
|
||||||
return err
|
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 changed {
|
if customBaseURL := bootstrapArgs.manifestsPath; customBaseURL != "" {
|
||||||
logger.Successf("repository created")
|
installOptions.BaseURL = customBaseURL
|
||||||
}
|
}
|
||||||
|
|
||||||
withErrors := false
|
// Source generation and secret config
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
repoURL := repository.GetSSH()
|
|
||||||
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
|
if bootstrapArgs.caFile != "" {
|
||||||
u, err := url.Parse(repoURL)
|
secretOpts.CAFilePath = bootstrapArgs.caFile
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
|
||||||
}
|
}
|
||||||
secretOpts.SSHHostname = u.Host
|
} else {
|
||||||
secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm
|
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
|
||||||
secretOpts.RSAKeyBits = 2048
|
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
|
||||||
}
|
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
|
||||||
|
secretOpts.SSHHostname = githubArgs.hostname
|
||||||
|
|
||||||
secret, err := sourcesecret.Generate(secretOpts)
|
if bootstrapArgs.sshHostname != "" {
|
||||||
if err != nil {
|
secretOpts.SSHHostname = bootstrapArgs.sshHostname
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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(),
|
||||||
)
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
|
||||||
|
}
|
||||||
|
if !githubArgs.private {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup bootstrapper with constructed configs
|
||||||
|
b, err := bootstrap.NewGitProviderBootstrapper(gitClient, providerClient, kubeClient, bootstrapOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit and push manifests
|
// Run
|
||||||
if changed, err = repository.Commit(
|
return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply manifests and waiting for sync
|
|
||||||
logger.Actionf("applying sync manifests")
|
|
||||||
if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if withErrors {
|
|
||||||
return fmt.Errorf("bootstrap completed with errors")
|
|
||||||
}
|
|
||||||
|
|
||||||
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,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()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(manifestsBase)
|
||||||
|
|
||||||
repository, err := git.NewRepository(
|
// Build GitLab provider
|
||||||
gitlabArgs.repository,
|
providerCfg := provider.Config{
|
||||||
gitlabArgs.owner,
|
Provider: provider.GitProviderGitLab,
|
||||||
gitlabArgs.hostname,
|
Hostname: gitlabArgs.hostname,
|
||||||
glToken,
|
Token: glToken,
|
||||||
"flux",
|
}
|
||||||
gitlabArgs.owner+"@users.noreply.gitlab.com",
|
providerClient, err := provider.BuildGitProvider(providerCfg)
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if gitlabArgs.sshHostname != "" {
|
// Lazy go-git repository
|
||||||
repository.SSHHost = gitlabArgs.sshHostname
|
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
|
||||||
}
|
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
gitClient := gogit.New(tmpDir, &http.BasicAuth{
|
||||||
|
Username: gitlabArgs.owner,
|
||||||
|
Password: glToken,
|
||||||
|
})
|
||||||
|
|
||||||
provider := &git.GitLabProvider{
|
// Install manifest config
|
||||||
IsPrivate: gitlabArgs.private,
|
installOptions := install.Options{
|
||||||
IsPersonal: gitlabArgs.personal,
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// create GitLab project if doesn't exists
|
// Source generation and secret config
|
||||||
logger.Actionf("connecting to %s", gitlabArgs.hostname)
|
|
||||||
changed, err := provider.CreateRepository(ctx, repository)
|
|
||||||
if err != nil {
|
|
||||||
return 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
if bootstrapArgs.caFile != "" {
|
||||||
u, err := url.Parse(repoURL)
|
secretOpts.CAFilePath = bootstrapArgs.caFile
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
|
||||||
}
|
}
|
||||||
secretOpts.SSHHostname = u.Host
|
} else {
|
||||||
secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm
|
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
|
||||||
secretOpts.RSAKeyBits = 2048
|
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
|
||||||
}
|
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
|
||||||
|
secretOpts.SSHHostname = githubArgs.hostname
|
||||||
|
|
||||||
secret, err := sourcesecret.Generate(secretOpts)
|
if bootstrapArgs.privateKeyFile != "" {
|
||||||
if err != nil {
|
secretOpts.PrivateKeyPath = bootstrapArgs.privateKeyFile
|
||||||
return err
|
|
||||||
}
|
|
||||||
var s corev1.Secret
|
|
||||||
if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(s.StringData) > 0 {
|
|
||||||
logger.Actionf("configuring deploy key")
|
|
||||||
if err := upsertSecret(ctx, kubeClient, s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
if bootstrapArgs.sshHostname != "" {
|
||||||
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
|
secretOpts.SSHHostname = bootstrapArgs.sshHostname
|
||||||
keyName := "flux"
|
|
||||||
if gitlabArgs.path != "" {
|
|
||||||
keyName = fmt.Sprintf("flux-%s", gitlabArgs.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
if changed, err := provider.AddDeployKey(ctx, repository, ppk, keyName); err != nil {
|
|
||||||
return err
|
|
||||||
} else if changed {
|
|
||||||
logger.Successf("deploy key configured")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(),
|
||||||
)
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
|
||||||
|
}
|
||||||
|
if !gitlabArgs.private {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup bootstrapper with constructed configs
|
||||||
|
b, err := bootstrap.NewGitProviderBootstrapper(gitClient, providerClient, kubeClient, bootstrapOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit and push manifests
|
// Run
|
||||||
if changed, err = repository.Commit(
|
return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply manifests and waiting for sync
|
|
||||||
logger.Actionf("applying sync manifests")
|
|
||||||
if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Successf("bootstrap finished")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -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
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -204,8 +204,6 @@ github.com/fluxcd/pkg/apis/kustomize v0.0.1 h1:TkA80R0GopRY27VJqzKyS6ifiKIAfwBd7
|
|||||||
github.com/fluxcd/pkg/apis/kustomize v0.0.1/go.mod h1:JAFPfnRmcrAoG1gNiA8kmEXsnOBuDyZ/F5X4DAQcVV0=
|
github.com/fluxcd/pkg/apis/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=
|
||||||
|
|||||||
Reference in New Issue
Block a user