diff --git a/cmd/kots/cli/install.go b/cmd/kots/cli/install.go index 5b8f7a1e5b..7d700e7ff7 100644 --- a/cmd/kots/cli/install.go +++ b/cmd/kots/cli/install.go @@ -163,10 +163,11 @@ func InstallCmd() *cobra.Command { }() upstream := pull.RewriteUpstream(args[0]) - preferredChannelSlug, err := extractPreferredChannelSlug(upstream) + preferredChannelSlug, err := extractPreferredChannelSlug(log, upstream) if err != nil { return errors.Wrap(err, "failed to extract preferred channel slug") } + license, err = kotslicense.VerifyAndUpdateLicense(log, license, preferredChannelSlug, isAirgap) if err != nil { return errors.Wrap(err, "failed to verify and update license") diff --git a/cmd/kots/cli/pull.go b/cmd/kots/cli/pull.go index d4dbdd0ba5..25ad78b4e2 100644 --- a/cmd/kots/cli/pull.go +++ b/cmd/kots/cli/pull.go @@ -101,14 +101,15 @@ func PullCmd() *cobra.Command { } upstream := pull.RewriteUpstream(args[0]) - preferredChannelSlug, err := extractPreferredChannelSlug(upstream) - if err != nil { - return errors.Wrap(err, "failed to extract preferred channel slug") - } log := logger.NewCLILogger(cmd.OutOrStdout()) log.Initialize() + preferredChannelSlug, err := extractPreferredChannelSlug(log, upstream) + if err != nil { + return errors.Wrap(err, "failed to extract preferred channel slug") + } + // If we are passed a multi-channel license, verify that the requested channel is in the license // so that we can warn the user immediately if it is not. license, err = kotslicense.VerifyAndUpdateLicense(log, license, preferredChannelSlug, false) diff --git a/cmd/kots/cli/util.go b/cmd/kots/cli/util.go index 4d4ea33f56..ac22cdec04 100644 --- a/cmd/kots/cli/util.go +++ b/cmd/kots/cli/util.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/pkg/errors" + "github.com/replicatedhq/kots/pkg/logger" "github.com/replicatedhq/kots/pkg/replicatedapp" "github.com/replicatedhq/kots/pkg/util" ) @@ -53,7 +54,7 @@ func splitEndpointAndNamespace(endpoint string) (string, string) { return registryEndpoint, registryNamespace } -func extractPreferredChannelSlug(upstreamURI string) (string, error) { +func extractPreferredChannelSlug(log *logger.CLILogger, upstreamURI string) (string, error) { u, err := url.ParseRequestURI(upstreamURI) if err != nil { return "", errors.Wrap(err, "failed to parse uri") @@ -67,5 +68,9 @@ func extractPreferredChannelSlug(upstreamURI string) (string, error) { if replicatedUpstream.Channel != nil { return *replicatedUpstream.Channel, nil } + + if log != nil { + log.ActionWithoutSpinner("No channel specified in upstream URI, falling back to channel slug 'stable'.") + } return "stable", nil } diff --git a/cmd/kots/cli/util_test.go b/cmd/kots/cli/util_test.go index a270dbaecf..50262ae0f9 100644 --- a/cmd/kots/cli/util_test.go +++ b/cmd/kots/cli/util_test.go @@ -111,7 +111,7 @@ func Test_extractPreferredChannelSlug(t *testing.T) { args{ upstreamURI: "replicated://app-slug", }, - "stable", // default channel + "stable", false, }, { @@ -133,7 +133,7 @@ func Test_extractPreferredChannelSlug(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := extractPreferredChannelSlug(tt.args.upstreamURI) + got, err := extractPreferredChannelSlug(nil, tt.args.upstreamURI) if (err != nil) != tt.wantErr { t.Errorf("extractPreferredChannelSlug() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/license/multichannel.go b/pkg/license/multichannel.go index db7f58d444..daad55f299 100644 --- a/pkg/license/multichannel.go +++ b/pkg/license/multichannel.go @@ -1,6 +1,9 @@ package license import ( + "fmt" + "strings" + "github.com/pkg/errors" "github.com/replicatedhq/kots/pkg/logger" "github.com/replicatedhq/kots/pkg/replicatedapp" @@ -40,11 +43,17 @@ func VerifyAndUpdateLicense(log *logger.CLILogger, license *kotsv1beta1.License, return nil, nil } if isAirgap { - if !canInstallFromChannel(preferredChannelSlug, license) { - return nil, errors.New("requested channel not found in supplied license") + if canInstallFromChannel(preferredChannelSlug, license) { + return license, nil + } + validChannels := []string{} + for _, channel := range license.Spec.Channels { + validChannels = append(validChannels, fmt.Sprintf("%s/%s", license.Spec.AppSlug, channel.ChannelSlug)) } - return license, nil + log.Errorf("Channel slug %q is not allowed by license. Please use one of the following: %s", preferredChannelSlug, strings.Join(validChannels, ", ")) + return license, errors.New(fmt.Sprintf("channel slug %q is not allowed by license", preferredChannelSlug)) } + log.ActionWithSpinner("Checking for license update") // we fetch the latest license to ensure that the license is up to date, before proceeding updatedLicense, err := replicatedapp.GetLatestLicense(license, "") @@ -53,8 +62,14 @@ func VerifyAndUpdateLicense(log *logger.CLILogger, license *kotsv1beta1.License, return nil, errors.Wrap(err, "failed to get latest license") } log.FinishSpinner() + if canInstallFromChannel(preferredChannelSlug, updatedLicense.License) { return updatedLicense.License, nil } - return nil, errors.New("requested channel not found in latest license") + validChannels := []string{} + for _, channel := range license.Spec.Channels { + validChannels = append(validChannels, fmt.Sprintf("%s/%s", license.Spec.AppSlug, channel.ChannelSlug)) + } + log.Errorf("Channel slug %q is not allowed by license. Please use one of the following: %s", preferredChannelSlug, strings.Join(validChannels, ", ")) + return updatedLicense.License, errors.New(fmt.Sprintf("channel slug %q is not allowed by latest license", preferredChannelSlug)) }