From af1603a1cf4662e030a3db3df5967d40c5d03738 Mon Sep 17 00:00:00 2001 From: tomasmik Date: Fri, 25 Aug 2023 14:51:57 +0300 Subject: [PATCH] Add a way to explore stack resources --- internal/cmd/stack/resources.go | 156 +++++++++++++++++++++++++++ internal/cmd/stack/stack.go | 16 +++ internal/cmd/stack/stack_selector.go | 2 +- main.go | 2 +- 4 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 internal/cmd/stack/resources.go diff --git a/internal/cmd/stack/resources.go b/internal/cmd/stack/resources.go new file mode 100644 index 0000000..18fa7c2 --- /dev/null +++ b/internal/cmd/stack/resources.go @@ -0,0 +1,156 @@ +package stack + +import ( + "github.com/pkg/errors" + "github.com/shurcooL/graphql" + "github.com/spacelift-io/spacectl/internal/cmd" + "github.com/spacelift-io/spacectl/internal/cmd/authenticated" + "github.com/urfave/cli/v2" +) + +func resourcesList(cliCtx *cli.Context) error { + stackID, err := getStackID(cliCtx) + if err != nil { + if !errors.Is(err, errNoStackFound) { + return err + } + + return resourcesListAllStacks(cliCtx) + } + + return resourcesListOneStack(cliCtx, stackID) +} + +func resourcesListOneStack(cliCtx *cli.Context, id string) error { + var query struct { + Stack stackWithResources `graphql:"stack(id: $id)"` + } + + variables := map[string]any{"id": graphql.ID(id)} + if err := authenticated.Client.Query(cliCtx.Context, &query, variables); err != nil { + return errors.Wrap(err, "failed to query one stack") + } + + return cmd.OutputJSON(query.Stack) +} + +func resourcesListAllStacks(cliCtx *cli.Context) error { + var query struct { + Stacks []stackWithResources `graphql:"stacks" json:"stacks,omitempty"` + } + + if err := authenticated.Client.Query(cliCtx.Context, &query, map[string]interface{}{}); err != nil { + return errors.Wrap(err, "failed to query list of stacks") + } + + return cmd.OutputJSON(query.Stacks) +} + +type stackWithResources struct { + ID string `graphql:"id" json:"id"` + Labels []string `graphql:"labels" json:"labels"` + Space string `graphql:"space" json:"space"` + Name string `graphql:"name" json:"name"` + + Entities []managedEntity `graphql:"entities" json:"entities"` +} + +type managedEntity struct { + ID string `graphql:"id" json:"id,omitempty"` + Address string `graphql:"address" json:"address,omitempty"` + Creator *run `graphql:"creator" json:"creator,omitempty"` + Drifted *int64 `graphql:"drifted" json:"drifted,omitempty"` + Name string `graphql:"name" json:"name,omitempty"` + Parent string `graphql:"parent" json:"parent,omitempty"` + ThirdPartyMetadata *string `graphql:"thirdPartyMetadata" json:"third_party_metadata,omitempty"` + Type string `graphql:"type" json:"type,omitempty"` + Updater *run `graphql:"updater" json:"updater,omitempty"` + Vendor entityVendor `graphql:"vendor" json:"vendor,omitempty"` +} + +type run struct { + ID string `graphql:"id" json:"id,omitempty"` +} + +type entityVendor struct { + EntityVendorAnsible struct { + Ansible *ansibleEntity `graphql:"ansible" json:"ansible,omitempty"` + } `graphql:"... on EntityVendorAnsible" json:"entity_vendor_ansible,omitempty"` + EntityVendorCloudFormation struct { + CloudFormation *cloudFormationEntity `graphql:"cloudFormation" json:"cloudFormation,omitempty"` + } `graphql:"... on EntityVendorCloudFormation" json:"entity_vendor_cloud_formation,omitempty"` + EntityVendorKubernetes struct { + Kubernetes *kubernetesEntity `graphql:"kubernetes" json:"kubernetes,omitempty"` + } `graphql:"... on EntityVendorKubernetes" json:"entity_vendor_kubernetes,omitempty"` + EntityVendorPulumi struct { + Pulumi *pulumiEntity `graphql:"pulumi" json:"pulumi,omitempty"` + } `graphql:"... on EntityVendorPulumi" json:"entity_vendor_pulumi,omitempty"` + EntityVendorTerraform struct { + Terraform *terraformEntity `graphql:"terraform" json:"terraform,omitempty"` + } `graphql:"... on EntityVendorTerraform" json:"entity_vendor_terraform,omitempty"` +} + +type ansibleEntity struct { + AnsibleResource struct { + Data string `graphql:"data" json:"data,omitempty"` + } `graphql:"... on AnsibleResource" json:"ansible_resource,omitempty"` +} + +type cloudFormationEntity struct { + CloudFormationResource struct { + LogicalResourceID string `graphql:"logicalResourceId" json:"logical_resource_id,omitempty"` + PhysicalResourceID string `graphql:"physicalResourceId" json:"physical_resource_id,omitempty"` + Template string `graphql:"template" json:"template,omitempty"` + } `graphql:"... on CloudFormationResource" json:"cloud_formation_resource,omitempty"` + CloudFormationOutput struct { + Description *string `graphql:"description" json:"description,omitempty"` + Export *string `graphql:"export" json:"export,omitempty"` + Value string `graphql:"value" json:"value,omitempty"` + } `graphql:"... on CloudFormationOutput" json:"cloud_formation_output,omitempty"` +} + +type kubernetesEntity struct { + KubernetesResource struct { + Data string `graphql:"data" json:"data,omitempty"` + } `graphql:"... on KubernetesResource" json:"kubernetes_resource,omitempty"` + KubernetesRoot struct { + Phantom bool `graphql:"phantom" json:"phantom,omitempty"` + } `graphql:"... on KubernetesRoot" json:"kubernetes_root,omitempty"` +} + +type pulumiEntity struct { + PulumiOutput struct { + Sensitive bool `graphql:"sensitive" json:"sensitive,omitempty"` + Value *string `graphql:"value" json:"value,omitempty"` + Hash string `graphql:"hash" json:"hash,omitempty"` + } `graphql:"... on PulumiOutput" json:"pulumi_output,omitempty"` + PulumiStack struct { + Phantom bool `graphql:"phantom" json:"phantom,omitempty"` + } `graphql:"... on PulumiStack" json:"pulumi_stack,omitempty"` + PulumiResource struct { + Urn string `graphql:"urn" json:"urn,omitempty"` + ID string `graphql:"id" json:"id,omitempty"` + Provider string `graphql:"provider" json:"provider,omitempty"` + Parent string `graphql:"parent" json:"parent,omitempty"` + Outputs string `graphql:"outputs" json:"outputs,omitempty"` + } `graphql:"... on PulumiResource " json:"pulumi_resource,omitempty"` +} + +type terraformEntity struct { + TerraformResource struct { + Address string `graphql:"address" json:"address,omitempty"` + Mode string `graphql:"mode" json:"mode,omitempty"` + Module string `graphql:"module" json:"module,omitempty"` + Provider string `graphql:"provider" json:"provider,omitempty"` + Tainted bool `graphql:"tainted" json:"tainted,omitempty"` + Values string `graphql:"values" json:"values,omitempty"` + } `graphql:"... on TerraformResource" json:"terraform_resource,omitempty"` + TerraformModule struct { + Phantom bool `graphql:"phantom" json:"phantom,omitempty"` + } `graphql:"... on TerraformModule" json:"terraform_module,omitempty"` + TerraformOutput struct { + Sensitive bool `graphql:"sensitive" json:"sensitive,omitempty"` + Value *string `graphql:"value" json:"value,omitempty"` + Hash string `graphql:"hash" json:"hash,omitempty"` + } `graphql:"... on TerraformOutput" json:"terraform_output,omitempty"` +} diff --git a/internal/cmd/stack/stack.go b/internal/cmd/stack/stack.go index 8e6290f..05a332b 100644 --- a/internal/cmd/stack/stack.go +++ b/internal/cmd/stack/stack.go @@ -328,6 +328,22 @@ func Command() *cli.Command { Before: authenticated.Ensure, ArgsUsage: cmd.EmptyArgsUsage, }, + { + Name: "resources", + Usage: "Manage and view resources for stacks", + Subcommands: []*cli.Command{ + { + Name: "list", + Usage: "Sets an environment variable.", + Flags: []cli.Flag{ + flagStackID, + }, + Action: resourcesList, + Before: authenticated.Ensure, + ArgsUsage: cmd.EmptyArgsUsage, + }, + }, + }, }, } } diff --git a/internal/cmd/stack/stack_selector.go b/internal/cmd/stack/stack_selector.go index 467fad1..4187858 100644 --- a/internal/cmd/stack/stack_selector.go +++ b/internal/cmd/stack/stack_selector.go @@ -38,7 +38,7 @@ func getStackID(cliCtx *cli.Context) (string, error) { }, true) if err != nil { if errors.Is(err, errNoStackFound) { - return "", errors.New("no --id flag was provided and stack could not be found by searching the current directory") + return "", fmt.Errorf("%w: no --id flag was provided and stack could not be found by searching the current directory", err) } return "", err diff --git a/main.go b/main.go index 82cb9db..34ec509 100644 --- a/main.go +++ b/main.go @@ -10,7 +10,7 @@ import ( "github.com/spacelift-io/spacectl/internal/cmd/module" "github.com/spacelift-io/spacectl/internal/cmd/profile" "github.com/spacelift-io/spacectl/internal/cmd/provider" - "github.com/spacelift-io/spacectl/internal/cmd/run_external_dependency" + runexternaldependency "github.com/spacelift-io/spacectl/internal/cmd/run_external_dependency" "github.com/spacelift-io/spacectl/internal/cmd/stack" versioncmd "github.com/spacelift-io/spacectl/internal/cmd/version" "github.com/spacelift-io/spacectl/internal/cmd/whoami"