From 9e43ed3cd47aa05567e29f816f60cd5d653a170b Mon Sep 17 00:00:00 2001 From: divolgin Date: Wed, 26 Jun 2024 17:32:30 +0000 Subject: [PATCH] Remove support for Ship Watches Remove graphql-api references remove graphql queries --- cmd/support-bundle/commands/generate.go | 4 - pkg/analyze/analyze/analyze.go | 12 +- pkg/analyze/cli/run.go | 3 - pkg/analyze/resolver/resolver.go | 34 +- pkg/collect/bundle/default.go | 12 - pkg/collect/cli/generate.go | 97 +----- pkg/collect/graphql/support_bundle_spec.go | 326 ------------------- pkg/collect/graphql/types.go | 91 ------ pkg/collect/lifecycle/lifecycle.go | 6 +- pkg/collect/lifecycle/upload.go | 28 +- pkg/collect/marketapi/support_bundle_spec.go | 114 +++++++ pkg/collect/marketapi/types.go | 12 + pkg/collect/types/lifecycle.go | 15 - pkg/collect/types/spec.go | 8 - 14 files changed, 154 insertions(+), 608 deletions(-) delete mode 100644 pkg/collect/graphql/support_bundle_spec.go delete mode 100644 pkg/collect/graphql/types.go create mode 100644 pkg/collect/marketapi/support_bundle_spec.go create mode 100644 pkg/collect/marketapi/types.go diff --git a/cmd/support-bundle/commands/generate.go b/cmd/support-bundle/commands/generate.go index 86edc465c..117bb5449 100644 --- a/cmd/support-bundle/commands/generate.go +++ b/cmd/support-bundle/commands/generate.go @@ -33,13 +33,9 @@ func NewGenerateCommand(supportBundle *cli.Cli) *cobra.Command { cmd.Flags().BoolVarP(&opts.ConfirmUploadPrompt, "yes-upload", "u", false, "If present, auto-confirm any upload prompts") cmd.Flags().BoolVar(&opts.DenyUploadPrompt, "no-upload", false, "If present, auto-deny any upload prompts") - cmd.Flags().StringVar(&opts.CustomerID, "customer-id", "", "Replicated Customer ID") cmd.Flags().StringVar(&opts.Endpoint, "endpoint", cli.DefaultEndpoint, "Customer API Endpoint") cmd.Flags().StringVar(&opts.ChannelID, "channel-id", "", "Replicated ChannelID to attempt to get a collector definition from") - cmd.Flags().StringVar(&opts.WatchID, "watch-id", "", "Replicated WatchID to attempt to get a collector definition from") - - cmd.Flags().MarkDeprecated("customer-id", "This argument is no longer supported. Consider using \"channel-id\"") //--out - works, and its totally interactive and everything, // and the bundle just gets dumped to stdout. diff --git a/pkg/analyze/analyze/analyze.go b/pkg/analyze/analyze/analyze.go index e75e31142..81f56ffc3 100644 --- a/pkg/analyze/analyze/analyze.go +++ b/pkg/analyze/analyze/analyze.go @@ -39,7 +39,6 @@ type Analyze struct { Specs []string SkipDefault bool BundleRootSubpath string - CustomerID string // deprecated ChannelID string Endpoint string @@ -60,7 +59,6 @@ func New(v *viper.Viper, logger log.Logger, resolver *resolver.Resolver, getter Specs: v.GetStringSlice("spec"), SkipDefault: v.GetBool("skip-default"), BundleRootSubpath: v.GetString("bundle-root-subpath"), - CustomerID: v.GetString("customer-id"), Endpoint: v.GetString("endpoint"), // analyze @@ -133,18 +131,16 @@ func (a *Analyze) Execute(ctx context.Context, bundlePath string) ([]api.Result, } input := resolver.Input{ - Files: a.SpecFiles, - Inline: a.Specs, - CustomerID: a.CustomerID, - ChannelID: a.ChannelID, - Endpoint: endpoint, + Files: a.SpecFiles, + Inline: a.Specs, + ChannelID: a.ChannelID, + Endpoint: endpoint, } spec, err := a.Resolver.ResolveSpec(ctx, input, a.SkipDefault) debug.Log( "phase", "resolve", "files", a.SpecFiles, "inline", a.Specs, - "customerID", a.CustomerID, "channelID", a.ChannelID, "endpoint", endpoint, "error", err) diff --git a/pkg/analyze/cli/run.go b/pkg/analyze/cli/run.go index c3718cd4d..d1ed155c6 100644 --- a/pkg/analyze/cli/run.go +++ b/pkg/analyze/cli/run.go @@ -61,9 +61,6 @@ func RunCmd() *cobra.Command { cmd.Flags().Bool("skip-default", false, "Skip the default analyze spec") cmd.Flags().String("bundle-root-subpath", "", "The subpath within the archive at which the bundle root resides") - cmd.Flags().String("customer-id", "", "Replicated Customer ID") - cmd.Flags().MarkDeprecated("customer-id", "This argument is no longer supported. Consider using \"channel-id\"") - cmd.Flags().String("channel-id", "", "Replicated ChannelID to attempt to get a collector definition from") cmd.Flags().String("endpoint", collectcli.DefaultEndpoint, "Endpoint to fetch collector definitions fom") diff --git a/pkg/analyze/resolver/resolver.go b/pkg/analyze/resolver/resolver.go index e14390df8..8739e3e74 100644 --- a/pkg/analyze/resolver/resolver.go +++ b/pkg/analyze/resolver/resolver.go @@ -10,7 +10,7 @@ import ( multierror "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/replicatedcom/support-bundle/pkg/analyze/api" - "github.com/replicatedcom/support-bundle/pkg/collect/graphql" + "github.com/replicatedcom/support-bundle/pkg/collect/marketapi" "github.com/spf13/afero" ) @@ -20,11 +20,10 @@ type Resolver struct { } type Input struct { - Files []string - Inline []string - CustomerID string - ChannelID string - Endpoint string + Files []string + Inline []string + ChannelID string + Endpoint string } func New(logger log.Logger, fs afero.Fs) *Resolver { @@ -77,29 +76,8 @@ func (r *Resolver) ResolveSpec(ctx context.Context, input Input, skipDefault boo } } - if input.CustomerID != "" { - client := graphql.NewClient(input.Endpoint, http.DefaultClient) - inline, errI := client.GetCustomerSpec(input.CustomerID) - debug.Log( - "phase", "doc.customer.retrieve", - "error", errI) - if errI != nil { - err = multierror.Append(err, errors.Wrap(errI, "retrieve customer doc")) - } else { - doc, errI := api.DeserializeDoc([]byte(inline)) - debug.Log( - "phase", "doc.customer.deserialize", - "error", errI) - if errI != nil { - err = multierror.Append(err, errors.Wrap(errI, "deserialize customer doc")) - } else { - resolved = mergeDocs(resolved, doc) - } - } - } - if input.ChannelID != "" { - client := graphql.NewClient(input.Endpoint, http.DefaultClient) + client := marketapi.NewClient(input.Endpoint, http.DefaultClient) inline, errI := client.GetChannelSpec(input.ChannelID) debug.Log( "phase", "doc.channel.retrieve", diff --git a/pkg/collect/bundle/default.go b/pkg/collect/bundle/default.go index 8e2a2757f..494299224 100644 --- a/pkg/collect/bundle/default.go +++ b/pkg/collect/bundle/default.go @@ -80,15 +80,3 @@ func ChannelJSONSpec(channelID string) types.Spec { }, } } - -func WatchJSONSpec(watchID string) types.Spec { - return types.Spec{ - SpecShared: types.SpecShared{ - Description: "Troubleshoot Watch Metadata", - OutputDir: "/", - }, - WatchMeta: &types.WatchMetaOptions{ - WatchID: watchID, - }, - } -} diff --git a/pkg/collect/cli/generate.go b/pkg/collect/cli/generate.go index 58bdcec31..f3f180e00 100644 --- a/pkg/collect/cli/generate.go +++ b/pkg/collect/cli/generate.go @@ -8,8 +8,8 @@ import ( "github.com/pkg/errors" "github.com/replicatedcom/support-bundle/pkg/collect/bundle" - "github.com/replicatedcom/support-bundle/pkg/collect/graphql" "github.com/replicatedcom/support-bundle/pkg/collect/lifecycle" + "github.com/replicatedcom/support-bundle/pkg/collect/marketapi" "github.com/replicatedcom/support-bundle/pkg/collect/spec" "github.com/replicatedcom/support-bundle/pkg/collect/types" "github.com/replicatedcom/support-bundle/pkg/util" @@ -17,7 +17,7 @@ import ( ) const ( - DefaultEndpoint = "https://pg.replicated.com/graphql" + DefaultEndpoint = "https://api.replicated.com/market" DefaultGenerateTimeoutSeconds = 60 ) @@ -32,7 +32,6 @@ type GenerateOptions struct { Quiet bool Endpoint string ChannelID string - WatchID string CustomerID string // Deprecated @@ -58,7 +57,7 @@ func (cli *Cli) Generate(opts GenerateOptions) error { endpoint = DefaultEndpoint } - graphQLClient := graphql.NewClient(endpoint, http.DefaultClient) + marketapiClient := marketapi.NewClient(endpoint, http.DefaultClient) specs, err := resolveLocalSpecs(opts) if err != nil { return errors.Wrap(err, "resolve specs") @@ -66,33 +65,12 @@ func (cli *Cli) Generate(opts GenerateOptions) error { var customerDoc *types.Doc var channelDoc *types.Doc - var watchDoc *types.Doc expectedDefaultTasks := 1 // there is always at least 1 for the version - // this next if statement and included scope is deprecated - if opts.CustomerID != "" { - jww.DEBUG.Printf("Getting spec with customer id %s", opts.CustomerID) - - customerDoc, err = getCustomerDoc(graphQLClient, opts.CustomerID) - if err != nil { - return errors.Wrap(err, "get customer specs") - } - specs = append(specs, customerDoc.Collect.V1...) - specs = append(specs, bundle.CustomerJSONSpec(opts.CustomerID)) - - if !opts.SkipDefault && types.GetUseDefaults(customerDoc.Lifecycle) { - defaultSpecs, err := bundle.DefaultSpecs() - if err != nil { - return errors.Wrap(err, "get default spec") - } - specs = append(specs, defaultSpecs...) - } - - expectedDefaultTasks++ - } else if opts.ChannelID != "" { + if opts.ChannelID != "" { jww.DEBUG.Printf("Getting spec with channel id %s", opts.ChannelID) - channelDoc, err = getChannelDoc(graphQLClient, opts.ChannelID) + channelDoc, err = getChannelDoc(marketapiClient, opts.ChannelID) if err != nil { return errors.Wrap(err, "get channel spec") } @@ -107,26 +85,6 @@ func (cli *Cli) Generate(opts GenerateOptions) error { specs = append(specs, defaultSpecs...) } - expectedDefaultTasks++ - } else if opts.WatchID != "" { - jww.DEBUG.Printf("Getting spec with watch id %s", opts.WatchID) - - watchDoc, err = getWatchDoc(graphQLClient, opts.WatchID) - if err != nil { - return errors.Wrap(err, "get watch spec") - } - - specs = append(specs, watchDoc.Collect.V1...) - specs = append(specs, bundle.WatchJSONSpec(opts.WatchID)) - - if !opts.SkipDefault && types.GetUseDefaults(watchDoc.Lifecycle) { - defaultSpecs, err := bundle.DefaultSpecs() - if err != nil { - return errors.Wrap(err, "get default spec") - } - specs = append(specs, defaultSpecs...) - } - expectedDefaultTasks++ } @@ -144,10 +102,8 @@ func (cli *Cli) Generate(opts GenerateOptions) error { BundleTasks: tasks, GenerateTimeout: timeoutSeconds, GenerateBundlePath: opts.BundlePath, - GraphQLClient: graphQLClient, - UploadCustomerID: opts.CustomerID, + MarketAPIClient: marketapiClient, UploadChannelID: opts.ChannelID, - UploadWatchID: opts.WatchID, ConfirmUploadPrompt: opts.ConfirmUploadPrompt, DenyUploadPrompt: opts.DenyUploadPrompt, Quiet: opts.Quiet, @@ -155,19 +111,13 @@ func (cli *Cli) Generate(opts GenerateOptions) error { lifecycleTasks := types.DefaultLifecycleTasks - if opts.WatchID != "" { - lifecycleTasks = types.DefaultWatchLifecycleTasks - } - if channelDoc != nil && channelDoc.Lifecycle != nil { lifecycleTasks = channelDoc.Lifecycle } else if customerDoc != nil && customerDoc.Lifecycle != nil { lifecycleTasks = customerDoc.Lifecycle - } else if watchDoc != nil && watchDoc.Lifecycle != nil { - lifecycleTasks = watchDoc.Lifecycle } - if opts.CustomerID == "" && opts.ChannelID == "" && opts.WatchID == "" { + if opts.CustomerID == "" && opts.ChannelID == "" { lifecycleTasks = types.GenerateOnlyLifecycleTasks } @@ -219,23 +169,8 @@ func resolveLocalSpecs(opts GenerateOptions) ([]types.Spec, error) { return specs, nil } -// getCustomerDoc is deprecated -func getCustomerDoc(gqlClient *graphql.Client, customerID string) (*types.Doc, error) { - remoteSpecBody, err := gqlClient.GetCustomerSpec(customerID) - if err != nil { - return nil, errors.Wrap(err, "get remote spec") - } - - customerDoc, err := spec.Unmarshal(remoteSpecBody) - if err != nil { - return nil, errors.Wrap(err, "parse customer spec") - } - - return customerDoc, nil -} - -func getChannelDoc(gqlClient *graphql.Client, channelID string) (*types.Doc, error) { - remoteSpecBody, err := gqlClient.GetChannelSpec(channelID) +func getChannelDoc(client *marketapi.Client, channelID string) (*types.Doc, error) { + remoteSpecBody, err := client.GetChannelSpec(channelID) if err != nil { return nil, errors.Wrap(err, "get remote spec") } @@ -247,17 +182,3 @@ func getChannelDoc(gqlClient *graphql.Client, channelID string) (*types.Doc, err return channelDoc, nil } - -func getWatchDoc(gqlClient *graphql.Client, watchID string) (*types.Doc, error) { - remoteSpecBody, err := gqlClient.GetWatchSpec(watchID) - if err != nil { - return nil, errors.Wrap(err, "get remote spec") - } - - watchDoc, err := spec.Unmarshal(remoteSpecBody) - if err != nil { - return nil, errors.Wrap(err, "parse watch spec") - } - - return watchDoc, nil -} diff --git a/pkg/collect/graphql/support_bundle_spec.go b/pkg/collect/graphql/support_bundle_spec.go deleted file mode 100644 index c2bc565a2..000000000 --- a/pkg/collect/graphql/support_bundle_spec.go +++ /dev/null @@ -1,326 +0,0 @@ -package graphql - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "fmt" - "net/http" - "net/url" - - "github.com/pkg/errors" -) - -// deprecated -const customerSpecQuery = ` -query { - supportBundleSpec { - spec - hydrated - } -} -` - -const channelSpecQuery = ` -query channelCollectors($channelId: String) { - channelCollectors(channelId: $channelId) { - spec - hydrated - } -} -` - -const watchSpecQuery = ` -query watchCollectors($watchId: String!) { - watchCollectors(watchId: $watchId) { - spec - hydrated - } -} -` - -const startCustomerUploadMutation = ` -mutation GetPresignedURI($size: Int, $notes: String) { - uploadSupportBundle(size: $size, notes: $notes) { - uploadUri, - supportBundle { - id - } - } -} -` - -const startChannelUploadMutation = ` -mutation GetChannelPresignedURI($channelId: String!, $size: Int, $notes: String) { - uploadChannelSupportBundle(channelId: $channelId, size: $size, notes: $notes) { - uploadUri, - supportBundle { - id - } - } -} -` - -const startWatchUploadMutation = ` -mutation GetWatchPresignedURI($watchId: String!, $size: Int) { - uploadSupportBundle(watchId: $watchId, size: $size) { - uploadUri, - supportBundle { - id - } - } -} -` - -const markSupportBundleUploadedMutation = ` -mutation FinishUploadSupportBundle($id: String!) { - markSupportBundleUploaded(id: $id) { - id - } -} -` - -type SupportBundleSpec struct { - CustomerID string -} - -type Client struct { - endpoint string - client *http.Client -} - -func NewClient(endpoint string, client *http.Client) *Client { - if client == nil { - client = http.DefaultClient - } - - return &Client{ - endpoint: endpoint, - client: client, - } -} - -// GetCustomerSpec is deprecated -func (c *Client) GetCustomerSpec(id string) ([]byte, error) { - resp, err := c.executeGraphQLQuery(id, Request{ - Query: customerSpecQuery, - }) - - if err != nil { - return nil, errors.Wrap(err, "executing graphql request") - } - defer resp.Body.Close() - - decoder := json.NewDecoder(resp.Body) - specBody := SupportBundleResponse{} - - if err := decoder.Decode(&specBody); err != nil { - return nil, errors.Wrap(err, "unmarshalling graphql response") - } - - if len(specBody.Errors) > 0 { - return nil, Errors{Errors: specBody.Errors} - } - - return []byte(specBody.Data.Hydrated), nil -} - -// GetChannelSpec will query the endpoint set in the client with a Replicated ChannelID -// and will return a fully rendered spec, containing collect and lifecycle keys -func (c *Client) GetChannelSpec(channelID string) ([]byte, error) { - resp, err := c.executeGraphQLQuery("", Request{ - Query: channelSpecQuery, - Variables: map[string]interface{}{ - "channelId": channelID, - }, - }) - defer resp.Body.Close() - - if err != nil { - return nil, errors.Wrap(err, "executing graphql request") - } - - decoder := json.NewDecoder(resp.Body) - specBody := ChannelCollectorsResponse{} - - if err := decoder.Decode(&specBody); err != nil { - return nil, errors.Wrap(err, "unmarshalling graphql response") - } - - if len(specBody.Errors) > 0 { - return nil, Errors{Errors: specBody.Errors} - } - - return []byte(specBody.Data.Hydrated), nil -} - -// GetWatchSpec will query the endpoint set in the client with a Replicated WatchID -// and will return a fully rendered spec, containing collect and lifecycle keys -func (c *Client) GetWatchSpec(watchID string) ([]byte, error) { - resp, err := c.executeGraphQLQuery("", Request{ - Query: watchSpecQuery, - Variables: map[string]interface{}{ - "watchId": watchID, - }, - }) - - if err != nil { - return nil, errors.Wrap(err, "executing graphql request") - } - defer resp.Body.Close() - - decoder := json.NewDecoder(resp.Body) - specBody := WatchCollectorsResponse{} - - if err := decoder.Decode(&specBody); err != nil { - return nil, errors.Wrap(err, "unmarshalling graphql response") - } - - if len(specBody.Errors) > 0 { - return nil, Errors{Errors: specBody.Errors} - } - - return []byte(specBody.Data.Hydrated), nil -} - -// GetSupportBundleCustomerUploadURI queries the Endpoint in Client to retrieve a URI that can be used to upload -// a support bundle for a specific customer -// This function is deprecated -func (c *Client) GetSupportBundleCustomerUploadURI(customerID string, size int64, notes string) (string, *url.URL, error) { - resp, err := c.executeGraphQLQuery(customerID, Request{ - Query: startCustomerUploadMutation, - Variables: map[string]interface{}{ - "size": size, - "notes": notes, - }, - }) - - if err != nil { - return "", nil, errors.Wrap(err, "executing graphql request") - } - defer resp.Body.Close() - - decoder := json.NewDecoder(resp.Body) - uploadBody := SupportBundleUploadResponse{} - - if err := decoder.Decode(&uploadBody); err != nil { - return "", nil, errors.Wrap(err, "unmarshalling graphql response") - } - - if len(uploadBody.Errors) > 0 { - return "", nil, fmt.Errorf("%v", uploadBody.Errors) - } - - uri, err := url.Parse(uploadBody.Data.UploadURI) - if err != nil { - return "", nil, errors.Wrap(err, "parsing upload URI") - } - - return uploadBody.Data.ID, uri, nil -} - -// GetSupportBundleChannelUploadURI queries the Endpoint in Client to retrieve a URI that can be used to upload -// a support bundle for a specific channel -func (c *Client) GetSupportBundleChannelUploadURI(channelID string, size int64, notes string) (string, *url.URL, error) { - resp, err := c.executeGraphQLQuery("", Request{ - Query: startChannelUploadMutation, - Variables: map[string]interface{}{ - "channelId": channelID, - "size": size, - "notes": notes, - }, - }) - - if err != nil { - return "", nil, errors.Wrap(err, "executing graphql request") - } - defer resp.Body.Close() - - decoder := json.NewDecoder(resp.Body) - uploadBody := SupportBundleChannelUploadResponse{} - - if err := decoder.Decode(&uploadBody); err != nil { - return "", nil, errors.Wrap(err, "unmarshalling graphql response") - } - - if len(uploadBody.Errors) > 0 { - return "", nil, fmt.Errorf("%v", uploadBody.Errors) - } - - uri, err := url.Parse(uploadBody.Data.UploadURI) - if err != nil { - return "", nil, errors.Wrap(err, "parsing upload URI") - } - - return uploadBody.Data.ID, uri, nil -} - -// GetSupportBundleWatchUploadURI queries the Endpoint in Client to retrieve a URI that can be used to upload -// a support bundle for a specific watch -func (c *Client) GetSupportBundleWatchUploadURI(watchID string, size int64) (string, *url.URL, error) { - resp, err := c.executeGraphQLQuery("", Request{ - Query: startWatchUploadMutation, - Variables: map[string]interface{}{ - "watchId": watchID, - "size": size, - }, - }) - - if err != nil { - return "", nil, errors.Wrap(err, "executing graphql request") - } - defer resp.Body.Close() - - decoder := json.NewDecoder(resp.Body) - uploadBody := SupportBundleWatchUploadResponse{} - - if err := decoder.Decode(&uploadBody); err != nil { - return "", nil, errors.Wrap(err, "unmarshalling graphql response") - } - - if len(uploadBody.Errors) > 0 { - return "", nil, fmt.Errorf("%v", uploadBody.Errors) - } - - uri, err := url.Parse(uploadBody.Data.UploadURI) - if err != nil { - return "", nil, errors.Wrap(err, "parsing upload URI") - } - - return uploadBody.Data.ID, uri, nil -} - -// MarkSupportBundleUploaded mutates the Endpoint in Client to mark a support bundle as uploaded -func (c *Client) MarkSupportBundleUploaded(bundleID string) error { - _, err := c.executeGraphQLQuery("", Request{ - Query: markSupportBundleUploadedMutation, - Variables: map[string]interface{}{ - "id": bundleID, - }, - }) - - return err -} - -func (c *Client) executeGraphQLQuery(auth string, gr Request) (*http.Response, error) { - body, err := json.Marshal(gr) - - if err != nil { - return nil, errors.Wrap(err, "marshalling graphql request") - } - - bodyReader := bytes.NewReader(body) - - req, err := http.NewRequest("POST", c.endpoint, bodyReader) - if err != nil { - return nil, errors.Wrap(err, "creating http request") - } - - req.Header.Set("Content-Type", "application/json") - - if auth != "" { - req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(auth+":")))) - } - - return c.client.Do(req) -} diff --git a/pkg/collect/graphql/types.go b/pkg/collect/graphql/types.go deleted file mode 100644 index cdba1084e..000000000 --- a/pkg/collect/graphql/types.go +++ /dev/null @@ -1,91 +0,0 @@ -package graphql - -import "fmt" - -type Request struct { - Query string `json:"query"` - Variables map[string]interface{} `json:"variables"` - OperationName string `json:"operationName"` -} - -type Error struct { - Locations []map[string]interface{} `json:"locations"` - Message string `json:"message"` - Code string `json:"code"` -} - -type SupportBundleResponse struct { - Data *SupportBundleResult `json:"data,omitempty"` - Errors []Error `json:"errors,omitempty"` -} - -type SupportBundleResult struct { - SupportBundle `json:"supportBundleSpec"` -} - -type ChannelCollectorsResponse struct { - Data ChannelCollectorsResult `json:"data,omitempty"` - Errors []Error `json:"errors,omitempty"` -} - -type ChannelCollectorsResult struct { - SupportBundle `json:"channelCollectors,omitempty"` -} - -type WatchCollectorsResponse struct { - Data WatchCollectorsResult `json:"data,omitempty"` - Errors []Error `json:"errors,omitempty"` -} - -type WatchCollectorsResult struct { - SupportBundle `json:"watchCollectors,omitempty"` -} - -type SupportBundle struct { - Spec string `json:"spec,omitempty"` - Hydrated string `json:"hydrated,omitempty"` -} - -type SupportBundleChannelUploadResponse struct { - Data SupportBundleChannelRequestResult `json:"data,omitempty"` - Errors []Error `json:"errors,omitempty"` -} - -type SupportBundleChannelRequestResult struct { - UploadSupportBundle `json:"uploadChannelSupportBundle,omitempty"` -} - -type SupportBundleWatchUploadResponse struct { - Data SupportBundleWatchRequestResult `json:"data,omitempty"` - Errors []Error `json:"errors,omitempty"` -} - -type SupportBundleWatchRequestResult struct { - UploadSupportBundle `json:"uploadSupportBundle,omitempty"` -} - -type SupportBundleUploadResponse struct { - Data SupportBundleRequestResult `json:"data,omitempty"` - Errors []Error `json:"errors,omitempty"` -} - -type SupportBundleRequestResult struct { - UploadSupportBundle `json:"uploadSupportBundle,omitempty"` -} - -type UploadSupportBundle struct { - UploadURI string `json:"uploadUri,omitempty"` - UploadedSupportBundle `json:"supportBundle,omitempty"` -} - -type UploadedSupportBundle struct { - ID string `json:"id,omitempty"` -} - -type Errors struct { - Errors []Error -} - -func (e Errors) Error() string { - return fmt.Sprintf("%v", e.Errors) -} diff --git a/pkg/collect/lifecycle/lifecycle.go b/pkg/collect/lifecycle/lifecycle.go index 8c0737828..e3fe29f40 100644 --- a/pkg/collect/lifecycle/lifecycle.go +++ b/pkg/collect/lifecycle/lifecycle.go @@ -4,7 +4,7 @@ import ( "os" "github.com/pkg/errors" - "github.com/replicatedcom/support-bundle/pkg/collect/graphql" + "github.com/replicatedcom/support-bundle/pkg/collect/marketapi" "github.com/replicatedcom/support-bundle/pkg/collect/types" ) @@ -14,10 +14,8 @@ type Lifecycle struct { DenyUploadPrompt bool Quiet bool GenerateBundlePath string - UploadCustomerID string // deprecated UploadChannelID string - UploadWatchID string - GraphQLClient *graphql.Client + MarketAPIClient *marketapi.Client FileInfo os.FileInfo // RealGeneratedBundlePath is the actual path the bundle was written to. // it will be different from GenerateBundlePath if a URL or `-` (stdout) diff --git a/pkg/collect/lifecycle/upload.go b/pkg/collect/lifecycle/upload.go index 82115c963..80e55554e 100644 --- a/pkg/collect/lifecycle/upload.go +++ b/pkg/collect/lifecycle/upload.go @@ -61,37 +61,23 @@ func (task *UploadTask) Execute(l *Lifecycle) (bool, error) { } } - if l.UploadCustomerID == "" && l.UploadChannelID == "" && l.UploadWatchID == "" { - return false, errors.New("upload with no watch id, channel id, or customer id") + if l.UploadChannelID == "" { + return false, errors.New("upload with no channel id") } var bundleID string var url *url.URL if l.UploadChannelID != "" { - channelBundleID, channelURL, err := l.GraphQLClient.GetSupportBundleChannelUploadURI(l.UploadChannelID, l.FileInfo.Size(), l.Notes) + channelBundleID, channelURL, err := l.MarketAPIClient.GetSupportBundleChannelUploadURI(l.UploadChannelID, l.FileInfo.Size(), l.Notes) if err != nil { return false, errors.Wrap(err, "get presigned URL for channel upload") } bundleID = channelBundleID url = channelURL - } else if l.UploadWatchID != "" { - watchBundleID, watchURL, err := l.GraphQLClient.GetSupportBundleWatchUploadURI(l.UploadWatchID, l.FileInfo.Size()) - if err != nil { - return false, errors.Wrap(err, "get presigned URL for watch upload") - } - - bundleID = watchBundleID - url = watchURL - } else if l.UploadCustomerID != "" { - customerBundleID, customerURL, err := l.GraphQLClient.GetSupportBundleCustomerUploadURI(l.UploadCustomerID, l.FileInfo.Size(), l.Notes) - if err != nil { - return false, errors.Wrap(err, "get presigned URL for customer upload") - } - - bundleID = customerBundleID - url = customerURL + } else { + return false, errors.New("upload with no channel id") } err = putObject(l.FileInfo, l.RealGeneratedBundlePath, url) @@ -99,8 +85,8 @@ func (task *UploadTask) Execute(l *Lifecycle) (bool, error) { return false, errors.Wrap(err, "uploading to presigned URL") } - if err = l.GraphQLClient.MarkSupportBundleUploaded(bundleID); err != nil { - return false, errors.Wrap(err, "mark support bundle uploaded") + if err = l.MarketAPIClient.MarkChannelSupportBundleUploaded(bundleID, l.UploadChannelID); err != nil { + return false, errors.Wrap(err, "mark channel support bundle uploaded") } return true, nil diff --git a/pkg/collect/marketapi/support_bundle_spec.go b/pkg/collect/marketapi/support_bundle_spec.go new file mode 100644 index 000000000..eb19465fa --- /dev/null +++ b/pkg/collect/marketapi/support_bundle_spec.go @@ -0,0 +1,114 @@ +package marketapi + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + + "github.com/pkg/errors" + "sigs.k8s.io/yaml" +) + +type Client struct { + endpoint string + client *http.Client +} + +func NewClient(endpoint string, client *http.Client) *Client { + if client == nil { + client = http.DefaultClient + } + + return &Client{ + endpoint: endpoint, + client: client, + } +} + +// GetChannelSpec will query the endpoint set in the client with a Replicated ChannelID +// and will return a fully rendered spec, containing collect and lifecycle keys +func (c *Client) GetChannelSpec(channelID string) ([]byte, error) { + resp, err := c.client.Get(fmt.Sprintf("%s/v1/support/channel/%s/spec", c.endpoint, channelID)) + if err != nil { + return nil, errors.Wrap(err, "executing request") + } + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + if resp.StatusCode != http.StatusOK { + return nil, errors.Errorf("Error getting channel spec: StatusCode=%v, %s", resp.StatusCode, body) + } + + collectorSpec := ChannelCollectorsResponse{} + if err := json.Unmarshal(body, &collectorSpec); err != nil { + return nil, errors.Wrap(err, "unmarshalling response") + } + + jsonSpec, err := yaml.YAMLToJSON([]byte(collectorSpec.Spec)) + if err != nil { + return nil, errors.Wrap(err, "converting yaml to json") + } + + return jsonSpec, nil +} + +// GetSupportBundleChannelUploadURI queries the Endpoint in Client to retrieve a URI that can be used to upload +// a support bundle for a specific channel +func (c *Client) GetSupportBundleChannelUploadURI(channelID string, size int64, notes string) (string, *url.URL, error) { + resp, err := c.client.Post(fmt.Sprintf("%s/v1/support/upload/url", c.endpoint), "application/json", nil) + if err != nil { + return "", nil, errors.Wrap(err, "executing request") + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", nil, errors.Wrap(err, "reading response body") + } + + if resp.StatusCode != http.StatusOK { + return "", nil, errors.Errorf("Error getting channel spec: StatusCode=%v, %s", resp.StatusCode, body) + } + + uploadResponse := UploadURLResponse{} + if err := json.Unmarshal(body, &uploadResponse); err != nil { + return "", nil, errors.Wrap(err, "unmarshalling response") + } + + uri, err := url.Parse(uploadResponse.UploadURL) + if err != nil { + return "", nil, errors.Wrap(err, "parsing upload URI") + } + + return uploadResponse.BundleID, uri, nil +} + +// MarkChannelSupportBundleUploaded mutates the Endpoint in Client to mark a support bundle as uploaded +func (c *Client) MarkChannelSupportBundleUploaded(bundleID string, channelID string) error { + payload := struct { + ChannelID string `json:"channel_id"` + }{ + ChannelID: channelID, + } + + payloadBytes, err := json.Marshal(payload) + if err != nil { + return errors.Wrap(err, "marshalling payload") + } + + resp, err := c.client.Post(fmt.Sprintf("%s/v1/support/bundle/%s/uploaded", c.endpoint, bundleID), "application/json", bytes.NewReader(payloadBytes)) + if err != nil { + return errors.Wrap(err, "executing request") + } + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + if resp.StatusCode != http.StatusOK { + return errors.Errorf("Error getting channel spec: StatusCode=%v, %s", resp.StatusCode, body) + } + + return nil +} diff --git a/pkg/collect/marketapi/types.go b/pkg/collect/marketapi/types.go new file mode 100644 index 000000000..bd1edc15f --- /dev/null +++ b/pkg/collect/marketapi/types.go @@ -0,0 +1,12 @@ +package marketapi + +type ChannelCollectorsResponse struct { + ID string `json:"id"` + Spec string `json:"spec,omitempty"` + Lifecycle string `json:"lifecycle,omitempty"` +} + +type UploadURLResponse struct { + BundleID string `json:"bundle_id"` + UploadURL string `json:"upload_url"` +} diff --git a/pkg/collect/types/lifecycle.go b/pkg/collect/types/lifecycle.go index 0ed6f1322..f9b707d37 100644 --- a/pkg/collect/types/lifecycle.go +++ b/pkg/collect/types/lifecycle.go @@ -32,21 +32,6 @@ var DefaultLifecycleTasks = []LifecycleTask{ }, } -var DefaultWatchLifecycleTasks = []LifecycleTask{ - { - Message: &MessageOptions{"Starting support bundle collection..."}, - }, - { - Generate: &GenerateOptions{UseDefaults: true}, - }, - { - Upload: &UploadOptions{}, - }, - { - Message: &MessageOptions{"Upload complete! Visit the Troubleshoot page in Ship to view the results"}, - }, -} - func GetUseDefaults(tasks []LifecycleTask) bool { for _, task := range tasks { if task.Generate != nil && task.Generate.UseDefaults { diff --git a/pkg/collect/types/spec.go b/pkg/collect/types/spec.go index 04f7c6e67..174a5ed09 100644 --- a/pkg/collect/types/spec.go +++ b/pkg/collect/types/spec.go @@ -74,7 +74,6 @@ type Spec struct { SupportBundleLogs *SupportBundleLogsOptions `json:"logs,omitempty"` CustomerMeta *CustomerMetaOptions `json:"meta.customer,omitempty"` // deprecated ChannelMeta *ChannelMetaOptions `json:"meta.channel,omitempty"` - WatchMeta *WatchMetaOptions `json:"meta.watch,omitempty"` // undocumented hack to add global redaction GlobalRedaction *GlobalRedactionOptions `json:"meta.redact,omitempty"` @@ -162,13 +161,6 @@ type ChannelMetaOptions struct { ChannelName string `json:"channel_name,omitempty"` } -// meta.watch -type WatchMetaOptions struct { - SpecShared `json:",inline,omitempty"` - WatchID string `json:"watch_id,omitempty"` - WatchName string `json:"watch_name,omitempty"` -} - // plugin.core options type CoreHostnameOptions struct {