From be88c6dead272dfd7345a705806267187c200067 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Tue, 26 Feb 2019 15:19:00 -0800 Subject: [PATCH 1/3] support additional replicated.app upstream formats --- pkg/specs/replicatedapp/resolver.go | 41 +++++++++++++++++++++++- pkg/specs/replicatedapp/selector.go | 10 ++++++ pkg/specs/replicatedapp/selector_test.go | 30 +++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/pkg/specs/replicatedapp/resolver.go b/pkg/specs/replicatedapp/resolver.go index 3a5a4c42b..3b5d71121 100644 --- a/pkg/specs/replicatedapp/resolver.go +++ b/pkg/specs/replicatedapp/resolver.go @@ -5,7 +5,9 @@ import ( "crypto/sha256" "encoding/json" "fmt" + "net/url" "path/filepath" + "strings" "github.com/replicatedhq/ship/pkg/specs/apptype" @@ -165,7 +167,24 @@ func (r *resolver) resolveCloudRelease(selector *Selector) (*ShipRelease, error) debug.Log("phase", "load-specs", "from", "gql", "addr", client.GQLServer.String()) release, err := client.GetRelease(selector) if err != nil { - return nil, err + if selector.InstallationID == "" { + debug.Log("event", "spec-resolve", "from", selector, "error", err) + fmt.Printf("Please enter a license id to continue: ") + var input string + fmt.Scanln(&input) + selector.InstallationID = input + + err = r.updateUpstream(*selector) + if err != nil { + return nil, errors.Wrapf(err, "persist updated upstream") + } + + release, err = client.GetRelease(selector) + } + + if err != nil { + return nil, err + } } if err := r.persistSpec([]byte(release.Spec)); err != nil { @@ -219,3 +238,23 @@ func (r *resolver) loadFakeEntitlements() (*api.Entitlements, error) { } return &entitlements, nil } + +// read the upstream, get the host/path, and replace the query params with the ones from the provided selector +func (r *resolver) updateUpstream(selector Selector) error { + currentState, err := r.StateManager.TryLoad() + if err != nil { + return errors.Wrap(err, "retrieve state") + } + currentUpstream := currentState.Upstream() + + parsedUpstream, err := url.Parse(currentUpstream) + if err != nil { + return errors.Wrap(err, "parse upstream") + } + + if !strings.HasSuffix(parsedUpstream.Path, "/") { + parsedUpstream.Path += "/" + } + + return r.StateManager.SerializeUpstream(parsedUpstream.Path + "?" + selector.String()) +} diff --git a/pkg/specs/replicatedapp/selector.go b/pkg/specs/replicatedapp/selector.go index 11ccac2ff..2aa48a1cc 100644 --- a/pkg/specs/replicatedapp/selector.go +++ b/pkg/specs/replicatedapp/selector.go @@ -2,6 +2,7 @@ package replicatedapp import ( "net/url" + "regexp" "strings" "github.com/google/go-querystring/query" @@ -30,6 +31,8 @@ func (s *Selector) String() string { return v.Encode() } +var pathQuery = regexp.MustCompile(`replicated\.app/([\w_\-/]+)`) + // this is less janky func (s *Selector) UnmarshalFrom(url *url.URL) *Selector { for key, values := range url.Query() { @@ -48,6 +51,13 @@ func (s *Selector) UnmarshalFrom(url *url.URL) *Selector { } } + if s.CustomerID == "" && pathQuery.MatchString(url.Path) { + matches := pathQuery.FindStringSubmatch(url.Path) + if len(matches) == 2 { + s.CustomerID = matches[1] + } + } + if strings.HasPrefix(url.String(), "staging.replicated.app") { s.Upstream = "https://pg.staging.replicated.com/graphql" } diff --git a/pkg/specs/replicatedapp/selector_test.go b/pkg/specs/replicatedapp/selector_test.go index 3198fa99b..edfb8b05f 100644 --- a/pkg/specs/replicatedapp/selector_test.go +++ b/pkg/specs/replicatedapp/selector_test.go @@ -34,6 +34,36 @@ func TestUnmarshalSelector(t *testing.T) { Upstream: "https://pg.staging.replicated.com/graphql", }, }, + { + name: "pathed app with customer id", + url: "replicated.app/app_id_here?customer_id=123&installation_id=456&release_id=789&release_semver=7.8.9", + want: &Selector{ + CustomerID: "123", + InstallationID: "456", + ReleaseID: "789", + ReleaseSemver: "7.8.9", + }, + }, + { + name: "pathed app WITHOUT customer id", + url: "replicated.app/app_id_here?installation_id=456&release_id=789&release_semver=7.8.9", + want: &Selector{ + CustomerID: "app_id_here", + InstallationID: "456", + ReleaseID: "789", + ReleaseSemver: "7.8.9", + }, + }, + { + name: "pathed app WITHOUT customer id and including forward slash in id", + url: "replicated.app/app/id/here?installation_id=456&release_id=789&release_semver=7.8.9", + want: &Selector{ + CustomerID: "app/id/here", + InstallationID: "456", + ReleaseID: "789", + ReleaseSemver: "7.8.9", + }, + }, } for _, test := range tests { From 9b359880938e8e8953b22c4b686bde818f69eb32 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Tue, 26 Feb 2019 15:44:33 -0800 Subject: [PATCH 2/3] copy and test updates --- pkg/specs/replicatedapp/resolver.go | 2 +- pkg/specs/replicatedapp/resolver_test.go | 54 ++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/pkg/specs/replicatedapp/resolver.go b/pkg/specs/replicatedapp/resolver.go index 3b5d71121..3e9a00482 100644 --- a/pkg/specs/replicatedapp/resolver.go +++ b/pkg/specs/replicatedapp/resolver.go @@ -169,7 +169,7 @@ func (r *resolver) resolveCloudRelease(selector *Selector) (*ShipRelease, error) if err != nil { if selector.InstallationID == "" { debug.Log("event", "spec-resolve", "from", selector, "error", err) - fmt.Printf("Please enter a license id to continue: ") + fmt.Printf("Please enter your license to continue: ") var input string fmt.Scanln(&input) selector.InstallationID = input diff --git a/pkg/specs/replicatedapp/resolver_test.go b/pkg/specs/replicatedapp/resolver_test.go index 60bfd829f..419533ac5 100644 --- a/pkg/specs/replicatedapp/resolver_test.go +++ b/pkg/specs/replicatedapp/resolver_test.go @@ -8,10 +8,12 @@ import ( "github.com/golang/mock/gomock" "github.com/replicatedhq/ship/pkg/api" "github.com/replicatedhq/ship/pkg/constants" + state2 "github.com/replicatedhq/ship/pkg/state" "github.com/replicatedhq/ship/pkg/test-mocks/state" "github.com/replicatedhq/ship/pkg/testing/logger" "github.com/replicatedhq/ship/pkg/testing/matchers" "github.com/spf13/afero" + "github.com/spf13/viper" "github.com/stretchr/testify/require" ) @@ -108,3 +110,55 @@ assets: }) } } + +func Test_resolver_updateUpstream(t *testing.T) { + tests := []struct { + name string + initUpstream string + selector Selector + expectUpstream string + }{ + { + name: "replicated.app", + initUpstream: "replicated.app", + selector: Selector{ + CustomerID: "abc", + InstallationID: "xyz", + }, + expectUpstream: "replicated.app/?customer_id=abc&installation_id=xyz", + }, + { + name: "staging.replicated.app", + initUpstream: "staging.replicated.app", + selector: Selector{ + CustomerID: "abc", + InstallationID: "xyz", + }, + expectUpstream: "staging.replicated.app/?customer_id=abc&installation_id=xyz", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := require.New(t) + + fs := afero.Afero{Fs: afero.NewMemMapFs()} + + realState := state2.MManager{FS: fs, Logger: &logger.TestLogger{T: t}, V: viper.New()} + + resolver := &resolver{ + Logger: &logger.TestLogger{T: t}, + StateManager: &realState, + } + + req.NoError(realState.SerializeUpstream(tt.initUpstream)) + + err := resolver.updateUpstream(tt.selector) + req.NoError(err) + + afterState, err := realState.TryLoad() + req.NoError(err) + + req.Equal(tt.expectUpstream, afterState.Upstream()) + }) + } +} From 7f23457969a00655002ca09ffc6393ea18a60436 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Tue, 26 Feb 2019 16:02:47 -0800 Subject: [PATCH 3/3] use mitchellh/cli for prompt --- pkg/specs/replicatedapp/resolver.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/pkg/specs/replicatedapp/resolver.go b/pkg/specs/replicatedapp/resolver.go index 3e9a00482..4bdf2f3ca 100644 --- a/pkg/specs/replicatedapp/resolver.go +++ b/pkg/specs/replicatedapp/resolver.go @@ -9,18 +9,19 @@ import ( "path/filepath" "strings" - "github.com/replicatedhq/ship/pkg/specs/apptype" - "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" + "github.com/mitchellh/cli" "github.com/pkg/errors" + "github.com/spf13/afero" + "github.com/spf13/viper" + yaml "gopkg.in/yaml.v2" + "github.com/replicatedhq/ship/pkg/api" "github.com/replicatedhq/ship/pkg/constants" "github.com/replicatedhq/ship/pkg/helpers/flags" + "github.com/replicatedhq/ship/pkg/specs/apptype" "github.com/replicatedhq/ship/pkg/state" - "github.com/spf13/afero" - "github.com/spf13/viper" - yaml "gopkg.in/yaml.v2" ) type shaSummer func([]byte) string @@ -29,6 +30,7 @@ type resolver struct { Client *GraphQLClient FS afero.Afero StateManager state.Manager + UI cli.Ui ShaSummer shaSummer Runbook string SetChannelName string @@ -45,11 +47,13 @@ func NewAppResolver( fs afero.Afero, graphql *GraphQLClient, stateManager state.Manager, + ui cli.Ui, ) Resolver { return &resolver{ Logger: logger, Client: graphql, FS: fs, + UI: ui, Runbook: flags.GetCurrentOrDeprecatedString(v, "runbook", "studio-file"), SetChannelName: flags.GetCurrentOrDeprecatedString(v, "set-channel-name", "studio-channel-name"), SetChannelIcon: flags.GetCurrentOrDeprecatedString(v, "set-channel-icon", "studio-channel-icon"), @@ -169,9 +173,13 @@ func (r *resolver) resolveCloudRelease(selector *Selector) (*ShipRelease, error) if err != nil { if selector.InstallationID == "" { debug.Log("event", "spec-resolve", "from", selector, "error", err) - fmt.Printf("Please enter your license to continue: ") + var input string - fmt.Scanln(&input) + input, err = r.UI.Ask("Please enter your license to continue: ") + if err != nil { + return nil, errors.Wrapf(err, "enter license from CLI") + } + selector.InstallationID = input err = r.updateUpstream(*selector)