diff --git a/CHANGELOG.md b/CHANGELOG.md index bb6fa12d2d..4dc453eb70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ IMPROVEMENTS: * Support `ui.dashboardURLTemplates.service` value for setting [dashboard URL templates](https://www.consul.io/docs/agent/options#ui_config_dashboard_url_templates_service). [[GH-937](https://github.com/hashicorp/consul-k8s/pull/937)] * Allow using dash-separated names for config entries when using `kubectl`. [[GH-965](https://github.com/hashicorp/consul-k8s/pull/965)] * Support Pod Security Policies with Vault integration. [[GH-985](https://github.com/hashicorp/consul-k8s/pull/985)] +* CLI + * Show a diff when upgrading a Consul installation on Kubernetes [[GH-934](https://github.com/hashicorp/consul-k8s/pull/934)] * Control Plane * Support the value `$POD_NAME` for the annotation `consul.hashicorp.com/service-meta-*` that will now be interpolated and set to the pod's name in the service's metadata. [[GH-982](https://github.com/hashicorp/consul-k8s/pull/982)] * Allow managing Consul sidecar resources via annotations. [[GH-956](https://github.com/hashicorp/consul-k8s/pull/956)] diff --git a/cli/cmd/common/fixtures/consul/Chart.yaml b/cli/cmd/common/fixtures/consul/Chart.yaml deleted file mode 100644 index 54c6e609d9..0000000000 --- a/cli/cmd/common/fixtures/consul/Chart.yaml +++ /dev/null @@ -1 +0,0 @@ -chart \ No newline at end of file diff --git a/cli/cmd/common/fixtures/consul/templates/foo.yaml b/cli/cmd/common/fixtures/consul/templates/foo.yaml deleted file mode 100644 index 1910281566..0000000000 --- a/cli/cmd/common/fixtures/consul/templates/foo.yaml +++ /dev/null @@ -1 +0,0 @@ -foo \ No newline at end of file diff --git a/cli/cmd/common/fixtures/consul/values.yaml b/cli/cmd/common/fixtures/consul/values.yaml deleted file mode 100644 index 972ac6208d..0000000000 --- a/cli/cmd/common/fixtures/consul/values.yaml +++ /dev/null @@ -1 +0,0 @@ -values \ No newline at end of file diff --git a/cli/cmd/common/utils.go b/cli/cmd/common/utils.go deleted file mode 100644 index 2cff8eccec..0000000000 --- a/cli/cmd/common/utils.go +++ /dev/null @@ -1,136 +0,0 @@ -package common - -import ( - "embed" - "errors" - "fmt" - "os" - "path/filepath" - "strings" - - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart/loader" - helmCLI "helm.sh/helm/v3/pkg/cli" - "k8s.io/cli-runtime/pkg/genericclioptions" -) - -const ( - DefaultReleaseName = "consul" - DefaultReleaseNamespace = "consul" - chartFileName = "Chart.yaml" - valuesFileName = "values.yaml" - templatesDirName = "templates" - TopLevelChartDirName = "consul" - - // CLILabelKey and CLILabelValue are added to each secret on creation so the CLI knows - // which key to delete on an uninstall. - CLILabelKey = "managed-by" - CLILabelValue = "consul-k8s" -) - -// ReadChartFiles reads the chart files from the embedded file system, and loads their contents into -// []*loader.BufferedFile. This is a format that the Helm Go SDK functions can read from to create a chart to install -// from. The names of these files are important, as there are case statements in the Helm Go SDK looking for files named -// "Chart.yaml" or "templates/.yaml", which is why even though the embedded file system has them named -// "consul/Chart.yaml" we have to strip the "consul" prefix out, which is done by the call to the helper method readFile. -func ReadChartFiles(chart embed.FS, chartDirName string) ([]*loader.BufferedFile, error) { - var chartFiles []*loader.BufferedFile - - // Load Chart.yaml and values.yaml first. - for _, f := range []string{chartFileName, valuesFileName} { - file, err := readFile(chart, filepath.Join(chartDirName, f), chartDirName) - if err != nil { - return nil, err - } - chartFiles = append(chartFiles, file) - } - - // Now load everything under templates/. - dirs, err := chart.ReadDir(filepath.Join(chartDirName, templatesDirName)) - if err != nil { - return nil, err - } - for _, f := range dirs { - if f.IsDir() { - // We only need to include files in the templates directory. - continue - } - - file, err := readFile(chart, filepath.Join(chartDirName, templatesDirName, f.Name()), chartDirName) - if err != nil { - return nil, err - } - chartFiles = append(chartFiles, file) - } - - return chartFiles, nil -} - -func readFile(chart embed.FS, f string, pathPrefix string) (*loader.BufferedFile, error) { - bytes, err := chart.ReadFile(f) - if err != nil { - return nil, err - } - // Remove the path prefix. - rel, err := filepath.Rel(pathPrefix, f) - if err != nil { - return nil, err - } - return &loader.BufferedFile{ - Name: rel, - Data: bytes, - }, nil -} - -// Abort returns true if the raw input string is not equal to "y" or "yes". -func Abort(raw string) bool { - confirmation := strings.TrimSuffix(raw, "\n") - return !(strings.ToLower(confirmation) == "y" || strings.ToLower(confirmation) == "yes") -} - -// InitActionConfig initializes a Helm Go SDK action configuration. This function currently uses a hack to override the -// namespace field that gets set in the K8s client set up by the SDK. -func InitActionConfig(actionConfig *action.Configuration, namespace string, settings *helmCLI.EnvSettings, logger action.DebugLog) (*action.Configuration, error) { - getter := settings.RESTClientGetter() - configFlags := getter.(*genericclioptions.ConfigFlags) - configFlags.Namespace = &namespace - err := actionConfig.Init(settings.RESTClientGetter(), namespace, - os.Getenv("HELM_DRIVER"), logger) - if err != nil { - return nil, fmt.Errorf("error setting up helm action configuration to find existing installations: %s", err) - } - return actionConfig, nil -} - -// CheckForInstallations uses the helm Go SDK to find helm releases in all namespaces where the chart name is -// "consul", and returns the release name and namespace if found, or an error if not found. -func CheckForInstallations(settings *helmCLI.EnvSettings, uiLogger action.DebugLog) (string, string, error) { - // Need a specific action config to call helm list, where namespace is NOT specified. - listConfig := new(action.Configuration) - if err := listConfig.Init(settings.RESTClientGetter(), "", - os.Getenv("HELM_DRIVER"), uiLogger); err != nil { - return "", "", fmt.Errorf("couldn't initialize helm config: %s", err) - } - - lister := action.NewList(listConfig) - lister.AllNamespaces = true - lister.StateMask = action.ListAll - res, err := lister.Run() - if err != nil { - return "", "", fmt.Errorf("couldn't check for installations: %s", err) - } - - for _, rel := range res { - if rel.Chart.Metadata.Name == "consul" { - return rel.Name, rel.Namespace, nil - } - } - return "", "", errors.New("couldn't find consul installation") -} - -func CloseWithError(c *BaseCommand) { - if err := c.Close(); err != nil { - c.Log.Error(err.Error()) - os.Exit(1) - } -} diff --git a/cli/cmd/common/utils_test.go b/cli/cmd/common/utils_test.go deleted file mode 100644 index fc9bf5292c..0000000000 --- a/cli/cmd/common/utils_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package common - -import ( - "embed" - "testing" - - "github.com/stretchr/testify/require" -) - -//go:embed fixtures/consul/* fixtures/consul/templates/_helpers.tpl -var testChart embed.FS - -func TestReadChartFiles(t *testing.T) { - files, err := ReadChartFiles(testChart, "fixtures/consul") - require.NoError(t, err) - var foundChart, foundValues, foundTemplate, foundHelper bool - for _, f := range files { - if f.Name == "Chart.yaml" { - require.Equal(t, "chart", string(f.Data)) - foundChart = true - } - if f.Name == "values.yaml" { - require.Equal(t, "values", string(f.Data)) - foundValues = true - } - if f.Name == "templates/foo.yaml" { - require.Equal(t, "foo", string(f.Data)) - foundTemplate = true - } - if f.Name == "templates/_helpers.tpl" { - require.Equal(t, "helpers", string(f.Data)) - foundHelper = true - } - } - require.True(t, foundChart) - require.True(t, foundValues) - require.True(t, foundTemplate) - require.True(t, foundHelper) -} diff --git a/cli/cmd/install/install.go b/cli/cmd/install/install.go index edc9308481..dd9f9f7abc 100644 --- a/cli/cmd/install/install.go +++ b/cli/cmd/install/install.go @@ -9,10 +9,11 @@ import ( "time" consulChart "github.com/hashicorp/consul-k8s/charts" - "github.com/hashicorp/consul-k8s/cli/cmd/common" - "github.com/hashicorp/consul-k8s/cli/cmd/common/flag" - "github.com/hashicorp/consul-k8s/cli/cmd/common/terminal" + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" "github.com/hashicorp/consul-k8s/cli/config" + "github.com/hashicorp/consul-k8s/cli/helm" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart/loader" helmCLI "helm.sh/helm/v3/pkg/cli" @@ -313,7 +314,7 @@ func (c *Command) Run(args []string) int { // Without informing the user, default global.name to consul if it hasn't been set already. We don't allow setting // the release name, and since that is hardcoded to "consul", setting global.name to "consul" makes it so resources // aren't double prefixed with "consul-consul-...". - vals = MergeMaps(config.Convert(config.GlobalNameConsul), vals) + vals = common.MergeMaps(config.Convert(config.GlobalNameConsul), vals) // Dry Run should exit here, no need to actual locate/download the charts. if c.flagDryRun { @@ -342,7 +343,7 @@ func (c *Command) Run(args []string) int { // Setup action configuration for Helm Go SDK function calls. actionConfig := new(action.Configuration) - actionConfig, err = common.InitActionConfig(actionConfig, c.flagNamespace, settings, uiLogger) + actionConfig, err = helm.InitActionConfig(actionConfig, c.flagNamespace, settings, uiLogger) if err != nil { c.UI.Output(err.Error(), terminal.WithErrorStyle()) return 1 @@ -357,7 +358,7 @@ func (c *Command) Run(args []string) int { install.Timeout = c.timeoutDuration // Read the embedded chart files into []*loader.BufferedFile. - chartFiles, err := common.ReadChartFiles(consulChart.ConsulHelmChart, common.TopLevelChartDirName) + chartFiles, err := helm.ReadChartFiles(consulChart.ConsulHelmChart, common.TopLevelChartDirName) if err != nil { c.UI.Output(err.Error(), terminal.WithErrorStyle()) return 1 @@ -455,32 +456,11 @@ func (c *Command) mergeValuesFlagsWithPrecedence(settings *helmCLI.EnvSettings) if c.flagPreset != defaultPreset { // Note the ordering of the function call, presets have lower precedence than set vals. presetMap := config.Presets[c.flagPreset].(map[string]interface{}) - vals = MergeMaps(presetMap, vals) + vals = common.MergeMaps(presetMap, vals) } return vals, err } -// MergeMaps is a helper function used in Run. Merges two maps giving b precedent. -// @source: https://github.com/helm/helm/blob/main/pkg/cli/values/options.go -func MergeMaps(a, b map[string]interface{}) map[string]interface{} { - out := make(map[string]interface{}, len(a)) - for k, v := range a { - out[k] = v - } - for k, v := range b { - if v, ok := v.(map[string]interface{}); ok { - if bv, ok := out[k]; ok { - if bv, ok := bv.(map[string]interface{}); ok { - out[k] = MergeMaps(bv, v) - continue - } - } - } - out[k] = v - } - return out -} - // validateFlags is a helper function that performs sanity checks on the user's provided flags. func (c *Command) validateFlags(args []string) error { if err := c.set.Parse(args); err != nil { @@ -490,14 +470,14 @@ func (c *Command) validateFlags(args []string) error { return errors.New("should have no non-flag arguments") } if len(c.flagValueFiles) != 0 && c.flagPreset != defaultPreset { - return fmt.Errorf("Cannot set both -%s and -%s", flagNameConfigFile, flagNamePreset) + return fmt.Errorf("cannot set both -%s and -%s", flagNameConfigFile, flagNamePreset) } if _, ok := config.Presets[c.flagPreset]; c.flagPreset != defaultPreset && !ok { return fmt.Errorf("'%s' is not a valid preset", c.flagPreset) } - if !validLabel(c.flagNamespace) { + if !common.IsValidLabel(c.flagNamespace) { return fmt.Errorf("'%s' is an invalid namespace. Namespaces follow the RFC 1123 label convention and must "+ - "consist of a lower case alphanumeric character or '-' and must start/end with an alphanumeric", c.flagNamespace) + "consist of a lower case alphanumeric character or '-' and must start/end with an alphanumeric character", c.flagNamespace) } duration, err := time.ParseDuration(c.flagTimeout) if err != nil { @@ -507,7 +487,7 @@ func (c *Command) validateFlags(args []string) error { if len(c.flagValueFiles) != 0 { for _, filename := range c.flagValueFiles { if _, err := os.Stat(filename); err != nil && os.IsNotExist(err) { - return fmt.Errorf("File '%s' does not exist.", filename) + return fmt.Errorf("file '%s' does not exist", filename) } } } @@ -518,21 +498,6 @@ func (c *Command) validateFlags(args []string) error { return nil } -// validLabel is a helper function that checks if a string follows RFC 1123 labels. -func validLabel(s string) bool { - for i, c := range s { - alphanum := ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') - // If the character is not the last or first, it can be a dash. - if i != 0 && i != (len(s)-1) { - alphanum = alphanum || (c == '-') - } - if !alphanum { - return false - } - } - return true -} - // checkValidEnterprise checks and validates an enterprise installation. // When an enterprise license secret is provided, check that the secret exists // in the "consul" namespace. diff --git a/cli/cmd/install/install_test.go b/cli/cmd/install/install_test.go index ba6608d256..005e711adb 100644 --- a/cli/cmd/install/install_test.go +++ b/cli/cmd/install/install_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "github.com/hashicorp/consul-k8s/cli/cmd/common" + "github.com/hashicorp/consul-k8s/cli/common" "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" @@ -121,54 +121,6 @@ func TestValidateFlags(t *testing.T) { } } -// TestValidLabel calls validLabel() which checks strings match RFC 1123 label convention. -func TestValidLabel(t *testing.T) { - testCases := []struct { - description string - input string - expected bool - }{ - { - "Standard name with leading numbers works.", - "1234-abc", - true, - }, - { - "All lower case letters works.", - "peppertrout", - true, - }, - { - "Test that dashes in the middle are allowed.", - "pepper-trout", - true, - }, - { - "Capitals violate RFC 1123 lower case label.", - "Peppertrout", - false, - }, - { - "Underscores are not permitted anywhere.", - "ab_cd", - false, - }, - { - "The dash must be in the middle of the word, not on the start/end character.", - "peppertrout-", - false, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.description, func(t *testing.T) { - if result := validLabel(testCase.input); result != testCase.expected { - t.Errorf("Incorrect output, got %v and expected %v", result, testCase.expected) - } - }) - } -} - // getInitializedCommand sets up a command struct for tests. func getInitializedCommand(t *testing.T) *Command { t.Helper() diff --git a/cli/cmd/status/status.go b/cli/cmd/status/status.go index 6d8e636b31..7b7a02a03d 100644 --- a/cli/cmd/status/status.go +++ b/cli/cmd/status/status.go @@ -9,9 +9,10 @@ import ( "helm.sh/helm/v3/pkg/release" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/hashicorp/consul-k8s/cli/cmd/common" - "github.com/hashicorp/consul-k8s/cli/cmd/common/flag" - "github.com/hashicorp/consul-k8s/cli/cmd/common/terminal" + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/consul-k8s/cli/helm" "helm.sh/helm/v3/pkg/action" helmCLI "helm.sh/helm/v3/pkg/cli" "k8s.io/client-go/kubernetes" @@ -137,7 +138,7 @@ func (c *Command) validateFlags(args []string) error { func (c *Command) checkHelmInstallation(settings *helmCLI.EnvSettings, uiLogger action.DebugLog, releaseName, namespace string) error { // Need a specific action config to call helm status, where namespace comes from the previous call to list. statusConfig := new(action.Configuration) - statusConfig, err := common.InitActionConfig(statusConfig, namespace, settings, uiLogger) + statusConfig, err := helm.InitActionConfig(statusConfig, namespace, settings, uiLogger) if err != nil { return err } diff --git a/cli/cmd/status/status_test.go b/cli/cmd/status/status_test.go index e369555143..79f908654f 100644 --- a/cli/cmd/status/status_test.go +++ b/cli/cmd/status/status_test.go @@ -6,7 +6,7 @@ import ( "os" "testing" - "github.com/hashicorp/consul-k8s/cli/cmd/common" + "github.com/hashicorp/consul-k8s/cli/common" "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" diff --git a/cli/cmd/uninstall/uninstall.go b/cli/cmd/uninstall/uninstall.go index 11d68aab7b..288f97823b 100644 --- a/cli/cmd/uninstall/uninstall.go +++ b/cli/cmd/uninstall/uninstall.go @@ -7,9 +7,10 @@ import ( "time" "github.com/cenkalti/backoff" - "github.com/hashicorp/consul-k8s/cli/cmd/common" - "github.com/hashicorp/consul-k8s/cli/cmd/common/flag" - "github.com/hashicorp/consul-k8s/cli/cmd/common/terminal" + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/consul-k8s/cli/helm" "helm.sh/helm/v3/pkg/action" helmCLI "helm.sh/helm/v3/pkg/cli" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -176,7 +177,7 @@ func (c *Command) Run(args []string) int { // Search for Consul installation by calling `helm list`. Depends on what's already specified. actionConfig := new(action.Configuration) - actionConfig, err = common.InitActionConfig(actionConfig, c.flagNamespace, settings, uiLogger) + actionConfig, err = helm.InitActionConfig(actionConfig, c.flagNamespace, settings, uiLogger) if err != nil { c.UI.Output(err.Error(), terminal.WithErrorStyle()) return 1 @@ -210,7 +211,7 @@ func (c *Command) Run(args []string) int { } // Actually call out to `helm delete`. - actionConfig, err = common.InitActionConfig(actionConfig, foundReleaseNamespace, settings, uiLogger) + actionConfig, err = helm.InitActionConfig(actionConfig, foundReleaseNamespace, settings, uiLogger) if err != nil { c.UI.Output(err.Error(), terminal.WithErrorStyle()) return 1 diff --git a/cli/cmd/uninstall/uninstall_test.go b/cli/cmd/uninstall/uninstall_test.go index faf3a132c1..8f1df8de80 100644 --- a/cli/cmd/uninstall/uninstall_test.go +++ b/cli/cmd/uninstall/uninstall_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "github.com/hashicorp/consul-k8s/cli/cmd/common" + "github.com/hashicorp/consul-k8s/cli/common" "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/require" batchv1 "k8s.io/api/batch/v1" diff --git a/cli/cmd/upgrade/upgrade.go b/cli/cmd/upgrade/upgrade.go index 27771a8ab6..5c9cf80651 100644 --- a/cli/cmd/upgrade/upgrade.go +++ b/cli/cmd/upgrade/upgrade.go @@ -9,18 +9,16 @@ import ( "time" consulChart "github.com/hashicorp/consul-k8s/charts" - "github.com/hashicorp/consul-k8s/cli/cmd/common" - "github.com/hashicorp/consul-k8s/cli/cmd/common/flag" - "github.com/hashicorp/consul-k8s/cli/cmd/common/terminal" - "github.com/hashicorp/consul-k8s/cli/cmd/install" + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" "github.com/hashicorp/consul-k8s/cli/config" + "github.com/hashicorp/consul-k8s/cli/helm" "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart/loader" helmCLI "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/getter" "k8s.io/client-go/kubernetes" - "sigs.k8s.io/yaml" ) const ( @@ -170,11 +168,22 @@ func (c *Command) Run(args []string) int { defer common.CloseWithError(c.BaseCommand) - if err := c.validateFlags(args); err != nil { + err := c.validateFlags(args) + if err != nil { c.UI.Output(err.Error()) return 1 } + if c.flagDryRun { + c.UI.Output("Dry Run Upgrade: No changes will be made to the cluster.") + } + + c.timeoutDuration, err = time.ParseDuration(c.flagTimeout) + if err != nil { + c.UI.Output(fmt.Sprintf("Invalid timeout: %s", err)) + return 1 + } + // helmCLI.New() will create a settings object which is used by the Helm Go SDK calls. settings := helmCLI.New() @@ -188,21 +197,6 @@ func (c *Command) Run(args []string) int { settings.KubeContext = c.flagKubeContext } - // Setup logger to stream Helm library logs - var uiLogger = func(s string, args ...interface{}) { - logMsg := fmt.Sprintf(s, args...) - - if c.flagVerbose { - // Only output all logs when verbose is enabled - c.UI.Output(logMsg, terminal.WithLibraryStyle()) - } else { - // When verbose is not enabled, output all logs except not ready messages for resources - if !strings.Contains(logMsg, "not ready") { - c.UI.Output(logMsg, terminal.WithLibraryStyle()) - } - } - } - // Set up the kubernetes client to use for non Helm SDK calls to the Kubernetes API // The Helm SDK will use settings.RESTClientGetter for its calls as well, so this will // use a consistent method to target the right cluster for both Helm SDK and non Helm SDK calls. @@ -220,51 +214,47 @@ func (c *Command) Run(args []string) int { } c.UI.Output("Pre-Upgrade Checks", terminal.WithHeaderStyle()) - - // Note the logic here, common's CheckForInstallations function returns an error if - // the release is not found. In `upgrade` we should indeed error if a user doesn't currently have a release. - var foundNamespace string - if name, ns, err := common.CheckForInstallations(settings, uiLogger); err != nil { - c.UI.Output("could not find existing Consul installation - run `consul-k8s install`") + uiLogger := c.createUILogger() + name, namespace, err := common.CheckForInstallations(settings, uiLogger) + if err != nil { + c.UI.Output("Could not find existing Consul installation. Run 'consul-k8s install' to create one.") return 1 - } else { - c.UI.Output("Existing installation found to be upgraded.", terminal.WithSuccessStyle()) - c.UI.Output("Name: %s", name, terminal.WithInfoStyle()) - c.UI.Output("Namespace: %s", ns, terminal.WithInfoStyle()) - - foundNamespace = ns } + c.UI.Output("Existing installation found to be upgraded.", terminal.WithSuccessStyle()) + c.UI.Output("Name: %s\nNamespace: %s", name, namespace, terminal.WithInfoStyle()) - // Handle preset, value files, and set values logic. - vals, err := c.mergeValuesFlagsWithPrecedence(settings) + chart, err := helm.LoadChart(consulChart.ConsulHelmChart, common.TopLevelChartDirName) if err != nil { c.UI.Output(err.Error(), terminal.WithErrorStyle()) return 1 } - valuesYaml, err := yaml.Marshal(vals) + c.UI.Output("Loaded charts", terminal.WithSuccessStyle()) + + currentChartValues, err := helm.FetchChartValues(namespace, settings, uiLogger) if err != nil { c.UI.Output(err.Error(), terminal.WithErrorStyle()) return 1 } - // Print out the upgrade summary. - if !c.flagAutoApprove { - c.UI.Output("Consul Upgrade Summary", terminal.WithHeaderStyle()) - c.UI.Output("Installation name: %s", common.DefaultReleaseName, terminal.WithInfoStyle()) - c.UI.Output("Namespace: %s", foundNamespace, terminal.WithInfoStyle()) - - if len(vals) == 0 { - c.UI.Output("Overrides: "+string(valuesYaml), terminal.WithInfoStyle()) - } else { - c.UI.Output("Overrides:"+"\n"+string(valuesYaml), terminal.WithInfoStyle()) - } + // Handle preset, value files, and set values logic. + chartValues, err := c.mergeValuesFlagsWithPrecedence(settings) + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 } // Without informing the user, default global.name to consul if it hasn't been set already. We don't allow setting // the release name, and since that is hardcoded to "consul", setting global.name to "consul" makes it so resources // aren't double prefixed with "consul-consul-...". - vals = install.MergeMaps(config.Convert(config.GlobalNameConsul), vals) + chartValues = common.MergeMaps(config.Convert(config.GlobalNameConsul), chartValues) + // Print out the upgrade summary. + if err = c.printDiff(currentChartValues, chartValues); err != nil { + c.UI.Output("Could not print diff between charts.", terminal.WithErrorStyle()) + return 1 + } + + // Check if the user is OK with the upgrade unless the auto approve or dry run flags are true. if !c.flagAutoApprove && !c.flagDryRun { confirmation, err := c.UI.Input(&terminal.Input{ Prompt: "Proceed with upgrade? (y/N)", @@ -290,7 +280,7 @@ func (c *Command) Run(args []string) int { // Setup action configuration for Helm Go SDK function calls. actionConfig := new(action.Configuration) - actionConfig, err = common.InitActionConfig(actionConfig, foundNamespace, settings, uiLogger) + actionConfig, err = helm.InitActionConfig(actionConfig, namespace, settings, uiLogger) if err != nil { c.UI.Output(err.Error(), terminal.WithErrorStyle()) return 1 @@ -298,28 +288,13 @@ func (c *Command) Run(args []string) int { // Setup the upgrade action. upgrade := action.NewUpgrade(actionConfig) - upgrade.Namespace = foundNamespace + upgrade.Namespace = namespace upgrade.DryRun = c.flagDryRun upgrade.Wait = c.flagWait upgrade.Timeout = c.timeoutDuration - // Read the embedded chart files into []*loader.BufferedFile. - chartFiles, err := common.ReadChartFiles(consulChart.ConsulHelmChart, common.TopLevelChartDirName) - if err != nil { - c.UI.Output(err.Error(), terminal.WithErrorStyle()) - return 1 - } - - // Create a *chart.Chart object from the files to run the upgrade from. - chart, err := loader.LoadFiles(chartFiles) - if err != nil { - c.UI.Output(err.Error(), terminal.WithErrorStyle()) - return 1 - } - c.UI.Output("Loaded charts", terminal.WithSuccessStyle()) - // Run the upgrade. Note that the dry run config is passed into the upgrade action, so upgrade.Run is called even during a dry run. - re, err := upgrade.Run(common.DefaultReleaseName, chart, vals) + _, err = upgrade.Run(common.DefaultReleaseName, chart, chartValues) if err != nil { c.UI.Output(err.Error(), terminal.WithErrorStyle()) return 1 @@ -327,23 +302,11 @@ func (c *Command) Run(args []string) int { // Dry Run should exit here, printing the release's config. if c.flagDryRun { - configYaml, err := yaml.Marshal(re.Config) - if err != nil { - c.UI.Output(err.Error(), terminal.WithErrorStyle()) - return 1 - } - - if len(re.Config) == 0 { - c.UI.Output("Config: "+string(configYaml), terminal.WithInfoStyle()) - } else { - c.UI.Output("Config:"+"\n"+string(configYaml), terminal.WithInfoStyle()) - } - c.UI.Output("Dry run complete - upgrade can proceed.", terminal.WithSuccessStyle()) + c.UI.Output("Dry run complete - upgrade can proceed.", terminal.WithInfoStyle()) return 0 } - c.UI.Output("Upgraded Consul into namespace %q", foundNamespace, terminal.WithSuccessStyle()) - + c.UI.Output("Upgraded Consul into namespace %q", namespace, terminal.WithSuccessStyle()) return 0 } @@ -361,11 +324,9 @@ func (c *Command) validateFlags(args []string) error { if _, ok := config.Presets[c.flagPreset]; c.flagPreset != defaultPreset && !ok { return fmt.Errorf("'%s' is not a valid preset", c.flagPreset) } - duration, err := time.ParseDuration(c.flagTimeout) - if err != nil { + if _, err := time.ParseDuration(c.flagTimeout); err != nil { return fmt.Errorf("unable to parse -%s: %s", flagNameTimeout, err) } - c.timeoutDuration = duration if len(c.flagValueFiles) != 0 { for _, filename := range c.flagValueFiles { if _, err := os.Stat(filename); err != nil && os.IsNotExist(err) { @@ -374,9 +335,6 @@ func (c *Command) validateFlags(args []string) error { } } - if c.flagDryRun { - c.UI.Output("Performing dry run upgrade.", terminal.WithInfoStyle()) - } return nil } @@ -404,17 +362,56 @@ func (c *Command) mergeValuesFlagsWithPrecedence(settings *helmCLI.EnvSettings) if c.flagPreset != defaultPreset { // Note the ordering of the function call, presets have lower precedence than set vals. presetMap := config.Presets[c.flagPreset].(map[string]interface{}) - vals = install.MergeMaps(presetMap, vals) + vals = common.MergeMaps(presetMap, vals) } return vals, err } +// Help returns a description of this command and how it can be used. func (c *Command) Help() string { c.once.Do(c.init) - s := "Usage: consul-k8s upgrade [flags]" + "\n" + "Upgrade Consul from an existing installation." + "\n" - return s + "\n" + c.help + return fmt.Sprintf("Usage: consul-k8s upgrade [flags]\n%s\n\n%s", c.Synopsis(), c.help) } +// Synopsis returns a short string describing the command. func (c *Command) Synopsis() string { - return "Upgrade Consul on Kubernetes." + return "Upgrade Consul on Kubernetes from an existing installation." +} + +// createUILogger creates a logger that will write to the UI. +func (c *Command) createUILogger() func(string, ...interface{}) { + return func(s string, args ...interface{}) { + logMsg := fmt.Sprintf(s, args...) + + if c.flagVerbose { + // Only output all logs when verbose is enabled + c.UI.Output(logMsg, terminal.WithLibraryStyle()) + } else { + // When verbose is not enabled, output all logs except not ready messages for resources + if !strings.Contains(logMsg, "not ready") { + c.UI.Output(logMsg, terminal.WithLibraryStyle()) + } + } + } +} + +// printDiff marshals both maps to YAML and prints the diff between the two. +func (c *Command) printDiff(old, new map[string]interface{}) error { + diff, err := common.Diff(old, new) + if err != nil { + return err + } + + c.UI.Output("Diff between user overrides", terminal.WithHeaderStyle()) + for _, line := range strings.Split(diff, "\n") { + if strings.HasPrefix(line, "+") { + c.UI.Output(line, terminal.WithDiffAddedStyle()) + } else if strings.HasPrefix(line, "-") { + c.UI.Output(line, terminal.WithDiffRemovedStyle()) + } else { + c.UI.Output(line, terminal.WithDiffUnchangedStyle()) + } + } + + return nil } diff --git a/cli/cmd/upgrade/upgrade_test.go b/cli/cmd/upgrade/upgrade_test.go index ab6e830259..f70496bda6 100644 --- a/cli/cmd/upgrade/upgrade_test.go +++ b/cli/cmd/upgrade/upgrade_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - "github.com/hashicorp/consul-k8s/cli/cmd/common" + "github.com/hashicorp/consul-k8s/cli/common" "github.com/hashicorp/go-hclog" ) diff --git a/cli/cmd/version/version.go b/cli/cmd/version/version.go index 417baaff4a..d565309b5a 100644 --- a/cli/cmd/version/version.go +++ b/cli/cmd/version/version.go @@ -4,7 +4,7 @@ import ( "fmt" "sync" - "github.com/hashicorp/consul-k8s/cli/cmd/common" + "github.com/hashicorp/consul-k8s/cli/common" ) type Command struct { diff --git a/cli/commands.go b/cli/commands.go index 17726ac5aa..681421c502 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -3,12 +3,12 @@ package main import ( "context" - "github.com/hashicorp/consul-k8s/cli/cmd/common" "github.com/hashicorp/consul-k8s/cli/cmd/install" "github.com/hashicorp/consul-k8s/cli/cmd/status" "github.com/hashicorp/consul-k8s/cli/cmd/uninstall" "github.com/hashicorp/consul-k8s/cli/cmd/upgrade" cmdversion "github.com/hashicorp/consul-k8s/cli/cmd/version" + "github.com/hashicorp/consul-k8s/cli/common" "github.com/hashicorp/consul-k8s/cli/version" "github.com/hashicorp/go-hclog" "github.com/mitchellh/cli" diff --git a/cli/cmd/common/base.go b/cli/common/base.go similarity index 94% rename from cli/cmd/common/base.go rename to cli/common/base.go index 60f0096850..81bb360267 100644 --- a/cli/cmd/common/base.go +++ b/cli/common/base.go @@ -4,7 +4,7 @@ import ( "context" "io" - "github.com/hashicorp/consul-k8s/cli/cmd/common/terminal" + "github.com/hashicorp/consul-k8s/cli/common/terminal" "github.com/hashicorp/go-hclog" ) diff --git a/cli/common/diff.go b/cli/common/diff.go new file mode 100644 index 0000000000..30d03e2968 --- /dev/null +++ b/cli/common/diff.go @@ -0,0 +1,148 @@ +package common + +import ( + "sort" + "strings" + + "github.com/google/go-cmp/cmp" + "sigs.k8s.io/yaml" +) + +// Diff returns a string representation of the difference between two maps as YAML. +// The returned string is sorted alphabetically by key. If the maps are identical, the returned string is empty. +func Diff(a, b map[string]interface{}) (string, error) { + if len(a) == 0 && len(b) == 0 { + return "", nil + } + + return diffRecursively(a, b, 0) +} + +// diffRecursively iterates over maps `a` and `b` and returns a string representation of the difference between them as YAML. +// The returned string is sorted alphabetically by key with `+` and `-` prefixed to "diffed" lines. +// `c` is a map of the default values for the chart. If a key is present in `a`, but not `b` (i.e. removed), +// the value is compared with the default value in `c` to prevent a false positive "removed" line. +func diffRecursively(a, b map[string]interface{}, recurseDepth int) (string, error) { + buf := new(strings.Builder) + + // Get the union of keys in a and b sorted alphabetically. + keys := collectKeys(a, b) + + for _, key := range keys { + valueInA, inA := a[key] + valueInB, inB := b[key] + + aMapWithKey := map[string]interface{}{ + key: valueInA, + } + bMapWithKey := map[string]interface{}{ + key: valueInB, + } + + // If the key is in both a and b, compare the values. + if inA && inB { + // If the map slices are the same, write as unchanged YAML. + if cmp.Equal(aMapWithKey, bMapWithKey) { + asYaml, err := yaml.Marshal(aMapWithKey) + if err != nil { + return "", err + } + + writeWithPrepend(" ", string(asYaml), recurseDepth, buf) + continue + } + + // If the maps are different and there is another level of depth to the map, recurse. + if !isMaxDepth(aMapWithKey) && !isMaxDepth(bMapWithKey) { + writeWithPrepend(" ", key+":", recurseDepth, buf) + + childDiff, err := diffRecursively(valueInA.(map[string]interface{}), valueInB.(map[string]interface{}), recurseDepth+1) + if err != nil { + return "", err + } + + buf.WriteString(childDiff) + + continue + } + + // If the map slices are different and there is no other level of depth to the map, write as changed YAML. + aSliceAsYaml, err := yaml.Marshal(aMapWithKey) + if err != nil { + return "", err + } + + bSliceAsYaml, err := yaml.Marshal(bMapWithKey) + if err != nil { + return "", err + } + + writeWithPrepend("- ", string(aSliceAsYaml), recurseDepth, buf) + writeWithPrepend("+ ", string(bSliceAsYaml), recurseDepth, buf) + } + + // If the key is in `a` but not in `b`, write as removed unless `a` matches the value in `c`. + if inA && !inB { + asYaml, err := yaml.Marshal(aMapWithKey) + if err != nil { + return "", err + } + + writeWithPrepend("- ", string(asYaml), recurseDepth, buf) + continue + } + + // If the key is in b but not in a, write as added. + if !inA && inB { + asYaml, err := yaml.Marshal(bMapWithKey) + if err != nil { + return "", err + } + + writeWithPrepend("+ ", string(asYaml), recurseDepth, buf) + continue + } + } + + return buf.String(), nil +} + +// collectKeys iterates over both maps and collects all keys sorted alphabetically, ignoring duplicates. +func collectKeys(a, b map[string]interface{}) []string { + keys := make([]string, 0, len(a)+len(b)) + for key := range a { + keys = append(keys, key) + } + for key := range b { + if _, ok := a[key]; !ok { + keys = append(keys, key) + } + } + + sort.Strings(keys) + return keys +} + +// writeWithPrepend writes each line to the buffer with the given prefix and indentation matching the recurse depth. +func writeWithPrepend(prepend, text string, recurseDepth int, buf *strings.Builder) { + lines := strings.Split(strings.TrimSpace(text), "\n") + for _, line := range lines { + buf.WriteString(prepend) + for i := 0; i < recurseDepth; i++ { + buf.WriteString(" ") + } + buf.WriteString(line) + buf.WriteString("\n") + } +} + +// isMaxDepth returns false if any of the values in the map are maps. +func isMaxDepth(m map[string]interface{}) bool { + for _, value := range m { + if _, ok := value.(map[string]interface{}); ok { + return false + } + } + + return true +} diff --git a/cli/common/diff_test.go b/cli/common/diff_test.go new file mode 100644 index 0000000000..31563e0b6b --- /dev/null +++ b/cli/common/diff_test.go @@ -0,0 +1,171 @@ +package common + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDiff(t *testing.T) { + type args struct { + a map[string]interface{} + b map[string]interface{} + } + + tests := []struct { + name string + args args + expected string + }{ + { + name: "Two empty maps should return an empty string", + args: args{ + a: map[string]interface{}{}, + b: map[string]interface{}{}, + }, + expected: "", + }, + { + name: "Two equal maps should return the map parsed as YAML", + args: args{ + a: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + "liz": map[string]interface{}{"qux": []string{"quux", "quuz"}}, + }, + b: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + "liz": map[string]interface{}{"qux": []string{"quux", "quuz"}}, + }, + }, + expected: " baz: qux\n foo: bar\n liz:\n qux:\n - quux\n - quuz\n", + }, + { + name: "New elements should be prefixed with a plus sign", + args: args{ + a: map[string]interface{}{ + "foo": "bar", + }, + b: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }, + }, + expected: "+ baz: qux\n foo: bar\n", + }, + { + name: "New non-string elements should be prefixed with a plus sign", + args: args{ + a: map[string]interface{}{ + "foo": "bar", + }, + b: map[string]interface{}{ + "foo": "bar", + "baz": []string{"qux"}, + "qux": map[string]string{ + "quux": "corge", + }, + }, + }, + expected: "+ baz:\n+ - qux\n foo: bar\n+ qux:\n+ quux: corge\n", + }, + { + name: "Deleted elements should be prefixed with a minus sign", + args: args{ + a: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }, + b: map[string]interface{}{ + "foo": "bar", + }, + }, + expected: "- baz: qux\n foo: bar\n", + }, + { + name: "Deleted non-string elements should be prefixed with a minus sign", + args: args{ + a: map[string]interface{}{ + "foo": "bar", + "baz": []string{"qux"}, + "qux": map[string]string{ + "quux": "corge", + }, + }, + b: map[string]interface{}{ + "foo": "bar", + }, + }, + expected: "- baz:\n- - qux\n foo: bar\n- qux:\n- quux: corge\n", + }, + { + name: "Diff between two complex maps should be correct", + args: args{ + a: map[string]interface{}{ + "global": map[string]interface{}{ + "name": "consul", + "metrics": map[string]interface{}{ + "enabled": true, + "enableAgentMetrics": true, + }, + }, + "connectInject": map[string]interface{}{ + "enabled": true, + "metrics": map[string]interface{}{ + "defaultEnabled": true, + "defaultEnableMerging": true, + "enableGatewayMetrics": true, + }, + }, + "server": map[string]interface{}{ + "replicas": 1, + }, + "controller": map[string]interface{}{ + "enabled": true, + }, + "ui": map[string]interface{}{ + "enabled": true, + "service": map[string]interface{}{ + "enabled": true, + }, + }, + "prometheus": map[string]interface{}{ + "enabled": true, + }, + }, + b: map[string]interface{}{ + "global": map[string]interface{}{ + "name": "consul", + "gossipEncryption": map[string]interface{}{ + "autoGenerate": true, + }, + "tls": map[string]interface{}{ + "enabled": true, + "enableAutoEncrypt": true, + }, + "acls": map[string]interface{}{ + "manageSystemACLs": true, + }, + }, + "server": map[string]interface{}{"replicas": 1}, + "connectInject": map[string]interface{}{ + "enabled": true, + }, + "controller": map[string]interface{}{ + "enabled": true, + }, + }, + }, + expected: " connectInject:\n enabled: true\n- metrics:\n- defaultEnableMerging: true\n- defaultEnabled: true\n- enableGatewayMetrics: true\n controller:\n enabled: true\n global:\n+ acls:\n+ manageSystemACLs: true\n+ gossipEncryption:\n+ autoGenerate: true\n- metrics:\n- enableAgentMetrics: true\n- enabled: true\n name: consul\n+ tls:\n+ enableAutoEncrypt: true\n+ enabled: true\n- prometheus:\n- enabled: true\n server:\n replicas: 1\n- ui:\n- enabled: true\n- service:\n- enabled: true\n", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual, err := Diff(tt.args.a, tt.args.b) + require.NoError(t, err) + require.Equal(t, tt.expected, actual) + }) + } +} diff --git a/cli/cmd/common/flag/doc.go b/cli/common/flag/doc.go similarity index 100% rename from cli/cmd/common/flag/doc.go rename to cli/common/flag/doc.go diff --git a/cli/cmd/common/flag/flag.go b/cli/common/flag/flag.go similarity index 100% rename from cli/cmd/common/flag/flag.go rename to cli/common/flag/flag.go diff --git a/cli/cmd/common/flag/flag_bool.go b/cli/common/flag/flag_bool.go similarity index 100% rename from cli/cmd/common/flag/flag_bool.go rename to cli/common/flag/flag_bool.go diff --git a/cli/cmd/common/flag/flag_enum.go b/cli/common/flag/flag_enum.go similarity index 100% rename from cli/cmd/common/flag/flag_enum.go rename to cli/common/flag/flag_enum.go diff --git a/cli/cmd/common/flag/flag_enum_single.go b/cli/common/flag/flag_enum_single.go similarity index 100% rename from cli/cmd/common/flag/flag_enum_single.go rename to cli/common/flag/flag_enum_single.go diff --git a/cli/cmd/common/flag/flag_float.go b/cli/common/flag/flag_float.go similarity index 100% rename from cli/cmd/common/flag/flag_float.go rename to cli/common/flag/flag_float.go diff --git a/cli/cmd/common/flag/flag_int.go b/cli/common/flag/flag_int.go similarity index 100% rename from cli/cmd/common/flag/flag_int.go rename to cli/common/flag/flag_int.go diff --git a/cli/cmd/common/flag/flag_string.go b/cli/common/flag/flag_string.go similarity index 100% rename from cli/cmd/common/flag/flag_string.go rename to cli/common/flag/flag_string.go diff --git a/cli/cmd/common/flag/flag_string_map.go b/cli/common/flag/flag_string_map.go similarity index 100% rename from cli/cmd/common/flag/flag_string_map.go rename to cli/common/flag/flag_string_map.go diff --git a/cli/cmd/common/flag/flag_string_slice.go b/cli/common/flag/flag_string_slice.go similarity index 100% rename from cli/cmd/common/flag/flag_string_slice.go rename to cli/common/flag/flag_string_slice.go diff --git a/cli/cmd/common/flag/flag_string_slice_test.go b/cli/common/flag/flag_string_slice_test.go similarity index 100% rename from cli/cmd/common/flag/flag_string_slice_test.go rename to cli/common/flag/flag_string_slice_test.go diff --git a/cli/cmd/common/flag/flag_time.go b/cli/common/flag/flag_time.go similarity index 100% rename from cli/cmd/common/flag/flag_time.go rename to cli/common/flag/flag_time.go diff --git a/cli/cmd/common/flag/flag_var.go b/cli/common/flag/flag_var.go similarity index 100% rename from cli/cmd/common/flag/flag_var.go rename to cli/common/flag/flag_var.go diff --git a/cli/cmd/common/flag/set.go b/cli/common/flag/set.go similarity index 100% rename from cli/cmd/common/flag/set.go rename to cli/common/flag/set.go diff --git a/cli/cmd/common/flag/set_test.go b/cli/common/flag/set_test.go similarity index 100% rename from cli/cmd/common/flag/set_test.go rename to cli/common/flag/set_test.go diff --git a/cli/cmd/common/terminal/basic.go b/cli/common/terminal/basic.go similarity index 94% rename from cli/cmd/common/terminal/basic.go rename to cli/common/terminal/basic.go index 37562d7598..2425b2d53c 100644 --- a/cli/cmd/common/terminal/basic.go +++ b/cli/common/terminal/basic.go @@ -93,6 +93,12 @@ func (ui *basicUI) Output(msg string, raw ...interface{}) { msg = colorSuccessBold.Sprintf(" ✓ %s", msg) case LibraryStyle: msg = colorLibrary.Sprintf(" --> %s", msg) + case DiffUnchangedStyle: + msg = colorDiffUnchanged.Sprintf(" %s", msg) + case DiffAddedStyle: + msg = colorDiffAdded.Sprintf(" %s", msg) + case DiffRemovedStyle: + msg = colorDiffRemoved.Sprintf(" %s", msg) case InfoStyle: lines := strings.Split(msg, "\n") for i, line := range lines { diff --git a/cli/cmd/common/terminal/doc.go b/cli/common/terminal/doc.go similarity index 100% rename from cli/cmd/common/terminal/doc.go rename to cli/common/terminal/doc.go diff --git a/cli/cmd/common/terminal/table.go b/cli/common/terminal/table.go similarity index 100% rename from cli/cmd/common/terminal/table.go rename to cli/common/terminal/table.go diff --git a/cli/cmd/common/terminal/ui.go b/cli/common/terminal/ui.go similarity index 72% rename from cli/cmd/common/terminal/ui.go rename to cli/common/terminal/ui.go index 38922d6399..e7d938a408 100644 --- a/cli/cmd/common/terminal/ui.go +++ b/cli/common/terminal/ui.go @@ -92,15 +92,18 @@ func Interpret(msg string, raw ...interface{}) (string, string, io.Writer) { } const ( - HeaderStyle = "header" - ErrorStyle = "error" - ErrorBoldStyle = "error-bold" - WarningStyle = "warning" - WarningBoldStyle = "warning-bold" - InfoStyle = "info" - LibraryStyle = "library" - SuccessStyle = "success" - SuccessBoldStyle = "success-bold" + HeaderStyle = "header" + ErrorStyle = "error" + ErrorBoldStyle = "error-bold" + WarningStyle = "warning" + WarningBoldStyle = "warning-bold" + InfoStyle = "info" + LibraryStyle = "library" + SuccessStyle = "success" + SuccessBoldStyle = "success-bold" + DiffUnchangedStyle = "diff-unchanged" + DiffAddedStyle = "diff-added" + DiffRemovedStyle = "diff-removed" ) type config struct { @@ -137,7 +140,7 @@ func WithErrorStyle() Option { } } -// WithWarningStyle styles the output as an error message. +// WithWarningStyle styles the output as an warning message. func WithWarningStyle() Option { return func(c *config) { c.Style = WarningStyle @@ -151,13 +154,35 @@ func WithSuccessStyle() Option { } } -// WithLibraryStyle styles the output as a success message. +// WithLibraryStyle styles the output with an arrow pointing to a section. func WithLibraryStyle() Option { return func(c *config) { c.Style = LibraryStyle } } +// WithDiffUnchangedStyle colors the diff style in white. +func WithDiffUnchangedStyle() Option { + return func(c *config) { + c.Style = DiffUnchangedStyle + } +} + +// WithDiffAddedStyle colors the output in green. +func WithDiffAddedStyle() Option { + return func(c *config) { + c.Style = DiffAddedStyle + } +} + +// WithDiffRemovedStyle colors the output in red. +func WithDiffRemovedStyle() Option { + return func(c *config) { + c.Style = DiffRemovedStyle + } +} + +// WithStyle allows for setting a style by passing a string. func WithStyle(style string) Option { return func(c *config) { c.Style = style @@ -170,13 +195,16 @@ func WithWriter(w io.Writer) Option { } var ( - colorHeader = color.New(color.Bold) - colorInfo = color.New() - colorError = color.New(color.FgRed) - colorErrorBold = color.New(color.FgRed, color.Bold) - colorLibrary = color.New(color.FgCyan) - colorSuccess = color.New(color.FgGreen) - colorSuccessBold = color.New(color.FgGreen, color.Bold) - colorWarning = color.New(color.FgYellow) - colorWarningBold = color.New(color.FgYellow, color.Bold) + colorHeader = color.New(color.Bold) + colorInfo = color.New() + colorError = color.New(color.FgRed) + colorErrorBold = color.New(color.FgRed, color.Bold) + colorLibrary = color.New(color.FgCyan) + colorSuccess = color.New(color.FgGreen) + colorSuccessBold = color.New(color.FgGreen, color.Bold) + colorWarning = color.New(color.FgYellow) + colorWarningBold = color.New(color.FgYellow, color.Bold) + colorDiffUnchanged = color.New() + colorDiffAdded = color.New(color.FgGreen) + colorDiffRemoved = color.New(color.FgRed) ) diff --git a/cli/cmd/common/usage.go b/cli/common/usage.go similarity index 100% rename from cli/cmd/common/usage.go rename to cli/common/usage.go diff --git a/cli/common/utils.go b/cli/common/utils.go new file mode 100644 index 0000000000..3d7df71433 --- /dev/null +++ b/cli/common/utils.go @@ -0,0 +1,108 @@ +package common + +import ( + "errors" + "fmt" + "os" + "strings" + + "helm.sh/helm/v3/pkg/action" + helmCLI "helm.sh/helm/v3/pkg/cli" +) + +const ( + DefaultReleaseName = "consul" + DefaultReleaseNamespace = "consul" + TopLevelChartDirName = "consul" + + // CLILabelKey and CLILabelValue are added to each secret on creation so the CLI knows + // which key to delete on an uninstall. + CLILabelKey = "managed-by" + CLILabelValue = "consul-k8s" +) + +// Abort returns true if the raw input string is not equal to "y" or "yes". +func Abort(raw string) bool { + confirmation := strings.TrimSuffix(raw, "\n") + return !(strings.ToLower(confirmation) == "y" || strings.ToLower(confirmation) == "yes") +} + +// CheckForInstallations uses the helm Go SDK to find helm releases in all namespaces where the chart name is +// "consul", and returns the release name and namespace if found, or an error if not found. +func CheckForInstallations(settings *helmCLI.EnvSettings, uiLogger action.DebugLog) (string, string, error) { + // Need a specific action config to call helm list, where namespace is NOT specified. + listConfig := new(action.Configuration) + if err := listConfig.Init(settings.RESTClientGetter(), "", + os.Getenv("HELM_DRIVER"), uiLogger); err != nil { + return "", "", fmt.Errorf("couldn't initialize helm config: %s", err) + } + + lister := action.NewList(listConfig) + lister.AllNamespaces = true + lister.StateMask = action.ListAll + res, err := lister.Run() + if err != nil { + return "", "", fmt.Errorf("couldn't check for installations: %s", err) + } + + for _, rel := range res { + if rel.Chart.Metadata.Name == "consul" { + return rel.Name, rel.Namespace, nil + } + } + return "", "", errors.New("couldn't find consul installation") +} + +// MergeMaps merges two maps giving b precedent. +// @source: https://github.com/helm/helm/blob/main/pkg/cli/values/options.go +func MergeMaps(a, b map[string]interface{}) map[string]interface{} { + out := make(map[string]interface{}, len(a)) + for k, v := range a { + out[k] = v + } + for k, v := range b { + if v, ok := v.(map[string]interface{}); ok { + if bv, ok := out[k]; ok { + if bv, ok := bv.(map[string]interface{}); ok { + out[k] = MergeMaps(bv, v) + continue + } + } + } + out[k] = v + } + return out +} + +func CloseWithError(c *BaseCommand) { + if err := c.Close(); err != nil { + c.Log.Error(err.Error()) + os.Exit(1) + } +} + +// IsValidLabel checks if a given label conforms to RFC 1123 https://datatracker.ietf.org/doc/html/rfc1123. +// This standard requires that the label begins and ends with an alphanumeric character, does not exceed 63 characters, +// and contains only alphanumeric characters and '-'. +func IsValidLabel(label string) bool { + if len(label) > 63 || len(label) == 0 { + return false + } + + for i, c := range label { + isAlphaNumeric := c >= '0' && c <= '9' || c >= 'a' && c <= 'z' + isTerminal := i == 0 || i == len(label)-1 + + // First and last character must be alphanumeric. + if isTerminal && !isAlphaNumeric { + return false + } + + // All other characters must be alphanumeric or '-'. + if !isAlphaNumeric && c != '-' { + return false + } + } + + return true +} diff --git a/cli/common/utils_test.go b/cli/common/utils_test.go new file mode 100644 index 0000000000..c15397e4a3 --- /dev/null +++ b/cli/common/utils_test.go @@ -0,0 +1,67 @@ +package common + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMergeMaps(t *testing.T) { + cases := map[string]struct { + a map[string]interface{} + b map[string]interface{} + expected map[string]interface{} + }{ + "a is empty": { + a: map[string]interface{}{}, + b: map[string]interface{}{"foo": "bar"}, + expected: map[string]interface{}{"foo": "bar"}, + }, + "b is empty": { + a: map[string]interface{}{"foo": "bar"}, + b: map[string]interface{}{}, + expected: map[string]interface{}{"foo": "bar"}, + }, + "b overrides a": { + a: map[string]interface{}{"foo": "bar"}, + b: map[string]interface{}{"foo": "baz"}, + expected: map[string]interface{}{"foo": "baz"}, + }, + "b partially overrides a": { + a: map[string]interface{}{"foo": "bar", "baz": "qux"}, + b: map[string]interface{}{"foo": "baz"}, + expected: map[string]interface{}{"foo": "baz", "baz": "qux"}, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + actual := MergeMaps(tc.a, tc.b) + require.Equal(t, tc.expected, actual) + }) + } +} + +func TestIsValidLabel(t *testing.T) { + cases := []struct { + name string + label string + expected bool + }{ + {"Valid label", "such-a-good-label", true}, + {"Valid label with leading numbers", "123-such-a-good-label", true}, + {"Invalid label empty", "", false}, + {"Invalid label contains capital letters", "Peppertrout", false}, + {"Invalid label contains underscores", "this_is_not_python", false}, + {"Invalid label too long", "a-very-very-very-long-label-that-is-more-than-63-characters-long", false}, + {"Invalid label starts with a dash", "-invalid-label", false}, + {"Invalid label ends with a dash", "invalid-label-", false}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + actual := IsValidLabel(tc.label) + require.Equal(t, tc.expected, actual) + }) + } +} diff --git a/cli/go.mod b/cli/go.mod index 5bab90cae6..fa8cdf886a 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -6,6 +6,7 @@ require ( github.com/bgentry/speakeasy v0.1.0 github.com/cenkalti/backoff v2.2.1+incompatible github.com/fatih/color v1.9.0 + github.com/google/go-cmp v0.5.5 github.com/hashicorp/consul-k8s/charts v0.0.0-00010101000000-000000000000 github.com/hashicorp/go-hclog v0.16.2 github.com/kr/text v0.2.0 @@ -15,21 +16,21 @@ require ( github.com/posener/complete v1.1.1 github.com/stretchr/testify v1.7.0 helm.sh/helm/v3 v3.6.1 - k8s.io/api v0.21.2 - k8s.io/apimachinery v0.21.2 + k8s.io/api v0.22.2 + k8s.io/apimachinery v0.22.2 k8s.io/cli-runtime v0.21.0 - k8s.io/client-go v0.21.2 + k8s.io/client-go v0.22.2 sigs.k8s.io/yaml v1.2.0 ) require ( cloud.google.com/go v0.54.0 // indirect - github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.12 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.5 // indirect + github.com/Azure/go-autorest/autorest v0.11.18 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/logger v0.2.0 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/BurntSushi/toml v0.3.1 // indirect github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect @@ -60,41 +61,40 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 // indirect github.com/docker/go-units v0.4.0 // indirect - github.com/evanphx/json-patch v4.9.0+incompatible // indirect + github.com/evanphx/json-patch v4.11.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect - github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect + github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/go-openapi/jsonpointer v0.19.3 // indirect github.com/go-openapi/jsonreference v0.19.3 // indirect github.com/go-openapi/spec v0.19.5 // indirect - github.com/go-openapi/swag v0.19.5 // indirect + github.com/go-openapi/swag v0.19.14 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/btree v1.0.0 // indirect - github.com/google/go-cmp v0.5.5 // indirect + github.com/google/btree v1.0.1 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.1.2 // indirect - github.com/googleapis/gnostic v0.4.1 // indirect + github.com/googleapis/gnostic v0.5.5 // indirect github.com/gorilla/mux v1.7.3 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.0 // indirect - github.com/hashicorp/golang-lru v0.5.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.11 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmoiron/sqlx v1.3.1 // indirect - github.com/json-iterator/go v1.1.10 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.11 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lib/pq v1.10.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mailru/easyjson v0.7.0 // indirect + github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-runewidth v0.0.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect @@ -102,7 +102,7 @@ require ( github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect + github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect @@ -113,10 +113,10 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.7.1 // indirect + github.com/prometheus/client_golang v1.11.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.10.0 // indirect - github.com/prometheus/procfs v0.2.0 // indirect + github.com/prometheus/common v0.26.0 // indirect + github.com/prometheus/procfs v0.6.0 // indirect github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 // indirect github.com/russross/blackfriday v1.5.2 // indirect github.com/shopspring/decimal v1.2.0 // indirect @@ -137,7 +137,7 @@ require ( golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/text v0.3.6 // indirect - golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/appengine v1.6.5 // indirect google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect google.golang.org/grpc v1.33.1 // indirect @@ -145,18 +145,18 @@ require ( gopkg.in/gorp.v1 v1.7.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/apiextensions-apiserver v0.21.0 // indirect k8s.io/apiserver v0.21.0 // indirect k8s.io/component-base v0.21.0 // indirect - k8s.io/klog/v2 v2.8.0 // indirect - k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 // indirect + k8s.io/klog/v2 v2.9.0 // indirect + k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect k8s.io/kubectl v0.21.0 // indirect - k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect + k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect rsc.io/letsencrypt v0.0.3 // indirect sigs.k8s.io/kustomize/api v0.8.5 // indirect sigs.k8s.io/kustomize/kyaml v0.10.15 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.1.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect ) // This replace directive is to avoid having to manually bump the version of the charts module upon changes to the Helm diff --git a/cli/go.sum b/cli/go.sum index e80ec5eba6..d6ada483c0 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -25,21 +25,26 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.12 h1:gI8ytXbxMfI+IVbI9mP2JGCTXIuhHLgRlvQ9X4PsnHE= github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0= +github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= @@ -88,6 +93,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -223,19 +229,22 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= @@ -252,6 +261,7 @@ github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -299,8 +309,9 @@ github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= @@ -337,8 +348,9 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -363,8 +375,9 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -392,8 +405,10 @@ github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 h1:893HsJqtxp9z1SF76gg6hY70hRY1wVlTSnC/h1yUDCo= @@ -436,14 +451,12 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= @@ -464,15 +477,20 @@ github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXL github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -507,8 +525,9 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -557,8 +576,9 @@ github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY7 github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 h1:yH0SvLzcbZxcJXho2yh7CqdENGMQe73Cw3woZBpPli0= +github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -572,6 +592,7 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -583,6 +604,8 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -594,12 +617,16 @@ github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FW github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -647,8 +674,9 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -663,8 +691,9 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -672,8 +701,9 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -732,6 +762,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -885,9 +916,12 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -929,6 +963,7 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -936,6 +971,7 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -949,6 +985,7 @@ golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -959,8 +996,8 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -979,8 +1016,9 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1073,6 +1111,7 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1113,7 +1152,6 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= @@ -1137,8 +1175,10 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= @@ -1154,20 +1194,20 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU= -k8s.io/api v0.21.2 h1:vz7DqmRsXTCSa6pNxXwQ1IYeAZgdIsua+DZU+o+SX3Y= -k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU= +k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= +k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= k8s.io/apiextensions-apiserver v0.21.0 h1:Nd4uBuweg6ImzbxkC1W7xUNZcCV/8Vt10iTdTIVF3hw= k8s.io/apiextensions-apiserver v0.21.0/go.mod h1:gsQGNtGkc/YoDG9loKI0V+oLZM4ljRPjc/sql5tmvzc= k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= -k8s.io/apimachinery v0.21.2 h1:vezUc/BHqWlQDnZ+XkrpXSmnANSLbpnlpwo0Lhk0gpc= -k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM= +k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= +k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apiserver v0.21.0 h1:1hWMfsz+cXxB77k6/y0XxWxwl6l9OF26PC9QneUVn1Q= k8s.io/apiserver v0.21.0/go.mod h1:w2YSn4/WIwYuxG5zJmcqtRdtqgW/J2JRgFAqps3bBpg= k8s.io/cli-runtime v0.21.0 h1:/V2Kkxtf6x5NI2z+Sd/mIrq4FQyQ8jzZAUD6N5RnN7Y= k8s.io/cli-runtime v0.21.0/go.mod h1:XoaHP93mGPF37MkLbjGVYqg3S1MnsFdKtiA/RZzzxOo= k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA= -k8s.io/client-go v0.21.2 h1:Q1j4L/iMN4pTw6Y4DWppBoUxgKO8LbffEMVEV00MUp0= -k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA= +k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= +k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= k8s.io/code-generator v0.21.0/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q= k8s.io/component-base v0.21.0 h1:tLLGp4BBjQaCpS/KiuWh7m2xqvAdsxLm4ATxHSe5Zpg= k8s.io/component-base v0.21.0/go.mod h1:qvtjz6X0USWXbgmbfXR+Agik4RZ3jv2Bgr5QnZzdPYw= @@ -1176,15 +1216,18 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8 k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0= +k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= +k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kubectl v0.21.0 h1:WZXlnG/yjcE4LWO2g6ULjFxtzK6H1TKzsfaBFuVIhNg= k8s.io/kubectl v0.21.0/go.mod h1:EU37NukZRXn1TpAkMUoy8Z/B2u6wjHDS4aInsDzVvks= k8s.io/metrics v0.21.0/go.mod h1:L3Ji9EGPP1YBbfm9sPfEXSpnj8i24bfQbAFAsW0NueQ= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g= +k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/letsencrypt v0.0.3 h1:H7xDfhkaFFSYEJlKeq38RwX2jYcnTeHuDQyT+mMNMwM= rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= @@ -1198,8 +1241,9 @@ sigs.k8s.io/kustomize/kustomize/v4 v4.0.5/go.mod h1:C7rYla7sI8EnxHE/xEhRBSHMNfcL sigs.k8s.io/kustomize/kyaml v0.10.15 h1:dSLgG78KyaxN4HylPXdK+7zB3k7sW6q3IcCmcfKA+aI= sigs.k8s.io/kustomize/kyaml v0.10.15/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8= sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/cli/helm/action.go b/cli/helm/action.go new file mode 100644 index 0000000000..c4d9de9650 --- /dev/null +++ b/cli/helm/action.go @@ -0,0 +1,24 @@ +package helm + +import ( + "fmt" + "os" + + "helm.sh/helm/v3/pkg/action" + helmCLI "helm.sh/helm/v3/pkg/cli" + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +// InitActionConfig initializes a Helm Go SDK action configuration. This function currently uses a hack to override the +// namespace field that gets set in the K8s client set up by the SDK. +func InitActionConfig(actionConfig *action.Configuration, namespace string, settings *helmCLI.EnvSettings, logger action.DebugLog) (*action.Configuration, error) { + getter := settings.RESTClientGetter() + configFlags := getter.(*genericclioptions.ConfigFlags) + configFlags.Namespace = &namespace + err := actionConfig.Init(settings.RESTClientGetter(), namespace, + os.Getenv("HELM_DRIVER"), logger) + if err != nil { + return nil, fmt.Errorf("error setting up helm action configuration to find existing installations: %s", err) + } + return actionConfig, nil +} diff --git a/cli/helm/chart.go b/cli/helm/chart.go new file mode 100644 index 0000000000..3558e688ca --- /dev/null +++ b/cli/helm/chart.go @@ -0,0 +1,98 @@ +package helm + +import ( + "embed" + "path/filepath" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" + helmCLI "helm.sh/helm/v3/pkg/cli" +) + +const ( + chartFileName = "Chart.yaml" + valuesFileName = "values.yaml" + templatesDirName = "templates" +) + +// LoadChart will attempt to load a chart from the embedded file system. +func LoadChart(chart embed.FS, chartDirName string) (*chart.Chart, error) { + chartFiles, err := ReadChartFiles(chart, chartDirName) + if err != nil { + return nil, err + } + + return loader.LoadFiles(chartFiles) +} + +// ReadChartFiles reads the chart files from the embedded file system, and loads their contents into +// []*loader.BufferedFile. This is a format that the Helm Go SDK functions can read from to create a chart to install +// from. The names of these files are important, as there are case statements in the Helm Go SDK looking for files named +// "Chart.yaml" or "templates/.yaml", which is why even though the embedded file system has them named +// "consul/Chart.yaml" we have to strip the "consul" prefix out, which is done by the call to the helper method readFile. +func ReadChartFiles(chart embed.FS, chartDirName string) ([]*loader.BufferedFile, error) { + var chartFiles []*loader.BufferedFile + + // Load Chart.yaml and values.yaml first. + for _, f := range []string{chartFileName, valuesFileName} { + file, err := readFile(chart, filepath.Join(chartDirName, f), chartDirName) + if err != nil { + return nil, err + } + chartFiles = append(chartFiles, file) + } + + // Now load everything under templates/. + dirs, err := chart.ReadDir(filepath.Join(chartDirName, templatesDirName)) + if err != nil { + return nil, err + } + for _, f := range dirs { + if f.IsDir() { + // We only need to include files in the templates directory. + continue + } + + file, err := readFile(chart, filepath.Join(chartDirName, templatesDirName, f.Name()), chartDirName) + if err != nil { + return nil, err + } + chartFiles = append(chartFiles, file) + } + + return chartFiles, nil +} + +// FetchChartValues will attempt to fetch the values from the currently installed Helm chart. +func FetchChartValues(namespace string, settings *helmCLI.EnvSettings, uiLogger action.DebugLog) (map[string]interface{}, error) { + cfg := new(action.Configuration) + cfg, err := InitActionConfig(cfg, namespace, settings, uiLogger) + if err != nil { + return nil, err + } + + status := action.NewStatus(cfg) + release, err := status.Run(namespace) + if err != nil { + return nil, err + } + + return release.Config, nil +} + +func readFile(chart embed.FS, f string, pathPrefix string) (*loader.BufferedFile, error) { + bytes, err := chart.ReadFile(f) + if err != nil { + return nil, err + } + // Remove the path prefix. + rel, err := filepath.Rel(pathPrefix, f) + if err != nil { + return nil, err + } + return &loader.BufferedFile{ + Name: rel, + Data: bytes, + }, nil +} diff --git a/cli/helm/chart_test.go b/cli/helm/chart_test.go new file mode 100644 index 0000000000..1bed8bbdbe --- /dev/null +++ b/cli/helm/chart_test.go @@ -0,0 +1,56 @@ +package helm + +import ( + "embed" + "testing" + + "github.com/stretchr/testify/require" +) + +// Embed a test chart to test against. +//go:embed fixtures/consul/* fixtures/consul/templates/_helpers.tpl +var testChartFiles embed.FS + +func TestLoadChart(t *testing.T) { + directory := "fixtures/consul" + + expectedApiVersion := "v2" + expectedName := "Foo" + expectedVersion := "0.1.0" + expectedDescription := "Mock Helm Chart for testing." + expectedValues := map[string]interface{}{ + "key": "value", + } + + actual, err := LoadChart(testChartFiles, directory) + require.NoError(t, err) + require.Equal(t, expectedApiVersion, actual.Metadata.APIVersion) + require.Equal(t, expectedName, actual.Metadata.Name) + require.Equal(t, expectedVersion, actual.Metadata.Version) + require.Equal(t, expectedDescription, actual.Metadata.Description) + require.Equal(t, expectedValues, actual.Values) +} + +func TestReadChartFiles(t *testing.T) { + directory := "fixtures/consul" + expectedFiles := map[string]string{ + "Chart.yaml": "# This is a mock Helm Chart.yaml file used for testing.\napiVersion: v2\nname: Foo\nversion: 0.1.0\ndescription: Mock Helm Chart for testing.", + "values.yaml": "# This is a mock Helm values.yaml file used for testing.\nkey: value", + "templates/_helpers.tpl": "helpers", + "templates/foo.yaml": "foo: bar\n", + } + + files, err := ReadChartFiles(testChartFiles, directory) + require.NoError(t, err) + + actualFiles := make(map[string]string, len(files)) + for _, f := range files { + actualFiles[f.Name] = string(f.Data) + } + + for expectedName, expectedContents := range expectedFiles { + actualContents, ok := actualFiles[expectedName] + require.True(t, ok, "Expected file %s not found", expectedName) + require.Equal(t, expectedContents, actualContents) + } +} diff --git a/cli/helm/fixtures/consul/Chart.yaml b/cli/helm/fixtures/consul/Chart.yaml new file mode 100644 index 0000000000..72cdfea793 --- /dev/null +++ b/cli/helm/fixtures/consul/Chart.yaml @@ -0,0 +1,5 @@ +# This is a mock Helm Chart.yaml file used for testing. +apiVersion: v2 +name: Foo +version: 0.1.0 +description: Mock Helm Chart for testing. \ No newline at end of file diff --git a/cli/cmd/common/fixtures/consul/templates/_helpers.tpl b/cli/helm/fixtures/consul/templates/_helpers.tpl similarity index 100% rename from cli/cmd/common/fixtures/consul/templates/_helpers.tpl rename to cli/helm/fixtures/consul/templates/_helpers.tpl diff --git a/cli/helm/fixtures/consul/templates/foo.yaml b/cli/helm/fixtures/consul/templates/foo.yaml new file mode 100644 index 0000000000..20e9ff3fea --- /dev/null +++ b/cli/helm/fixtures/consul/templates/foo.yaml @@ -0,0 +1 @@ +foo: bar diff --git a/cli/helm/fixtures/consul/values.yaml b/cli/helm/fixtures/consul/values.yaml new file mode 100644 index 0000000000..e8062923bc --- /dev/null +++ b/cli/helm/fixtures/consul/values.yaml @@ -0,0 +1,2 @@ +# This is a mock Helm values.yaml file used for testing. +key: value \ No newline at end of file