-
Notifications
You must be signed in to change notification settings - Fork 3
/
load_workspace.go
149 lines (133 loc) · 5.08 KB
/
load_workspace.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package workspace
import (
"context"
"errors"
"fmt"
"log/slog"
"time"
"github.com/spf13/viper"
"github.com/turbot/pipe-fittings/v2/constants"
"github.com/turbot/pipe-fittings/v2/credential"
"github.com/turbot/pipe-fittings/v2/error_helpers"
"github.com/turbot/pipe-fittings/v2/inputvars"
"github.com/turbot/pipe-fittings/v2/modconfig"
"github.com/turbot/pipe-fittings/v2/statushooks"
"github.com/turbot/pipe-fittings/v2/steampipeconfig"
"github.com/turbot/terraform-components/terraform"
)
type LoadWorkspaceOption func(*LoadWorkspaceConfig)
type LoadWorkspaceConfig struct {
credentials map[string]credential.Credential
integrations map[string]modconfig.Integration
notifiers map[string]modconfig.Notifier
blockTypeInclusions []string
validateVariables bool
skipResourceLoadIfNoModfile bool
}
func newLoadWorkspaceConfig() *LoadWorkspaceConfig {
return &LoadWorkspaceConfig{
credentials: make(map[string]credential.Credential),
integrations: make(map[string]modconfig.Integration),
notifiers: make(map[string]modconfig.Notifier),
validateVariables: true,
}
}
func WithCredentials(credentials map[string]credential.Credential) LoadWorkspaceOption {
return func(m *LoadWorkspaceConfig) {
m.credentials = credentials
}
}
func WithIntegrations(integrations map[string]modconfig.Integration) LoadWorkspaceOption {
return func(m *LoadWorkspaceConfig) {
m.integrations = integrations
}
}
func WithNotifiers(notifiers map[string]modconfig.Notifier) LoadWorkspaceOption {
return func(m *LoadWorkspaceConfig) {
m.notifiers = notifiers
}
}
func WithBlockType(blockTypeInclusions []string) LoadWorkspaceOption {
return func(m *LoadWorkspaceConfig) {
m.blockTypeInclusions = blockTypeInclusions
}
}
func WithVariableValidation(enabled bool) LoadWorkspaceOption {
return func(m *LoadWorkspaceConfig) {
m.validateVariables = enabled
}
}
// TODO this is only needed as Pipe fittings tests rely on loading workspaces without modfiles
func WithSkipResourceLoadIfNoModfile(enabled bool) LoadWorkspaceOption {
return func(m *LoadWorkspaceConfig) {
m.skipResourceLoadIfNoModfile = enabled
}
}
func LoadWorkspacePromptingForVariables(ctx context.Context, workspacePath string, opts ...LoadWorkspaceOption) (*Workspace, error_helpers.ErrorAndWarnings) {
// do not load resources if there is no modfile
opts = append(opts, WithSkipResourceLoadIfNoModfile(true))
t := time.Now()
defer func() {
slog.Debug("Workspace load complete", "duration (ms)", time.Since(t).Milliseconds())
}()
w, errAndWarnings := Load(ctx, workspacePath, opts...)
if errAndWarnings.GetError() == nil {
return w, errAndWarnings
}
var missingVariablesError steampipeconfig.MissingVariableError
ok := errors.As(errAndWarnings.GetError(), &missingVariablesError)
// if there was an error which is NOT a MissingVariableError, return it
if !ok {
return nil, errAndWarnings
}
// if there are missing transitive dependency variables, fail as we do not prompt for these
if len(missingVariablesError.MissingTransitiveVariables) > 0 {
return nil, errAndWarnings
}
// if interactive input is disabled, return the missing variables error
if !viper.GetBool(constants.ArgInput) {
return nil, error_helpers.NewErrorsAndWarning(missingVariablesError)
}
// so we have missing variables - prompt for them
// first hide spinner if it is there
statushooks.Done(ctx)
if err := promptForMissingVariables(ctx, missingVariablesError.MissingVariables, workspacePath); err != nil {
slog.Debug("Interactive variables prompting returned error %v", err)
return nil, error_helpers.NewErrorsAndWarning(err)
}
// ok we should have all variables now - reload workspace
return Load(ctx, workspacePath, opts...)
}
func promptForMissingVariables(ctx context.Context, missingVariables []*modconfig.Variable, workspacePath string) error {
fmt.Println() //nolint:forbidigo // UI formatting
fmt.Println("Variables defined with no value set.") //nolint:forbidigo // UI formatting
for _, v := range missingVariables {
variableName := v.ShortName
variableDisplayName := fmt.Sprintf("var.%s", v.ShortName)
// if this variable is NOT part of the workspace mod, add the mod name to the variable name
if v.Mod.ModPath != workspacePath {
variableDisplayName = fmt.Sprintf("%s.var.%s", v.ModName, v.ShortName)
variableName = fmt.Sprintf("%s.%s", v.ModName, v.ShortName)
}
r, err := promptForVariable(ctx, variableDisplayName, v.GetDescription())
if err != nil {
return err
}
addInteractiveVariableToViper(variableName, r)
}
return nil
}
func promptForVariable(ctx context.Context, name, description string) (string, error) {
uiInput := &inputvars.UIInput{}
rawValue, err := uiInput.Input(ctx, &terraform.InputOpts{
Id: name,
Query: name,
Description: description,
})
return rawValue, err
}
func addInteractiveVariableToViper(name string, rawValue string) {
varMap := viper.GetStringMap(constants.ConfigInteractiveVariables)
varMap[name] = rawValue
viper.Set(constants.ConfigInteractiveVariables, varMap)
}