package stacks import ( "context" "fmt" "os" "path/filepath" "github.com/pulumi/pulumi/sdk/v3/go/auto" "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "forge.dananglin.me.uk/code/dananglin/helix/internal/config" ) const ( dockerStackName string = "docker" ) type Previewer interface { Preview(ctx context.Context) error } type Updater interface { Update(ctx context.Context) error } type Destroyer interface { Destroy(ctx context.Context) error } func NewPreviewer(ctx context.Context, stack string, c config.Config) (Previewer, error) { var p Previewer var err error switch stack { case dockerStackName: p, err = newDockerStack(ctx, c.ProjectName, stack, c) if err != nil { return nil, fmt.Errorf("unable to initialise '%s' stack...\n%w", stack, err) } default: return nil, fmt.Errorf("unknown stack name '%s'", stack) } return p, nil } func NewUpdater(ctx context.Context, stack string, c config.Config) (Updater, error) { var u Updater var err error switch stack { case dockerStackName: u, err = newDockerStack(ctx, c.ProjectName, stack, c) if err != nil { return nil, fmt.Errorf("unable to initialise '%s' stack...\n%w", stack, err) } default: return nil, fmt.Errorf("unknown stack name '%s'", stack) } return u, nil } func NewDestroyer(ctx context.Context, stack string, c config.Config) (Destroyer, error) { var d Destroyer var err error switch stack { case dockerStackName: d, err = newDockerStack(ctx, c.ProjectName, stack, c) if err != nil { return nil, fmt.Errorf("unable to initialise '%s' stack...\n%w", stack, err) } default: return nil, fmt.Errorf("unknown stack name '%s'", stack) } return d, nil } func createOrSelectStack(ctx context.Context, projectName, stackName string, deployFunc pulumi.RunFunc) (auto.Stack, error) { var s auto.Stack opts, err := workspaceOptions(projectName, stackName) if err != nil { return s, fmt.Errorf("unable to create the workspace options...\n%w", err) } fmt.Printf("Creating/selecting stack (%s)...\n", stackName) s, err = auto.UpsertStackInlineSource(ctx, stackName, projectName, deployFunc, opts...) if err != nil { return s, fmt.Errorf("unable to create/select the stack...\n%w", err) } w := s.Workspace() fmt.Println("Installing the Docker plugin") if err = w.InstallPlugin(ctx, "docker", "v3.1.0"); err != nil { return s, fmt.Errorf("unable to install the docker plugin...\n%w", err) } fmt.Printf("Refreshing stack (%s)...\n", stackName) _, err = s.Refresh(ctx) if err != nil { return s, fmt.Errorf("unable to refresh the stack...\n%w", err) } return s, nil } func workspaceOptions(projectName, stackName string) ([]auto.LocalWorkspaceOption, error) { pulumiHome := auto.PulumiHome(filepath.Join(os.Getenv("HOME"), ".pulumi")) wd := filepath.Join(os.Getenv("HOME"), "Pulumi", "projects", projectName, "workspaces", stackName) fmt.Printf("Ensuring that %s exists...\n", wd) if err := os.MkdirAll(wd, 0750); err != nil { return nil, fmt.Errorf("unable to ensure that the directory exists...\n%w", err) } workDir := auto.WorkDir(wd) secretsProvider := auto.SecretsProvider("passphrase") backend := filepath.Join(os.Getenv("HOME"), "Pulumi", "projects", projectName, "stacks") if err := os.MkdirAll(backend, 0750); err != nil { return nil, fmt.Errorf("unable to create the backend directory...\n%w", err) } project := auto.Project(workspace.Project{ Name: tokens.PackageName(projectName), Runtime: workspace.NewProjectRuntimeInfo("go", nil), Backend: &workspace.ProjectBackend{ URL: fmt.Sprintf("file://%s", backend), }, }) opts := []auto.LocalWorkspaceOption{pulumiHome, secretsProvider, project, workDir} return opts, nil }