-
Notifications
You must be signed in to change notification settings - Fork 196
/
Copy pathcreate.go
253 lines (224 loc) · 8.26 KB
/
create.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
// Copyright © 2017 The Kubicorn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"fmt"
"os"
"strings"
"encoding/json"
"github.com/kubicorn/kubicorn/apis/cluster"
"github.com/kubicorn/kubicorn/pkg/cli"
"github.com/kubicorn/kubicorn/pkg/kubeconfig"
"github.com/kubicorn/kubicorn/pkg/logger"
"github.com/kubicorn/kubicorn/pkg/namer"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/kube-deploy/cluster-api/api/cluster/v1alpha1"
)
// CreateCmd represents create command
func CreateCmd() *cobra.Command {
var co = &cli.CreateOptions{}
var createCmd = &cobra.Command{
Use: "create [NAME] [-p|--profile PROFILENAME] [-c|--cloudid CLOUDID]",
Short: "Create a Kubicorn API model from a profile",
Long: `Use this command to create a Kubicorn API model in a defined state store.
This command will create a cluster API model as a YAML manifest in a state store.
Once the API model has been created, a user can optionally change the model to their liking.
After a model is defined and configured properly, the user can then apply the model.`,
Run: func(cmd *cobra.Command, args []string) {
switch len(args) {
case 0:
co.Name = viper.GetString(keyKubicornName)
if co.Name == "" {
co.Name = namer.RandomName()
}
case 1:
co.Name = args[0]
default:
logger.Critical("Too many arguments.")
os.Exit(1)
}
if err := RunCreate(co); err != nil {
logger.Critical(err.Error())
os.Exit(1)
}
},
}
fs := createCmd.Flags()
bindCommonStateStoreFlags(&co.StateStoreOptions, fs)
bindCommonAwsFlags(&co.AwsOptions, fs)
fs.StringVarP(&co.Profile, keyProfile, "p", viper.GetString(keyProfile), descProfile)
fs.StringVarP(&co.CloudID, keyCloudID, "c", viper.GetString(keyCloudID), descCloudID)
fs.StringVar(&co.KubeConfigLocalFile, keyKubeConfigLocalFile, viper.GetString(keyKubeConfigLocalFile), descKubeConfigLocalFile)
fs.StringArrayVarP(&co.Set, keySet, "C", viper.GetStringSlice(keySet), descSet)
fs.StringArrayVarP(&co.MasterSet, keyMasterSet, "M", viper.GetStringSlice(keyMasterSet), descMasterSet)
fs.StringArrayVarP(&co.NodeSet, keyNodeSet, "N", viper.GetStringSlice(keyNodeSet), descNodeSet)
fs.StringVarP(&co.GitRemote, keyGitConfig, "g", viper.GetString(keyGitConfig), descGitConfig)
fs.StringArrayVar(&co.AwsOptions.PolicyAttachments, keyPolicyAttachments, co.AwsOptions.PolicyAttachments, descPolicyAttachments)
flagApplyAnnotations(createCmd, "profile", "__kubicorn_parse_profiles")
flagApplyAnnotations(createCmd, "cloudid", "__kubicorn_parse_cloudid")
createCmd.SetUsageTemplate(cli.UsageTemplate)
return createCmd
}
// RunCreate is the starting point when a user runs the create command.
func RunCreate(options *cli.CreateOptions) error {
// Create our cluster resource
name := options.Name
var newCluster *cluster.Cluster
if _, ok := cli.ProfileMapIndexed[options.Profile]; ok {
newCluster = cli.ProfileMapIndexed[options.Profile].ProfileFunc(name)
} else {
return fmt.Errorf("Invalid profile [%s]", options.Profile)
}
if options.KubeConfigLocalFile != "" {
if newCluster.Annotations == nil {
newCluster.Annotations = make(map[string]string)
}
newCluster.Annotations[kubeconfig.ClusterAnnotationKubeconfigLocalFile] = options.KubeConfigLocalFile
}
if len(options.Set) > 0 {
// Here we override Set options
for _, set := range options.Set {
parts := strings.SplitN(set, "=", 2)
if len(parts) == 1 {
continue
}
providerConfig := newCluster.ProviderConfig()
err := cli.SwalkerWrite(strings.Title(parts[0]), providerConfig, parts[1])
if err != nil {
//fmt.Println(1)
return fmt.Errorf("Invalid --set: %v", err)
}
newCluster.SetProviderConfig(providerConfig)
}
}
if len(options.MasterSet) > 0 {
// Here we override MasterSet options
for _, set := range options.MasterSet {
parts := strings.SplitN(set, "=", 2)
if len(parts) == 1 {
continue
}
for i, ms := range newCluster.MachineSets {
isMaster := false
for _, role := range ms.Spec.Template.Spec.Roles {
if role == v1alpha1.MasterRole {
isMaster = true
break
}
}
if !isMaster {
continue
}
pcStr := ms.Spec.Template.Spec.ProviderConfig
providerConfig := &cluster.MachineProviderConfig{}
json.Unmarshal([]byte(pcStr), providerConfig)
err := cli.SwalkerWrite(strings.Title(parts[0]), providerConfig, parts[1])
if err != nil {
//fmt.Println(2)
return fmt.Errorf("Invalid --set: %v", err)
}
// Now set the provider config
bytes, err := json.Marshal(providerConfig)
if err != nil {
logger.Critical("Unable to marshal provider config: %v", err)
return err
}
str := string(bytes)
newCluster.MachineSets[i].Spec.Template.Spec.ProviderConfig = str
}
}
}
if len(options.NodeSet) > 0 {
// Here we override NodeSet options
for _, set := range options.NodeSet {
parts := strings.SplitN(set, "=", 2)
if len(parts) == 1 {
continue
}
for i, ms := range newCluster.MachineSets {
isNode := false
for _, role := range ms.Spec.Template.Spec.Roles {
if role == v1alpha1.NodeRole {
isNode = true
break
}
}
if !isNode {
continue
}
pcStr := ms.Spec.Template.Spec.ProviderConfig
providerConfig := &cluster.MachineProviderConfig{}
json.Unmarshal([]byte(pcStr), providerConfig)
err := cli.SwalkerWrite(strings.Title(parts[0]), providerConfig, parts[1])
if err != nil {
//fmt.Println(3)
return fmt.Errorf("Invalid --set: %v", err)
}
// Now set the provider config
bytes, err := json.Marshal(providerConfig)
if err != nil {
logger.Critical("Unable to marshal provider config: %v", err)
return err
}
str := string(bytes)
newCluster.MachineSets[i].Spec.Template.Spec.ProviderConfig = str
}
}
}
if len(options.AwsOptions.PolicyAttachments) > 0 {
for i, ms := range newCluster.MachineSets {
pcStr := ms.Spec.Template.Spec.ProviderConfig
providerConfig := &cluster.MachineProviderConfig{}
if err := json.Unmarshal([]byte(pcStr), providerConfig); err != nil {
logger.Critical("Unable to unmarshal provider config: %v", err)
return err
}
if providerConfig.ServerPool != nil && providerConfig.ServerPool.InstanceProfile != nil && providerConfig.ServerPool.InstanceProfile.Role != nil {
providerConfig.ServerPool.InstanceProfile.Role.PolicyAttachments = options.AwsOptions.PolicyAttachments
}
// Now set the provider config
bytes, err := json.Marshal(providerConfig)
if err != nil {
logger.Critical("Unable to marshal provider config: %v", err)
return err
}
str := string(bytes)
newCluster.MachineSets[i].Spec.Template.Spec.ProviderConfig = str
}
}
if newCluster.ProviderConfig().Cloud == cluster.CloudGoogle && options.CloudID == "" {
return fmt.Errorf("CloudID is required for google cloud. Please set it to your project ID")
}
providerConfig := newCluster.ProviderConfig()
providerConfig.CloudId = options.CloudID
newCluster.SetProviderConfig(providerConfig)
// Expand state store path
// Todo (@kris-nova) please pull this into a filepath package or something
options.StateStorePath = cli.ExpandPath(options.StateStorePath)
// Register state store and check if it exists
stateStore, err := options.NewStateStore()
if err != nil {
return err
} else if stateStore.Exists() {
return fmt.Errorf("State store [%s] exists, will not overwrite. Delete existing profile [%s] and retry", name, options.StateStorePath+"/"+name)
}
// Init new state store with the cluster resource
err = stateStore.Commit(newCluster)
if err != nil {
return fmt.Errorf("Unable to init state store: %v", err)
}
logger.Always("The state [%s/%s/cluster.yaml] has been created. You can edit the file, then run `kubicorn apply %s`", options.StateStorePath, name, name)
return nil
}