-
Notifications
You must be signed in to change notification settings - Fork 787
/
create_env.go
328 lines (289 loc) · 11.6 KB
/
create_env.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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
package cmd
import (
"strings"
"github.com/jenkins-x/jx/pkg/auth"
"github.com/jenkins-x/jx/pkg/jenkinsfile"
"github.com/jenkins-x/jx/pkg/kube/serviceaccount"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"fmt"
v1 "github.com/jenkins-x/jx/pkg/apis/jenkins.io/v1"
"github.com/jenkins-x/jx/pkg/config"
"github.com/jenkins-x/jx/pkg/gits"
"github.com/jenkins-x/jx/pkg/jx/cmd/templates"
"github.com/jenkins-x/jx/pkg/kube"
"github.com/jenkins-x/jx/pkg/log"
"github.com/jenkins-x/jx/pkg/prow"
"github.com/jenkins-x/jx/pkg/util"
)
const (
optionPullSecrets = "pull-secrets"
)
var (
env_description = `
An Environment maps to a Kubernetes cluster and namespace and is a place that your team's applications can be promoted to via Continous Delivery.
You can optionally use GitOps to manage the configuration of an Environment by storing all configuration in a Git repository and then only changing it via Pull Requests and CI/CD.
For more documentation on Environments see: [https://jenkins-x.io/about/features/#environments](https://jenkins-x.io/about/features/#environments)
`
create_env_long = templates.LongDesc(`
Creates a new Environment
` + env_description + `
`)
create_env_example = templates.Examples(`
# Create a new Environment, prompting for the required data
jx create env
# Creates a new Environment passing in the required data on the command line
jx create env -n prod -l Production --no-gitops --namespace my-prod
`)
)
// CreateEnvOptions the options for the create env command
type CreateEnvOptions struct {
CreateOptions
Options v1.Environment
HelmValuesConfig config.HelmValuesConfig
PromotionStrategy string
NoGitOps bool
NoDevNamespaceInit bool
Prow bool
GitOpsMode bool
ForkEnvironmentGitRepo string
EnvJobCredentials string
GitRepositoryOptions gits.GitRepositoryOptions
Prefix string
BranchPattern string
Vault bool
PullSecrets string
}
// NewCmdCreateEnv creates a command object for the "create" command
func NewCmdCreateEnv(commonOpts *CommonOptions) *cobra.Command {
options := &CreateEnvOptions{
HelmValuesConfig: config.HelmValuesConfig{
ExposeController: &config.ExposeController{},
},
CreateOptions: CreateOptions{
CommonOptions: commonOpts,
},
}
cmd := &cobra.Command{
Use: "environment",
Short: "Create a new Environment which is used to promote your Team's Applications via Continuous Delivery",
Aliases: []string{"env"},
Long: create_env_long,
Example: create_env_example,
Run: func(cmd *cobra.Command, args []string) {
options.Cmd = cmd
options.Args = args
err := options.Run()
CheckErr(err)
},
}
//addCreateAppFlags(cmd, &options.CreateOptions)
cmd.Flags().StringVarP(&options.Options.Name, kube.OptionName, "n", "", "The Environment resource name. Must follow the Kubernetes name conventions like Services, Namespaces")
cmd.Flags().StringVarP(&options.Options.Spec.Label, "label", "l", "", "The Environment label which is a descriptive string like 'Production' or 'Staging'")
cmd.Flags().StringVarP(&options.Options.Spec.Namespace, kube.OptionNamespace, "s", "", "The Kubernetes namespace for the Environment")
cmd.Flags().StringVarP(&options.Options.Spec.Cluster, "cluster", "c", "", "The Kubernetes cluster for the Environment. If blank and a namespace is specified assumes the current cluster")
cmd.Flags().StringVarP(&options.Options.Spec.Source.URL, "git-url", "g", "", "The Git clone URL for the source code for GitOps based Environments")
cmd.Flags().StringVarP(&options.Options.Spec.Source.Ref, "git-ref", "r", "", "The Git repo reference for the source code for GitOps based Environments")
cmd.Flags().StringVarP(&options.GitRepositoryOptions.Owner, "git-owner", "", "", "Git organisation / owner")
cmd.Flags().Int32VarP(&options.Options.Spec.Order, "order", "o", 100, "The order weighting of the Environment so that they can be sorted by this order before name")
cmd.Flags().StringVarP(&options.Prefix, "prefix", "", "jx", "Environment repo prefix, your Git repo will be of the form 'environment-$prefix-$envName'")
cmd.Flags().StringVarP(&options.PromotionStrategy, "promotion", "p", "", "The promotion strategy")
cmd.Flags().StringVarP(&options.ForkEnvironmentGitRepo, "fork-git-repo", "f", kube.DefaultEnvironmentGitRepoURL, "The Git repository used as the fork when creating new Environment Git repos")
cmd.Flags().StringVarP(&options.EnvJobCredentials, "env-job-credentials", "", "", "The Jenkins credentials used by the GitOps Job for this environment")
cmd.Flags().StringVarP(&options.BranchPattern, "branches", "", "", "The branch pattern for branches to trigger CI/CD pipelines on the environment Git repository")
cmd.Flags().BoolVarP(&options.NoGitOps, "no-gitops", "x", false, "Disables the use of GitOps on the environment so that promotion is implemented by directly modifying the resources via helm instead of using a Git repository")
cmd.Flags().BoolVarP(&options.Prow, "prow", "", false, "Install and use Prow for environment promotion")
cmd.Flags().BoolVarP(&options.Vault, "vault", "", false, "Sets up a Hashicorp Vault for storing secrets during the cluster creation")
cmd.Flags().StringVarP(&options.PullSecrets, optionPullSecrets, "", "", "A list of Kubernetes secret names that will be attached to the service account (e.g. foo, bar, baz)")
addGitRepoOptionsArguments(cmd, &options.GitRepositoryOptions)
options.HelmValuesConfig.AddExposeControllerValues(cmd, false)
return cmd
}
// Run implements the command
func (o *CreateEnvOptions) Run() error {
args := o.Args
if len(args) > 0 && o.Options.Name == "" {
o.Options.Name = args[0]
}
jxClient, ns, err := o.JXClientAndDevNamespace()
if err != nil {
return err
}
kubeClient, err := o.KubeClient()
if err != nil {
return err
}
envDir, err := util.EnvironmentsDir()
if err != nil {
return err
}
authConfigSvc, err := o.CreateGitAuthConfigService()
if err != nil {
return err
}
prowFlag := o.Prow
var devEnv *v1.Environment
if o.GitOpsMode {
err = o.ModifyDevEnvironment(func(env *v1.Environment) error {
devEnv = env
return nil
})
if err != nil {
return err
}
} else {
devEnv, err = kube.EnsureDevEnvironmentSetup(jxClient, ns)
if err != nil {
return err
}
prowFlag, err = o.isProw()
if err != nil {
return err
}
if prowFlag && !o.Prow {
o.Prow = true
}
}
if o.Prow {
// lets make sure we have the prow enabled
err = o.ModifyDevEnvironment(func(env *v1.Environment) error {
env.Spec.TeamSettings.PromotionEngine = v1.PromotionEngineProw
return nil
})
if err != nil {
return err
}
}
env := v1.Environment{}
o.Options.Spec.PromotionStrategy = v1.PromotionStrategyType(o.PromotionStrategy)
gitProvider, err := kube.CreateEnvironmentSurvey(o.BatchMode, authConfigSvc, devEnv, &env, &o.Options, o.ForkEnvironmentGitRepo, ns,
jxClient, kubeClient, envDir, &o.GitRepositoryOptions, o.HelmValuesConfig, o.Prefix, o.Git(), o.In, o.Out, o.Err)
if err != nil {
return err
}
err = o.ModifyEnvironment(env.Name, func(env2 *v1.Environment) error {
env2.Name = env.Name
env2.Spec = env.Spec
// lets copy across any labels or annotations
if env2.Annotations == nil {
env2.Annotations = map[string]string{}
}
if env2.Labels == nil {
env2.Labels = map[string]string{}
}
for k, v := range env.Annotations {
env2.Annotations[k] = v
}
for k, v := range env.Labels {
env2.Labels[k] = v
}
return nil
})
log.Infof("Created environment %s\n", util.ColorInfo(env.Name))
if !o.GitOpsMode {
err = kube.EnsureEnvironmentNamespaceSetup(kubeClient, jxClient, &env, ns)
if err != nil {
return err
}
}
gitURL := env.Spec.Source.URL
gitInfo, err := gits.ParseGitURL(gitURL)
if err != nil {
return err
}
if o.Prow {
repo := fmt.Sprintf("%s/%s", gitInfo.Organisation, gitInfo.Name)
err = prow.AddEnvironment(kubeClient, []string{repo}, devEnv.Spec.Namespace, env.Spec.Namespace, &devEnv.Spec.TeamSettings)
if err != nil {
return fmt.Errorf("failed to add repo %s to Prow config in namespace %s: %v", repo, env.Spec.Namespace, err)
}
}
/* It is important this pull secret handling goes after any namespace creation code; the service account exists in the created namespace */
if o.PullSecrets != "" {
// We need the namespace to be created first - do the check
if !o.GitOpsMode {
err = kube.EnsureEnvironmentNamespaceSetup(kubeClient, jxClient, &env, env.Spec.Namespace)
if err != nil {
// This can happen if, for whatever reason, the namespace takes a while to create. That shouldn't stop the entire process though
log.Warnf("Namespace %s does not exist for jx to patch the service account for, you should patch the service account manually with your pull secret(s) \n", env.Spec.Namespace)
}
}
imagePullSecrets := strings.Fields(o.PullSecrets)
saName := "default"
//log.Infof("Patching the secrets %s for the service account %s\n", imagePullSecrets, saName)
err = serviceaccount.PatchImagePullSecrets(kubeClient, env.Spec.Namespace, saName, imagePullSecrets)
if err != nil {
return fmt.Errorf("Failed to add pull secrets %s to service account %s in namespace %s: %v", imagePullSecrets, saName, env.Spec.Namespace, err)
} else {
log.Infof("Service account \"%s\" in namespace \"%s\" configured to use pull secret(s) %s \n", saName, env.Spec.Namespace, imagePullSecrets)
log.Infof("Pull secret(s) must exist in namespace %s before deploying your applications in this environment \n", env.Spec.Namespace)
}
}
// Skip the environment registration if gitops mode is active
if o.GitOpsMode {
return nil
}
err = o.RegisterEnvironment(&env, gitProvider, authConfigSvc)
if err != nil {
errors.Wrapf(err, "registering the environment %s/%s", env.GetNamespace(), env.GetName())
}
return nil
}
// RegisterEnvironment performs the environment registration
func (o *CreateEnvOptions) RegisterEnvironment(env *v1.Environment, gitProvider gits.GitProvider, authConfigSvc auth.ConfigService) error {
gitURL := env.Spec.Source.URL
gitInfo, err := gits.ParseGitURL(gitURL)
if err != nil {
return errors.Wrap(err, "parsing git repository URL from environment source")
}
if gitURL == "" {
return nil
}
envDir, err := util.EnvironmentsDir()
if err != nil {
return errors.Wrap(err, "getting environments directory")
}
if gitProvider == nil {
authConfigSvc, err = o.CreateGitAuthConfigService()
if err != nil {
return err
}
gitKind, err := o.GitServerKind(gitInfo)
if err != nil {
return err
}
message := "user name to create the Git repository"
commonOpts := o.CreateOptions.CommonOptions
p, err := commonOpts.NewGitProvider(gitURL, message, authConfigSvc, gitKind, o.BatchMode, o.Git())
if err != nil {
return err
}
gitProvider = p
}
if o.Prow {
config := authConfigSvc.Config()
u := gitInfo.HostURL()
server := config.GetOrCreateServer(u)
if len(server.Users) == 0 {
// lets check if the host was used in `~/.jx/gitAuth.yaml` instead of URL
s2 := config.GetOrCreateServer(gitInfo.Host)
if s2 != nil && len(s2.Users) > 0 {
server = s2
u = gitInfo.Host
}
}
user, err := config.PickServerUserAuth(server, "user name for the Pipeline", o.BatchMode, "", o.In, o.Out, o.Err)
if err != nil {
return err
}
if user.Username == "" {
return fmt.Errorf("Could not find a username for git server %s", u)
}
_, err = o.updatePipelineGitCredentialsSecret(server, user)
if err != nil {
return err
}
return o.createWebhookProw(gitURL, gitProvider)
}
return o.ImportProject(gitURL, envDir, jenkinsfile.Name, o.BranchPattern, o.EnvJobCredentials, false, gitProvider, authConfigSvc, true, o.BatchMode)
}