mirror of
https://github.com/fluxcd/flux2.git
synced 2026-02-08 00:37:27 +00:00
Merge pull request #4706 from fluxcd/bootstrap-registry-creds
Add `--registry-creds` flag to bootstrap and install commands
This commit is contained in:
commit
9e52b3ff41
13 changed files with 103 additions and 6 deletions
6
.github/workflows/e2e-bootstrap.yaml
vendored
6
.github/workflows/e2e-bootstrap.yaml
vendored
|
|
@ -53,16 +53,22 @@ jobs:
|
|||
run: |
|
||||
/tmp/flux bootstrap github --manifests ./manifests/install/ \
|
||||
--owner=fluxcd-testing \
|
||||
--image-pull-secret=ghcr-auth \
|
||||
--registry-creds=fluxcd:$GITHUB_TOKEN \
|
||||
--repository=${{ steps.vars.outputs.test_repo_name }} \
|
||||
--branch=main \
|
||||
--path=test-cluster \
|
||||
--team=team-z
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||
- name: verify image pull secret
|
||||
run: |
|
||||
kubectl -n flux-system get secret ghcr-auth | grep dockerconfigjson
|
||||
- name: bootstrap no-op
|
||||
run: |
|
||||
/tmp/flux bootstrap github --manifests ./manifests/install/ \
|
||||
--owner=fluxcd-testing \
|
||||
--image-pull-secret=ghcr-auth \
|
||||
--repository=${{ steps.vars.outputs.test_repo_name }} \
|
||||
--branch=main \
|
||||
--path=test-cluster \
|
||||
|
|
|
|||
|
|
@ -52,8 +52,9 @@ type bootstrapFlags struct {
|
|||
extraComponents []string
|
||||
requiredComponents []string
|
||||
|
||||
registry string
|
||||
imagePullSecret string
|
||||
registry string
|
||||
registryCredential string
|
||||
imagePullSecret string
|
||||
|
||||
secretName string
|
||||
tokenAuth bool
|
||||
|
|
@ -98,6 +99,8 @@ func init() {
|
|||
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd",
|
||||
"container registry where the Flux controller images are published")
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registryCredential, "registry-creds", "",
|
||||
"container registry credentials in the format 'user:password', requires --image-pull-secret to be set")
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.imagePullSecret, "image-pull-secret", "",
|
||||
"Kubernetes secret name used for pulling the controller images from a private registry")
|
||||
|
||||
|
|
@ -181,6 +184,14 @@ func bootstrapValidate() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if bootstrapArgs.registryCredential != "" && bootstrapArgs.imagePullSecret == "" {
|
||||
return fmt.Errorf("--registry-creds requires --image-pull-secret to be set")
|
||||
}
|
||||
|
||||
if bootstrapArgs.registryCredential != "" && len(strings.Split(bootstrapArgs.registryCredential, ":")) != 2 {
|
||||
return fmt.Errorf("invalid --registry-creds format, expected 'user:password'")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
|
|||
Namespace: *kubeconfigArgs.Namespace,
|
||||
Components: bootstrapComponents(),
|
||||
Registry: bootstrapArgs.registry,
|
||||
RegistryCredential: bootstrapArgs.registryCredential,
|
||||
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
||||
NetworkPolicy: bootstrapArgs.networkPolicy,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/fluxcd/pkg/git"
|
||||
"github.com/fluxcd/pkg/git/gogit"
|
||||
|
||||
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/pkg/bootstrap"
|
||||
|
|
@ -35,8 +38,6 @@ import (
|
|||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sync"
|
||||
"github.com/fluxcd/pkg/git"
|
||||
"github.com/fluxcd/pkg/git/gogit"
|
||||
)
|
||||
|
||||
var bootstrapGitCmd = &cobra.Command{
|
||||
|
|
@ -201,6 +202,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||
Namespace: *kubeconfigArgs.Namespace,
|
||||
Components: bootstrapComponents(),
|
||||
Registry: bootstrapArgs.registry,
|
||||
RegistryCredential: bootstrapArgs.registryCredential,
|
||||
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
||||
NetworkPolicy: bootstrapArgs.networkPolicy,
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@ func bootstrapGiteaCmdRun(cmd *cobra.Command, args []string) error {
|
|||
Namespace: *kubeconfigArgs.Namespace,
|
||||
Components: bootstrapComponents(),
|
||||
Registry: bootstrapArgs.registry,
|
||||
RegistryCredential: bootstrapArgs.registryCredential,
|
||||
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
||||
NetworkPolicy: bootstrapArgs.networkPolicy,
|
||||
|
|
|
|||
|
|
@ -191,6 +191,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||
Namespace: *kubeconfigArgs.Namespace,
|
||||
Components: bootstrapComponents(),
|
||||
Registry: bootstrapArgs.registry,
|
||||
RegistryCredential: bootstrapArgs.registryCredential,
|
||||
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
||||
NetworkPolicy: bootstrapArgs.networkPolicy,
|
||||
|
|
|
|||
|
|
@ -216,6 +216,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||
Namespace: *kubeconfigArgs.Namespace,
|
||||
Components: bootstrapComponents(),
|
||||
Registry: bootstrapArgs.registry,
|
||||
RegistryCredential: bootstrapArgs.registryCredential,
|
||||
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
||||
NetworkPolicy: bootstrapArgs.networkPolicy,
|
||||
|
|
|
|||
|
|
@ -21,16 +21,20 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/v2/pkg/status"
|
||||
)
|
||||
|
||||
|
|
@ -66,6 +70,7 @@ type installFlags struct {
|
|||
defaultComponents []string
|
||||
extraComponents []string
|
||||
registry string
|
||||
registryCredential string
|
||||
imagePullSecret string
|
||||
branch string
|
||||
watchAllNamespaces bool
|
||||
|
|
@ -92,6 +97,8 @@ func init() {
|
|||
installCmd.Flags().StringVar(&installArgs.manifestsPath, "manifests", "", "path to the manifest directory")
|
||||
installCmd.Flags().StringVar(&installArgs.registry, "registry", rootArgs.defaults.Registry,
|
||||
"container registry where the toolkit images are published")
|
||||
installCmd.Flags().StringVar(&installArgs.registryCredential, "registry-creds", "",
|
||||
"container registry credentials in the format 'user:password', requires --image-pull-secret to be set")
|
||||
installCmd.Flags().StringVar(&installArgs.imagePullSecret, "image-pull-secret", "",
|
||||
"Kubernetes secret name used for pulling the toolkit images from a private registry")
|
||||
installCmd.Flags().BoolVar(&installArgs.watchAllNamespaces, "watch-all-namespaces", rootArgs.defaults.WatchAllNamespaces,
|
||||
|
|
@ -124,6 +131,14 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if installArgs.registryCredential != "" && installArgs.imagePullSecret == "" {
|
||||
return fmt.Errorf("--registry-creds requires --image-pull-secret to be set")
|
||||
}
|
||||
|
||||
if installArgs.registryCredential != "" && len(strings.Split(installArgs.registryCredential, ":")) != 2 {
|
||||
return fmt.Errorf("invalid --registry-creds format, expected 'user:password'")
|
||||
}
|
||||
|
||||
if ver, err := getVersion(installArgs.version); err != nil {
|
||||
return err
|
||||
} else {
|
||||
|
|
@ -154,6 +169,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||
Namespace: *kubeconfigArgs.Namespace,
|
||||
Components: components,
|
||||
Registry: installArgs.registry,
|
||||
RegistryCredential: installArgs.registryCredential,
|
||||
ImagePullSecret: installArgs.imagePullSecret,
|
||||
WatchAllNamespaces: installArgs.watchAllNamespaces,
|
||||
NetworkPolicy: installArgs.networkPolicy,
|
||||
|
|
@ -224,6 +240,29 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||
|
||||
fmt.Fprintln(os.Stderr, applyOutput)
|
||||
|
||||
if opts.ImagePullSecret != "" && opts.RegistryCredential != "" {
|
||||
logger.Actionf("generating image pull secret %s", opts.ImagePullSecret)
|
||||
credentials := strings.SplitN(opts.RegistryCredential, ":", 2)
|
||||
secretOpts := sourcesecret.Options{
|
||||
Name: opts.ImagePullSecret,
|
||||
Namespace: opts.Namespace,
|
||||
Registry: opts.Registry,
|
||||
Username: credentials[0],
|
||||
Password: credentials[1],
|
||||
}
|
||||
imagePullSecret, err := sourcesecret.Generate(secretOpts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("install failed: %w", err)
|
||||
}
|
||||
var s corev1.Secret
|
||||
if err := yaml.Unmarshal([]byte(imagePullSecret.Content), &s); err != nil {
|
||||
return fmt.Errorf("install failed: %w", err)
|
||||
}
|
||||
if err := upsertSecret(ctx, kubeClient, s); err != nil {
|
||||
return fmt.Errorf("install failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
kubeConfig, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("install failed: %w", err)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,11 @@ func TestInstall(t *testing.T) {
|
|||
args: "install unexpectedPosArg --namespace=example",
|
||||
assert: assertError(`unknown command "unexpectedPosArg" for "flux install"`),
|
||||
},
|
||||
{
|
||||
name: "missing image pull secret",
|
||||
args: "install --registry-creds=fluxcd:test",
|
||||
assert: assertError(`--registry-creds requires --image-pull-secret to be set`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
|||
|
|
@ -173,6 +173,26 @@ func reconcileSecret(ctx context.Context, kube client.Client, secret corev1.Secr
|
|||
return kube.Update(ctx, &existing)
|
||||
}
|
||||
|
||||
func reconcileImagePullSecret(ctx context.Context, kube client.Client, installOpts install.Options) error {
|
||||
credentials := strings.SplitN(installOpts.RegistryCredential, ":", 2)
|
||||
dcj, err := sourcesecret.GenerateDockerConfigJson(installOpts.Registry, credentials[0], credentials[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate docker config json: %w", err)
|
||||
}
|
||||
|
||||
secret := corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: installOpts.Namespace,
|
||||
Name: installOpts.ImagePullSecret,
|
||||
},
|
||||
StringData: map[string]string{
|
||||
corev1.DockerConfigJsonKey: string(dcj),
|
||||
},
|
||||
Type: corev1.SecretTypeDockerConfigJson,
|
||||
}
|
||||
return reconcileSecret(ctx, kube, secret)
|
||||
}
|
||||
|
||||
func kustomizationPathDiffers(ctx context.Context, kube client.Client, objKey client.ObjectKey, path string) (string, error) {
|
||||
var k kustomizev1.Kustomization
|
||||
if err := kube.Get(ctx, objKey, &k); err != nil {
|
||||
|
|
|
|||
|
|
@ -207,6 +207,14 @@ func (b *PlainGitBootstrapper) ReconcileComponents(ctx context.Context, manifest
|
|||
b.logger.Successf("installed components")
|
||||
}
|
||||
|
||||
// Reconcile image pull secret if needed
|
||||
if options.ImagePullSecret != "" && options.RegistryCredential != "" {
|
||||
if err := reconcileImagePullSecret(ctx, b.kube, options); err != nil {
|
||||
return fmt.Errorf("failed to reconcile image pull secret: %w", err)
|
||||
}
|
||||
b.logger.Successf("reconciled image pull secret %s", options.ImagePullSecret)
|
||||
}
|
||||
|
||||
b.logger.Successf("reconciled components")
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ type Options struct {
|
|||
ComponentsExtra []string
|
||||
EventsAddr string
|
||||
Registry string
|
||||
RegistryCredential string
|
||||
ImagePullSecret string
|
||||
WatchAllNamespaces bool
|
||||
NetworkPolicy bool
|
||||
|
|
@ -46,6 +47,7 @@ func MakeDefaultOptions() Options {
|
|||
ComponentsExtra: []string{"image-reflector-controller", "image-automation-controller"},
|
||||
EventsAddr: "",
|
||||
Registry: "ghcr.io/fluxcd",
|
||||
RegistryCredential: "",
|
||||
ImagePullSecret: "",
|
||||
WatchAllNamespaces: true,
|
||||
NetworkPolicy: true,
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
|
|||
|
||||
var dockerCfgJson []byte
|
||||
if options.Registry != "" {
|
||||
dockerCfgJson, err = generateDockerConfigJson(options.Registry, options.Username, options.Password)
|
||||
dockerCfgJson, err = GenerateDockerConfigJson(options.Registry, options.Username, options.Password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate json for docker config: %w", err)
|
||||
}
|
||||
|
|
@ -223,7 +223,7 @@ func resourceToString(data []byte) string {
|
|||
return string(data)
|
||||
}
|
||||
|
||||
func generateDockerConfigJson(url, username, password string) ([]byte, error) {
|
||||
func GenerateDockerConfigJson(url, username, password string) ([]byte, error) {
|
||||
cred := fmt.Sprintf("%s:%s", username, password)
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(cred))
|
||||
cfg := DockerConfigJSON{
|
||||
|
|
|
|||
Loading…
Reference in a new issue