Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions pkg/actions/flux/enable.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,23 @@ type Installer struct {
fluxClient InstallerClient
}

// ValidateFlags checks that the Flux flags configured for bootstrap are
// recognised by the Flux CLI, without performing an actual bootstrap. It is
// intended to be called before cluster creation, so misconfigured flags are
// caught early instead of after a lengthy cluster creation.
func ValidateFlags(opts *api.GitOps) error {
if opts == nil || opts.Flux == nil {
return nil
}

fluxClient, err := flux.NewClient(opts.Flux)
if err != nil {
return err
}

return fluxClient.Validate()
}

func New(k8sClientSet kubeclient.Interface, opts *api.GitOps) (*Installer, error) {
if opts.Flux == nil {
return nil, errors.New("expected gitops.flux in cluster configuration but found nil")
Expand Down
3 changes: 3 additions & 0 deletions pkg/ctl/create/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ func doCreateCluster(cmd *cmdutils.Cmd, ngFilter *filter.NodeGroupFilter, params
if _, err := exec.LookPath("flux"); err != nil {
return fmt.Errorf("flux binary is required when gitops configuration is set: %w", err)
}
if err := flux.ValidateFlags(cfg.GitOps); err != nil {
return fmt.Errorf("validating Flux configuration: %w", err)
}
}

if params.InstallWindowsVPCController {
Expand Down
26 changes: 26 additions & 0 deletions pkg/flux/flux.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ import (
const (
fluxBin = "flux"
minSupportedVersion = "0.32.0"

// validationFlag is appended to the args we pass to `flux bootstrap` when
// validating flags. It is never a flag Flux recognises, so pflag will
// fail to parse it. Since pflag stops at the first unrecognised flag,
// an error that doesn't mention validationFlag means a real flag (one
// that appears earlier in the args) is invalid.
validationFlag = "eksctl-internal-validate-only"
)

type Client struct {
Expand Down Expand Up @@ -59,6 +66,25 @@ func (c *Client) Bootstrap() error {
return c.runFluxCmd(args...)
}

// Validate checks that the Flux flags configured for bootstrap are
// recognised by the Flux CLI, without performing an actual bootstrap.
// Flux has no dry-run mode for bootstrap, so this works around that by
// appending a flag Flux cannot recognise: see validationFlag.
func (c *Client) Validate() error {
args := []string{"bootstrap", c.opts.GitProvider}

for k, v := range c.opts.Flags {
args = append(args, fmt.Sprintf("--%s", k), v)
}
args = append(args, fmt.Sprintf("--%s", validationFlag))

err := c.runFluxCmd(args...)
if err == nil || strings.Contains(err.Error(), validationFlag) {
return nil
}
return fmt.Errorf("invalid flux flags: %w", err)
}

func (c *Client) runFluxCmd(args ...string) error {
logger.Debug(fmt.Sprintf("running flux %v ", args))
return c.executor.Exec(fluxBin, args...)
Expand Down
49 changes: 49 additions & 0 deletions pkg/flux/flux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package flux_test

import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -173,4 +175,51 @@ var _ = Describe("Flux", func() {
})
})
})

Context("Validate", func() {
// recognisedFlags simulates the real `flux bootstrap` command:
// pflag parses args in order and fails on the first flag it doesn't
// recognise.
recognisedFlags := map[string]bool{
"owner": true,
"repository": true,
"branch": true,
"path": true,
}

BeforeEach(func() {
fakeExecutor.ExecCalls(func(_ string, args ...string) error {
for _, arg := range args {
if !strings.HasPrefix(arg, "--") {
continue
}
flagName := strings.TrimPrefix(arg, "--")
if !recognisedFlags[flagName] {
return fmt.Errorf("unknown flag: --%s", flagName)
}
}
return nil
})
})

When("all configured flags are recognised by Flux", func() {
BeforeEach(func() {
opts.Flags = api.FluxFlags{"owner": "me", "repository": "my-repo"}
})

It("succeeds, having failed only on the validation sentinel flag", func() {
Expect(fluxClient.Validate()).To(Succeed())
})
})

When("a configured flag is not recognised by Flux", func() {
BeforeEach(func() {
opts.Flags = api.FluxFlags{"owner": "me", "totally-bogus-flag": "oops"}
})

It("returns an error identifying the invalid flag, instead of the validation sentinel", func() {
Expect(fluxClient.Validate()).To(MatchError(ContainSubstring("totally-bogus-flag")))
})
})
})
})