Skip to content

Commit

Permalink
Allow defining a subset of subcharts to be updated when using CRD upg…
Browse files Browse the repository at this point in the history
…rader (#51)
  • Loading branch information
burmanm authored Jun 21, 2024
1 parent 96e1f69 commit 94ac857
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 16 deletions.
20 changes: 17 additions & 3 deletions cmd/kubectl-k8ssandra/helm/crds.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type options struct {
chartRepo string
repoURL string
download bool
chartList []string
}

func newOptions(streams genericclioptions.IOStreams) *options {
Expand All @@ -49,13 +50,17 @@ func NewUpgradeCmd(streams genericclioptions.IOStreams) *cobra.Command {
Short: "upgrade CRDs from chart to target version",
Example: fmt.Sprintf(upgraderExample, "kubectl k8ssandra helm crds"),
SilenceUsage: true,
RunE: func(c *cobra.Command, args []string) error {
PreRunE: func(c *cobra.Command, args []string) error {
if err := o.Complete(c, args); err != nil {
return err
}
if err := o.Validate(); err != nil {
return err
}

return nil
},
RunE: func(c *cobra.Command, args []string) error {
if err := o.Run(); err != nil {
log.Error("Error upgrading CustomResourceDefinitions", "error", err)
return err
Expand All @@ -70,16 +75,25 @@ func NewUpgradeCmd(streams genericclioptions.IOStreams) *cobra.Command {
fl.StringVar(&o.chartVersion, "chartVersion", "", "chartVersion to upgrade to")
fl.StringVar(&o.chartRepo, "chartRepo", "", "optional chart repository name to override the default (k8ssandra)")
fl.StringVar(&o.repoURL, "repoURL", "", "optional chart repository url to override the default (helm.k8ssandra.io)")
fl.StringSliceVar(&o.chartList, "charts", []string{}, "optional list of dependency charts to upgrade, default is just the main chart. Use \"_\" to update all the subcharts.")
fl.BoolVar(&o.download, "download", false, "only download the chart")
o.configFlags.AddFlags(fl)

if err := cmd.MarkFlagRequired("chartName"); err != nil {
panic(err)
}

if err := cmd.MarkFlagRequired("chartVersion"); err != nil {
panic(err)
}

return cmd
}

// Complete parses the arguments and necessary flags to options
func (c *options) Complete(cmd *cobra.Command, args []string) error {
var err error
if c.chartName == "" && c.chartVersion == "" {
if c.chartName == "" || c.chartVersion == "" {
return errNotEnoughParameters
}

Expand Down Expand Up @@ -118,7 +132,7 @@ func (c *options) Run() error {

ctx := context.Background()

upgrader, err := helmutil.NewUpgrader(kubeClient, c.chartRepo, c.repoURL, c.chartName)
upgrader, err := helmutil.NewUpgrader(kubeClient, c.chartRepo, c.repoURL, c.chartName, c.chartList)
if err != nil {
return err
}
Expand Down
58 changes: 58 additions & 0 deletions cmd/kubectl-k8ssandra/helm/helm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package helm

import (
"testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/require"
"k8s.io/cli-runtime/pkg/genericiooptions"
)

func TestSimplestCRDCommand(t *testing.T) {
require := require.New(t)

cmd := NewUpgradeCmd(genericiooptions.NewTestIOStreamsDiscard())
cmd.RunE = func(cmd *cobra.Command, args []string) error {
return nil
}

cmd.Root().SetArgs([]string{"upgrade", "--chartName", "k8ssandra-operator", "--chartVersion", "1.0.0"})
require.NoError(cmd.Execute())
}

func TestMissingParamsCRDCommand(t *testing.T) {
require := require.New(t)

cmd := NewUpgradeCmd(genericiooptions.NewTestIOStreamsDiscard())
cmd.RunE = func(cmd *cobra.Command, args []string) error {
return nil
}

cmd.Root().SetArgs([]string{"upgrade", "--chartName", "k8ssandra-operator"})
require.Error(cmd.Execute())
}

func TestInvalidParamsCRDCommand(t *testing.T) {
require := require.New(t)

cmd := NewUpgradeCmd(genericiooptions.NewTestIOStreamsDiscard())
cmd.RunE = func(cmd *cobra.Command, args []string) error {
return nil
}

cmd.Root().SetArgs([]string{"upgrade", "--chartName", "k8ssandra-operator", "--chartTarget", "1.0.0"})
require.Error(cmd.Execute())
}

func TestAllParamsCRDCommand(t *testing.T) {
require := require.New(t)

cmd := NewUpgradeCmd(genericiooptions.NewTestIOStreamsDiscard())
cmd.RunE = func(cmd *cobra.Command, args []string) error {
return nil
}

cmd.Root().SetArgs([]string{"upgrade", "--chartName", "k8ssandra-operator", "--chartVersion", "1.0.0",
"--chartRepo", "devel", "--repoURL", "https://helm.k8ssandra.io/devel", "--download", "true", "--charts", "cass-operator", "--charts", "k8ssandra-operator,cass-operator"})
require.NoError(cmd.Execute())
}
54 changes: 44 additions & 10 deletions pkg/helmutil/crds.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,27 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
AllSubCharts = "_"
)

// Upgrader is a utility to update the CRDs in a helm chart's pre-upgrade hook
type Upgrader struct {
client client.Client
repoName string
repoURL string
chartName string
subCharts []string
}

// NewUpgrader returns a new Upgrader client
func NewUpgrader(c client.Client, repoName, repoURL, chartName string) (*Upgrader, error) {
func NewUpgrader(c client.Client, repoName, repoURL, chartName string, subCharts []string) (*Upgrader, error) {
return &Upgrader{
client: c,
repoName: repoName,
repoURL: repoURL,
chartName: chartName,
subCharts: subCharts,
}, nil
}

Expand Down Expand Up @@ -73,12 +79,11 @@ func (u *Upgrader) Upgrade(ctx context.Context, chartVersion string) ([]unstruct
crds := make([]unstructured.Unstructured, 0)

// For each dir under the charts subdir, check the "crds/"
paths, _ := findCRDDirs(chartDir)
paths, _ := findCRDDirs(chartDir, u.subCharts)

for _, path := range paths {
log.Debug("Processing CustomResourceDefinition directory", "path", path)
err = parseChartCRDs(&crds, path)
if err != nil {
if err := parseChartCRDs(&crds, path); err != nil {
return nil, err
}
}
Expand Down Expand Up @@ -106,18 +111,47 @@ func (u *Upgrader) Upgrade(ctx context.Context, chartVersion string) ([]unstruct
return crds, err
}

func findCRDDirs(chartDir string) ([]string, error) {
func findCRDDirs(chartDir string, subCharts []string) ([]string, error) {
chartsList := make(map[string]struct{})
for _, chart := range subCharts {
chartsList[chart] = struct{}{}
}

chartFilter := func(path string, info os.FileInfo) bool {
if !info.IsDir() || filepath.Base(path) != "crds" {
return false
}

chartParts := strings.Split(filepath.Clean(path), string(os.PathSeparator))
chartName := chartParts[len(chartParts)-2]
subChart := false
if len(chartParts) > 3 {
subChart = chartParts[len(chartParts)-3] == "charts"
}

if !subChart {
return true
}

if _, found := chartsList[AllSubCharts]; found {
return true
}

if _, found := chartsList[chartName]; found {
return true
}

return false
}
dirs := make([]string, 0)
err := filepath.Walk(chartDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
if strings.HasSuffix(path, "crds") {
dirs = append(dirs, path)
}
return nil
if chartFilter(path, info) {
dirs = append(dirs, path)
}

return nil
})
return dirs, err
Expand Down
2 changes: 1 addition & 1 deletion pkg/helmutil/crds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestUpgradingCRDs(t *testing.T) {
require.NoError(cleanCache("k8ssandra", chartName))

// creating new upgrader
u, err := helmutil.NewUpgrader(kubeClient, helmutil.K8ssandraRepoName, helmutil.StableK8ssandraRepoURL, chartName)
u, err := helmutil.NewUpgrader(kubeClient, helmutil.K8ssandraRepoName, helmutil.StableK8ssandraRepoURL, chartName, []string{})
require.NoError(err)

crds, err := u.Upgrade(context.TODO(), "0.42.0")
Expand Down
34 changes: 32 additions & 2 deletions pkg/helmutil/crds_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,50 @@ func TestFindCRDDirs(t *testing.T) {

require.NoError(os.MkdirAll(chartDir+"/downstream-operator/crds", 0755))

dirs, err := findCRDDirs(chartDir)
dirs, err := findCRDDirs(chartDir, []string{"downstream-operator"})
require.NoError(err)

require.Len(dirs, 1)
require.Equal(chartDir+"/downstream-operator/crds", dirs[0])

dirs, err = findCRDDirs(chartDir, []string{""})
require.NoError(err)

require.Len(dirs, 1)
require.Equal(chartDir+"/downstream-operator/crds", dirs[0])

dirs, err = findCRDDirs(chartDir, nil)
require.NoError(err)

require.Len(dirs, 1)
require.Equal(chartDir+"/downstream-operator/crds", dirs[0])

require.NoError(os.MkdirAll(chartDir+"/downstream-operator/charts/k8ssandra-operator/crds", 0755))
require.NoError(os.MkdirAll(chartDir+"/downstream-operator/charts/k8ssandra-operator/charts/cass-operator/crds", 0755))
require.NoError(os.MkdirAll(chartDir+"/downstream-operator/charts/third-party-operator/crds", 0755))
dirs, err = findCRDDirs(chartDir, []string{AllSubCharts})
require.NoError(err)

dirs, err = findCRDDirs(chartDir)
require.Len(dirs, 4)

dirs, err = findCRDDirs(chartDir, []string{"k8ssandra-operator", "cass-operator"})
require.NoError(err)

require.Len(dirs, 3)
require.Contains(dirs, chartDir+"/downstream-operator/crds")
require.Contains(dirs, chartDir+"/downstream-operator/charts/k8ssandra-operator/crds")
require.Contains(dirs, chartDir+"/downstream-operator/charts/k8ssandra-operator/charts/cass-operator/crds")

dirs, err = findCRDDirs(chartDir, []string{"k8ssandra-operator"})
require.NoError(err)

require.Len(dirs, 2)
require.Contains(dirs, chartDir+"/downstream-operator/crds")
require.Contains(dirs, chartDir+"/downstream-operator/charts/k8ssandra-operator/crds")

dirs, err = findCRDDirs(chartDir, []string{""})
require.NoError(err)

require.Len(dirs, 1)
require.Contains(dirs, chartDir+"/downstream-operator/crds")
}

0 comments on commit 94ac857

Please sign in to comment.