Skip to content

Commit 557968d

Browse files
add --all flag to plural destroy (#359)
* add `--all` flag to plural destroy This will make a full cluster destroy require an implicit invocation which can prevent mistyping * try to enable services when some are missing * Add ability to ignore preflight checks
1 parent 2a39f5c commit 557968d

7 files changed

Lines changed: 70 additions & 15 deletions

File tree

.github/workflows/e2e.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
- run: go test -v -race ./pkg/test/e2e/... -tags="e2e"
3131
- run: |
3232
cd $HOME/test
33-
plural destroy --force --commit=""
33+
plural destroy --force --all --commit=""
3434
env:
3535
PLURAL_DESTROY_CONFIRM: true
3636
PLURAL_DESTROY_AFFIRM_UNINSTALL_APPS: true

cmd/plural/deploy.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ func (p *Plural) build(c *cli.Context) error {
117117
}
118118

119119
func (p *Plural) doBuild(installation *api.Installation, force bool) error {
120-
p.InitPluralClient()
121120
repoName := installation.Repository.Name
122121
fmt.Printf("Building workspace for %s\n", repoName)
123122

@@ -360,13 +359,16 @@ func (p *Plural) destroy(c *cli.Context) error {
360359
repoName := c.Args().Get(0)
361360
repoRoot, err := git.Root()
362361
force := c.Bool("force")
362+
all := c.Bool("all")
363363
if err != nil {
364364
return err
365365
}
366366

367367
infix := "this workspace"
368368
if repoName != "" {
369369
infix = repoName
370+
} else if !all {
371+
return fmt.Errorf("you must either specify an individual application or `--all` to destroy the entire workspace")
370372
}
371373

372374
if !force && !confirm(fmt.Sprintf("Are you sure you want to destroy %s?", infix), "PLURAL_DESTROY_CONFIRM") {

cmd/plural/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (p *Plural) handleInit(c *cli.Context) error {
5151
}
5252

5353
prov, err := runPreflights()
54-
if err != nil {
54+
if err != nil && !c.Bool("ignore-preflights") {
5555
return err
5656
}
5757

cmd/plural/plural.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,10 @@ func (p *Plural) getCommands() []cli.Command {
235235
Name: "force",
236236
Usage: "use force push when pushing to git",
237237
},
238+
cli.BoolFlag{
239+
Name: "all",
240+
Usage: "tear down the entire cluster gracefully in one go",
241+
},
238242
},
239243
Action: tracked(latestVersion(owned(upstreamSynced(p.destroy))), "cli.destroy"),
240244
},
@@ -250,6 +254,10 @@ func (p *Plural) getCommands() []cli.Command {
250254
Name: "service-account",
251255
Usage: "email for the service account you'd like to use for this workspace",
252256
},
257+
cli.BoolFlag{
258+
Name: "ignore-preflights",
259+
Usage: "whether to ignore preflight check failures prior to init",
260+
},
253261
},
254262
Action: tracked(latestVersion(p.handleInit), "cli.init"),
255263
},

pkg/provider/gcp.go

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -364,32 +364,51 @@ func (gcp *GCPProvider) validateEnabled() error {
364364
return errEnabled
365365
}
366366

367-
wrapped := func(name string) string {
368-
return fmt.Sprintf("projects/%s/services/%s", proj.ProjectId, name)
369-
}
367+
services := algorithms.Map([]string{
368+
"serviceusage.googleapis.com",
369+
"cloudresourcemanager.googleapis.com",
370+
"container.googleapis.com",
371+
}, func(name string) string { return fmt.Sprintf("projects/%s/services/%s", proj.ProjectId, name) })
372+
parent := fmt.Sprintf("projects/%s", proj.ProjectId)
370373
req := &serviceusagepb.BatchGetServicesRequest{
371-
Parent: fmt.Sprintf("projects/%s", proj.ProjectId),
372-
Names: []string{
373-
wrapped("serviceusage.googleapis.com"),
374-
wrapped("cloudresourcemanager.googleapis.com"),
375-
wrapped("container.googleapis.com"),
376-
},
374+
Parent: parent,
375+
Names: services,
377376
}
378377
resp, err := c.BatchGetServices(ctx, req)
379378
if err != nil {
380379
utils.LogError().Println(err)
381380
return errEnabled
382381
}
383382

384-
for _, svc := range resp.Services {
385-
if svc.State != serviceusagepb.State_ENABLED {
386-
utils.LogError().Printf("the service state %v != %v", svc.State, serviceusagepb.State_ENABLED)
383+
missing := algorithms.Filter(resp.Services, func(svc *serviceusagepb.Service) bool {
384+
return svc.State != serviceusagepb.State_ENABLED
385+
})
386+
387+
if len(missing) > 0 {
388+
services := algorithms.Map(missing, func(svc *serviceusagepb.Service) string { return svc.Name })
389+
enableReq := &serviceusagepb.BatchEnableServicesRequest{
390+
Parent: parent,
391+
ServiceIds: services,
392+
}
393+
utils.LogError().Printf("Attempting to enable services %v", services)
394+
if err := tryToEnableServices(ctx, c, enableReq); err != nil {
387395
return errEnabled
388396
}
389397
}
398+
390399
return nil
391400
}
392401

402+
func tryToEnableServices(ctx context.Context, client *serviceusage.Client, req *serviceusagepb.BatchEnableServicesRequest) (err error) {
403+
op, err := client.BatchEnableServices(ctx, req)
404+
if err != nil {
405+
return
406+
}
407+
408+
_, err = op.Wait(ctx)
409+
return
410+
}
411+
393412
func (gcp *GCPProvider) Permissions() (permissions.Checker, error) {
394413
proj, err := gcp.getProject()
395414
if err != nil {

pkg/scaffold/terraform.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"golang.org/x/mod/semver"
1414

1515
"github.com/pluralsh/plural/pkg/api"
16+
"github.com/pluralsh/plural/pkg/config"
1617
"github.com/pluralsh/plural/pkg/template"
1718
"github.com/pluralsh/plural/pkg/utils"
1819
"github.com/pluralsh/plural/pkg/utils/pathing"
@@ -122,6 +123,7 @@ func (scaffold *Scaffold) handleTerraform(wk *wkspace.Workspace) error {
122123
"Namespace": wk.Config.Namespace(repo.Name),
123124
"Region": wk.Provider.Region(),
124125
"Context": wk.Provider.Context(),
126+
"Config": config.Read(),
125127
"Applications": apps,
126128
}
127129
if err := tmpl.Execute(&buf, values); err != nil {

pkg/server/setup.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"github.com/pluralsh/plural/pkg/provider"
1515
"github.com/pluralsh/plural/pkg/utils"
1616
"github.com/pluralsh/plural/pkg/wkspace"
17+
18+
"github.com/pluralsh/polly/algorithms"
1719
)
1820

1921
func toConfig(setup *SetupRequest) *config.Config {
@@ -151,6 +153,11 @@ func setupCli(c *gin.Context) error {
151153
if err != nil {
152154
return err
153155
}
156+
157+
if err := runPreflights(prov); err != nil {
158+
return err
159+
}
160+
154161
missing, err := prov.Permissions()
155162
if err != nil {
156163
return err
@@ -161,3 +168,20 @@ func setupCli(c *gin.Context) error {
161168
c.JSON(http.StatusOK, gin.H{"success": true, "missing": missing})
162169
return nil
163170
}
171+
172+
func runPreflights(prov provider.Provider) error {
173+
// run only relevant preflights
174+
preflights := []*provider.Preflight{}
175+
if prov.Name() == provider.GCP {
176+
preflights = algorithms.Filter(prov.Preflights(), func(pre *provider.Preflight) bool {
177+
return pre.Name == "Enabled Services"
178+
})
179+
}
180+
181+
for _, pre := range preflights {
182+
if err := pre.Validate(); err != nil {
183+
return err
184+
}
185+
}
186+
return nil
187+
}

0 commit comments

Comments
 (0)