forked from vmware-archive/fly
/
atc_config.go
223 lines (179 loc) · 5.82 KB
/
atc_config.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package setpipelinehelpers
import (
"fmt"
"io/ioutil"
"os"
"github.com/concourse/atc"
"github.com/concourse/atc/web"
"github.com/concourse/fly/commands/internal/displayhelpers"
"github.com/concourse/fly/template"
"github.com/concourse/go-concourse/concourse"
"github.com/mitchellh/mapstructure"
"github.com/onsi/gomega/gexec"
"github.com/tedsuo/rata"
"github.com/vito/go-interact/interact"
"gopkg.in/yaml.v2"
)
type ATCConfig struct {
PipelineName string
Team concourse.Team
WebRequestGenerator *rata.RequestGenerator
SkipInteraction bool
}
func (atcConfig ATCConfig) ApplyConfigInteraction() bool {
if atcConfig.SkipInteraction {
return true
}
confirm := false
err := interact.NewInteraction("apply configuration?").Resolve(&confirm)
if err != nil {
return false
}
return confirm
}
func (atcConfig ATCConfig) Set(configPath atc.PathFlag, templateVariables template.Variables, templateVariablesFiles []atc.PathFlag) error {
newConfig := atcConfig.newConfig(configPath, templateVariablesFiles, templateVariables)
existingConfig, _, existingConfigVersion, _, err := atcConfig.Team.PipelineConfig(atcConfig.PipelineName)
errorMessages := []string{}
if err != nil {
if configError, ok := err.(concourse.PipelineConfigError); ok {
errorMessages = configError.ErrorMessages
} else {
return err
}
}
diff(existingConfig, newConfig)
if len(errorMessages) > 0 {
atcConfig.showPipelineConfigErrors(errorMessages)
}
if !atcConfig.ApplyConfigInteraction() {
displayhelpers.Failf("bailing out")
}
created, updated, warnings, err := atcConfig.Team.CreateOrUpdatePipelineConfig(
atcConfig.PipelineName,
existingConfigVersion,
newConfig,
)
if err != nil {
return err
}
if len(warnings) > 0 {
atcConfig.showWarnings(warnings)
}
atcConfig.showHelpfulMessage(created, updated)
return nil
}
func (atcConfig ATCConfig) newConfig(configPath atc.PathFlag, templateVariablesFiles []atc.PathFlag, templateVariables template.Variables) atc.Config {
configFile, err := ioutil.ReadFile(string(configPath))
if err != nil {
displayhelpers.FailWithErrorf("could not read config file", err)
}
var resultVars template.Variables
for _, path := range templateVariablesFiles {
fileVars, templateErr := template.LoadVariablesFromFile(string(path))
if templateErr != nil {
displayhelpers.FailWithErrorf("failed to load variables from file (%s)", templateErr, string(path))
}
resultVars = resultVars.Merge(fileVars)
}
resultVars = resultVars.Merge(templateVariables)
configFile, err = template.Evaluate(configFile, resultVars)
if err != nil {
displayhelpers.FailWithErrorf("failed to evaluate variables into template", err)
}
var configStructure interface{}
err = yaml.Unmarshal(configFile, &configStructure)
if err != nil {
displayhelpers.FailWithErrorf("failed to unmarshal configStructure", err)
}
var newConfig atc.Config
msConfig := &mapstructure.DecoderConfig{
Result: &newConfig,
WeaklyTypedInput: true,
DecodeHook: mapstructure.ComposeDecodeHookFunc(
atc.SanitizeDecodeHook,
atc.VersionConfigDecodeHook,
),
}
decoder, err := mapstructure.NewDecoder(msConfig)
if err != nil {
displayhelpers.FailWithErrorf("failed to construct decoder", err)
}
if err := decoder.Decode(configStructure); err != nil {
displayhelpers.FailWithErrorf("failed to decode config", err)
}
return newConfig
}
func (atcConfig ATCConfig) showPipelineConfigErrors(errorMessages []string) {
fmt.Fprintln(os.Stderr, "")
displayhelpers.PrintWarningHeader()
fmt.Fprintln(os.Stderr, "Error loading existing config:")
for _, errorMessage := range errorMessages {
fmt.Fprintf(os.Stderr, " - %s\n", errorMessage)
}
fmt.Fprintln(os.Stderr, "")
}
func (atcConfig ATCConfig) showWarnings(warnings []concourse.ConfigWarning) {
fmt.Fprintln(os.Stderr, "")
displayhelpers.PrintDeprecationWarningHeader()
for _, warning := range warnings {
fmt.Fprintf(os.Stderr, " - %s\n", warning.Message)
}
fmt.Fprintln(os.Stderr, "")
}
func (atcConfig ATCConfig) showHelpfulMessage(created bool, updated bool) {
if updated {
fmt.Println("configuration updated")
} else if created {
pipelineWebReq, _ := atcConfig.WebRequestGenerator.CreateRequest(
web.Pipeline,
rata.Params{
"pipeline": atcConfig.PipelineName,
"team_name": atcConfig.Team.Name(),
},
nil,
)
fmt.Println("pipeline created!")
pipelineURL := pipelineWebReq.URL
// don't show username and password
pipelineURL.User = nil
fmt.Printf("you can view your pipeline here: %s\n", pipelineURL.String())
fmt.Println("")
fmt.Println("the pipeline is currently paused. to unpause, either:")
fmt.Println(" - run the unpause-pipeline command")
fmt.Println(" - click play next to the pipeline in the web ui")
} else {
panic("Something really went wrong!")
}
}
func diff(existingConfig atc.Config, newConfig atc.Config) {
indent := gexec.NewPrefixedWriter(" ", os.Stdout)
groupDiffs := diffIndices(GroupIndex(existingConfig.Groups), GroupIndex(newConfig.Groups))
if len(groupDiffs) > 0 {
fmt.Println("groups:")
for _, diff := range groupDiffs {
diff.Render(indent, "group")
}
}
resourceDiffs := diffIndices(ResourceIndex(existingConfig.Resources), ResourceIndex(newConfig.Resources))
if len(resourceDiffs) > 0 {
fmt.Println("resources:")
for _, diff := range resourceDiffs {
diff.Render(indent, "resource")
}
}
resourceTypeDiffs := diffIndices(ResourceTypeIndex(existingConfig.ResourceTypes), ResourceTypeIndex(newConfig.ResourceTypes))
if len(resourceTypeDiffs) > 0 {
fmt.Println("resource types:")
for _, diff := range resourceTypeDiffs {
diff.Render(indent, "resource type")
}
}
jobDiffs := diffIndices(JobIndex(existingConfig.Jobs), JobIndex(newConfig.Jobs))
if len(jobDiffs) > 0 {
fmt.Println("jobs:")
for _, diff := range jobDiffs {
diff.Render(indent, "job")
}
}
}