Merge "Install criproxy binary in /usr/local/bin instead of /tmp. Issue-ID: MULTICLOU...
[multicloud/k8s.git] / src / dcm / pkg / module / apply.go
1 /*\r
2 * Copyright 2020 Intel Corporation, Inc\r
3 *\r
4 * Licensed under the Apache License, Version 2.0 (the "License");\r
5 * you may not use this file except in compliance with the License.\r
6 * You may obtain a copy of the License at\r
7 *\r
8 *     http://www.apache.org/licenses/LICENSE-2.0\r
9 *\r
10 * Unless required by applicable law or agreed to in writing, software\r
11 * distributed under the License is distributed on an "AS IS" BASIS,\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13 * See the License for the specific language governing permissions and\r
14 * limitations under the License.\r
15 */\r
16 \r
17 package module\r
18 \r
19 import (\r
20     "strings"\r
21     "fmt"\r
22     "crypto/rsa"\r
23     "crypto/rand"\r
24     "crypto/x509"\r
25     "crypto/x509/pkix"\r
26     "encoding/json"\r
27     "encoding/pem"\r
28     "encoding/base64"\r
29     "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext"\r
30     log "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/logutils"\r
31     "gopkg.in/yaml.v2"\r
32     pkgerrors "github.com/pkg/errors"\r
33 \r
34 )\r
35 \r
36 type Resource struct {\r
37     ApiVersion      string               `yaml:"apiVersion"`\r
38     Kind            string               `yaml:"kind"`\r
39     MetaData        MetaDatas            `yaml:"metadata"`\r
40     Specification   Specs                 `yaml:"spec,omitempty"`\r
41     Rules           []RoleRules          `yaml:"rules,omitempty"`\r
42     Subjects        []RoleSubjects       `yaml:"subjects,omitempty"`\r
43     RoleRefs        RoleRef              `yaml:"roleRef,omitempty"`\r
44 }\r
45 \r
46 type MetaDatas struct {\r
47     Name           string                `yaml:"name"`\r
48     Namespace      string                `yaml:"namespace,omitempty"`\r
49 }\r
50 \r
51 type Specs struct {\r
52     Request        string                `yaml:"request,omitempty"`\r
53     Usages         []string              `yaml:"usages,omitempty"`\r
54     //Hard           logicalcloud.QSpec    `yaml:"hard,omitempty"`\r
55     Hard           QSpec    `yaml:"hard,omitempty"`\r
56 }\r
57 \r
58 type RoleRules struct {\r
59     ApiGroups      []string              `yaml:"apiGroups"`\r
60     Resources      []string              `yaml:"resources"`\r
61     Verbs          []string              `yaml:"verbs"`\r
62 }\r
63 \r
64 type RoleSubjects struct {\r
65     Kind           string                `yaml:"kind"`\r
66     Name           string                `yaml:"name"`\r
67     ApiGroup       string                `yaml:"apiGroup"`\r
68 }\r
69 \r
70 type RoleRef struct {\r
71     Kind           string                `yaml:"kind"`\r
72     Name           string                `yaml:"name"`\r
73     ApiGroup       string                `yaml:"apiGroup"`\r
74 }\r
75 \r
76 \r
77 func createNamespace(logicalcloud LogicalCloud) (string, error) {\r
78 \r
79     namespace := Resource{\r
80         ApiVersion: "v1",\r
81         Kind:       "Namespace",\r
82         MetaData:  MetaDatas{\r
83             Name:  logicalcloud.Specification.NameSpace,\r
84         },\r
85     }\r
86 \r
87     nsData, err := yaml.Marshal(&namespace)\r
88     if err != nil {\r
89         return "", err\r
90     }\r
91 \r
92 \r
93     return string(nsData), nil\r
94 }\r
95 \r
96 func createRole(logicalcloud LogicalCloud) (string, error) {\r
97 \r
98     userPermissions := logicalcloud.Specification.User.UserPermissions[0]\r
99     \r
100     role := Resource{\r
101         ApiVersion: "rbac.authorization.k8s.io/v1beta1",\r
102         Kind: "Role",\r
103         MetaData: MetaDatas{\r
104             Name: strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-role"}, ""),\r
105             Namespace:  logicalcloud.Specification.NameSpace,\r
106         },\r
107         Rules: []RoleRules{ RoleRules{\r
108             ApiGroups: userPermissions.APIGroups,\r
109             Resources: userPermissions.Resources,\r
110             Verbs: userPermissions.Verbs,\r
111             },\r
112         },\r
113 \r
114     }\r
115 \r
116     roleData, err := yaml.Marshal(&role)\r
117     if err != nil {\r
118         return "", err\r
119     }\r
120 \r
121     return string(roleData), nil\r
122 }\r
123 \r
124 func createRoleBinding(logicalcloud LogicalCloud) (string, error) {\r
125 \r
126     roleBinding := Resource{\r
127         ApiVersion: "rbac.authorization.k8s.io/v1beta1",\r
128         Kind: "RoleBinding",\r
129         MetaData: MetaDatas{\r
130             Name: strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-roleBinding"}, ""),\r
131             Namespace:  logicalcloud.Specification.NameSpace,\r
132         },\r
133         Subjects: []RoleSubjects{ RoleSubjects{\r
134             Kind: "User",\r
135             Name: logicalcloud.Specification.User.UserName,\r
136             ApiGroup: "",\r
137           },\r
138         },\r
139 \r
140         RoleRefs: RoleRef{\r
141             Kind: "Role",\r
142             Name: strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-role"}, ""),\r
143             ApiGroup: "",\r
144         },\r
145     }\r
146 \r
147     rBData, err := yaml.Marshal(&roleBinding)\r
148     if err != nil {\r
149         return "", err\r
150     }\r
151 \r
152 \r
153     return string(rBData), nil\r
154 \r
155 }\r
156 \r
157 func createQuota(quota []Quota, namespace string) (string, error) {\r
158     lcQuota := quota[0]\r
159 \r
160     q := Resource{\r
161         ApiVersion: "v1",\r
162         Kind:       "ResourceQuota",\r
163         MetaData:  MetaDatas{\r
164             Name:  lcQuota.MetaData.QuotaName,\r
165             Namespace: namespace,\r
166         },\r
167         Specification:  Specs{\r
168             Hard:  lcQuota.Specification,\r
169         },\r
170     }\r
171 \r
172     qData, err := yaml.Marshal(&q)\r
173     if err != nil {\r
174         return "", err\r
175     }\r
176 \r
177 \r
178 \r
179     return string(qData), nil\r
180 \r
181 }\r
182 \r
183 func createUserCSR(logicalcloud LogicalCloud) (string, error) {\r
184     KEYSIZE := 4096\r
185     userName := logicalcloud.Specification.User.UserName\r
186 \r
187     key, err := rsa.GenerateKey(rand.Reader, KEYSIZE)\r
188     if err != nil {\r
189         return "", err\r
190     }\r
191 \r
192     csrTemplate := x509.CertificateRequest{Subject: pkix.Name{CommonName: userName,},\r
193     }\r
194 \r
195     csrCert, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, key)\r
196     if err != nil {\r
197         return "", err\r
198     }\r
199 \r
200     //Encode csr\r
201     csr := pem.EncodeToMemory(&pem.Block{\r
202         Type: "CERTIFICATE REQUEST",\r
203         Bytes: csrCert,\r
204     })\r
205 \r
206     csrObj :=  Resource{\r
207         ApiVersion: "certificates.k8s.io/v1beta1",\r
208         Kind:       "CertificateSigningRequest",\r
209         MetaData:  MetaDatas{\r
210             Name:  strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-user-csr"}, ""),\r
211             Namespace: logicalcloud.Specification.NameSpace,\r
212         },\r
213         Specification:  Specs{\r
214             Request: base64.StdEncoding.EncodeToString(csr),\r
215             Usages: []string{"digital signature", "key encipherment"},\r
216         },\r
217     }\r
218 \r
219     csrData, err := yaml.Marshal(&csrObj)\r
220     if err != nil {\r
221         return "", err\r
222     }\r
223 \r
224 \r
225     return string(csrData), nil\r
226 \r
227 \r
228 }\r
229 \r
230 // TODO:\r
231 // Install istio\r
232 // Store user key for user creation\r
233 // Code to run kubectl commands for user\r
234 // kubectl certificate approve lc1-user-cert\r
235 // kubectl get csr lc1-user-cert -o jsonpath='{.status.certificate}' | base64 --decode > user.crt\r
236 // kubectl config set-credentials user --client-certificate=<user.crt>  --client-key=<user.key>\r
237 // kubectl config set-context user-context --cluster=cluster-name --namespace=lc1 --user=user\r
238 \r
239 \r
240 func CreateEtcdContext(logicalcloud LogicalCloud, clusterList []Cluster,\r
241      quotaList []Quota  ) error {\r
242 \r
243     APP := "logical-cloud"\r
244     logicalCloudName := logicalcloud.MetaData.LogicalCloudName\r
245 \r
246 \r
247     //Resource Names\r
248     namespaceName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+namespace"}, "")\r
249     roleName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+role"}, "")\r
250     roleBindingName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+roleBinding"}, "")\r
251     quotaName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+quota"}, "")\r
252     csrName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+CertificateSigningRequest"}, "")\r
253 \r
254     // Get resources to be added\r
255     namespace, err := createNamespace(logicalcloud)\r
256     if err != nil {\r
257         return pkgerrors.Wrap(err, "Error Creating Namespace YAML for logical cloud")\r
258     }\r
259 \r
260     role, err := createRole(logicalcloud)\r
261     if err != nil {\r
262         return pkgerrors.Wrap(err, "Error Creating Role YAML for logical cloud")\r
263     }\r
264 \r
265     roleBinding, err := createRoleBinding(logicalcloud)\r
266     if err != nil {\r
267         return pkgerrors.Wrap(err, "Error Creating RoleBinding YAML for logical cloud")\r
268     }\r
269 \r
270     quota, err := createQuota(quotaList, logicalcloud.Specification.NameSpace)\r
271     if err != nil {\r
272         return pkgerrors.Wrap(err, "Error Creating Quota YAML for logical cloud")\r
273     }\r
274 \r
275     csr, err := createUserCSR(logicalcloud)\r
276     if err != nil {\r
277         return pkgerrors.Wrap(err, "Error Creating User CSR for logical cloud")\r
278     }\r
279 \r
280 \r
281     context := appcontext.AppContext{}\r
282     ctxVal, err := context.InitAppContext()\r
283     if err != nil {\r
284         return pkgerrors.Wrap(err, "Error creating AppContext")\r
285     }\r
286 \r
287     fmt.Printf("%v\n", ctxVal)\r
288 \r
289     handle, err := context.CreateCompositeApp()\r
290     if err != nil {\r
291         return pkgerrors.Wrap(err, "Error creating AppContext CompositeApp")\r
292     }\r
293 \r
294 \r
295     appHandle, err := context.AddApp(handle, APP)\r
296     if err != nil {\r
297         cleanuperr := context.DeleteCompositeApp()\r
298         if cleanuperr != nil {\r
299             log.Warn("Error cleaning AppContext CompositeApp create failure", log.Fields{\r
300                 "logical-cloud": logicalCloudName,\r
301             })\r
302         }\r
303         return pkgerrors.Wrap(err, "Error adding App to AppContext")\r
304     }\r
305 \r
306 \r
307     // Iterate through cluster list and add all the clusters\r
308     for _, cluster:= range clusterList {\r
309         clusterName := strings.Join([]string{cluster.Specification.ClusterProvider, "+", cluster.Specification.ClusterName, }, "")\r
310         clusterHandle, err := context.AddCluster(appHandle, clusterName)\r
311 \r
312         if err != nil {\r
313             cleanuperr := context.DeleteCompositeApp()\r
314             if cleanuperr != nil {\r
315                 log.Warn("Error cleaning AppContext after add cluster failure", log.Fields{\r
316                     "cluster-provider": cluster.Specification.ClusterProvider,\r
317                     "cluster":          cluster.Specification.ClusterName,\r
318                     "logical-cloud": logicalCloudName,\r
319 \r
320                 })\r
321             }\r
322             return pkgerrors.Wrap(err, "Error adding Cluster to AppContext")\r
323         }\r
324 \r
325         // Add namespace resource to each cluster\r
326         _, err = context.AddResource(clusterHandle, namespaceName, namespace)\r
327         if err != nil {\r
328             cleanuperr := context.DeleteCompositeApp()\r
329             if cleanuperr != nil {\r
330                 log.Warn("Error cleaning AppContext after add namespace resource failure", log.Fields{\r
331                     "cluster-provider": cluster.Specification.ClusterProvider,\r
332                     "cluster":          cluster.Specification.ClusterName,\r
333                     "logical-cloud": logicalCloudName,\r
334 \r
335                 })\r
336             }\r
337             return pkgerrors.Wrap(err, "Error adding Namespace Resource to AppContext")\r
338         }\r
339 \r
340         // Add csr resource to each cluster\r
341         _, err = context.AddResource(clusterHandle, csrName, csr)\r
342         if err != nil {\r
343             cleanuperr := context.DeleteCompositeApp()\r
344             if cleanuperr != nil {\r
345                 log.Warn("Error cleaning AppContext after add CSR resource failure", log.Fields{\r
346                     "cluster-provider": cluster.Specification.ClusterProvider,\r
347                     "cluster":          cluster.Specification.ClusterName,\r
348                     "logical-cloud": logicalCloudName,\r
349 \r
350                 })\r
351             }\r
352             return pkgerrors.Wrap(err, "Error adding CSR Resource to AppContext")\r
353         }\r
354 \r
355 \r
356 \r
357         // Add Role resource to each cluster\r
358         _, err = context.AddResource(clusterHandle, roleName, role)\r
359         if err != nil {\r
360             cleanuperr := context.DeleteCompositeApp()\r
361             if cleanuperr != nil {\r
362                 log.Warn("Error cleaning AppContext after add role resource failure", log.Fields{\r
363                     "cluster-provider": cluster.Specification.ClusterProvider,\r
364                     "cluster":          cluster.Specification.ClusterName,\r
365                     "logical-cloud": logicalCloudName,\r
366 \r
367                 })\r
368             }\r
369             return pkgerrors.Wrap(err, "Error adding role Resource to AppContext")\r
370         }\r
371 \r
372         // Add RoleBinding resource to each cluster\r
373         _, err = context.AddResource(clusterHandle, roleBindingName, roleBinding)\r
374         if err != nil {\r
375             cleanuperr := context.DeleteCompositeApp()\r
376             if cleanuperr != nil {\r
377                 log.Warn("Error cleaning AppContext after add roleBinding resource failure", log.Fields{\r
378                     "cluster-provider": cluster.Specification.ClusterProvider,\r
379                     "cluster":          cluster.Specification.ClusterName,\r
380                     "logical-cloud": logicalCloudName,\r
381 \r
382                 })\r
383             }\r
384             return pkgerrors.Wrap(err, "Error adding roleBinding Resource to AppContext")\r
385         }\r
386 \r
387         // Add quota resource to each cluster\r
388         _, err = context.AddResource(clusterHandle, quotaName, quota)\r
389         if err != nil {\r
390             cleanuperr := context.DeleteCompositeApp()\r
391             if cleanuperr != nil {\r
392                 log.Warn("Error cleaning AppContext after add quota resource failure", log.Fields{\r
393                     "cluster-provider": cluster.Specification.ClusterProvider,\r
394                     "cluster":          cluster.Specification.ClusterName,\r
395                     "logical-cloud": logicalCloudName,\r
396 \r
397                 })\r
398             }\r
399             return pkgerrors.Wrap(err, "Error adding quota Resource to AppContext")\r
400         }\r
401 \r
402         // Add Resource Order and Resource Dependency\r
403         resOrder, err := json.Marshal(map[string][]string{"resorder" : []string{namespaceName, quotaName, csrName, roleName, roleBindingName}})\r
404         if err != nil {\r
405             return pkgerrors.Wrap(err, "Error creating resource order JSON")\r
406         }\r
407 \r
408         resDependency, err  :=  json.Marshal(map[string]map[string]string{"resdependency" : map[string]string{namespaceName : "go",\r
409                 quotaName : strings.Join([]string{"wait on ", namespaceName}, ""), csrName:  strings.Join([]string{"wait on ", quotaName}, ""),\r
410                 roleName : strings.Join([]string{"wait on ", csrName}, ""), roleBindingName: strings.Join([]string{"wait on ", roleName}, ""),}})\r
411 \r
412         if err != nil {\r
413             return pkgerrors.Wrap(err, "Error creating resource dependency JSON")\r
414         }\r
415 \r
416         _, err = context.AddInstruction(clusterHandle, "resource", "order", string(resOrder))\r
417         if err != nil {\r
418             cleanuperr := context.DeleteCompositeApp()\r
419             if cleanuperr != nil {\r
420                 log.Warn("Error cleaning AppContext after add instruction  failure", log.Fields{\r
421                     "cluster-provider": cluster.Specification.ClusterProvider,\r
422                     "cluster":          cluster.Specification.ClusterName,\r
423                     "logical-cloud": logicalCloudName,\r
424 \r
425                 })\r
426             }\r
427             return pkgerrors.Wrap(err, "Error adding instruction order to AppContext")\r
428         }\r
429 \r
430         _, err = context.AddInstruction(clusterHandle, "resource", "dependency", string(resDependency))\r
431         if err != nil {\r
432             cleanuperr := context.DeleteCompositeApp()\r
433             if cleanuperr != nil {\r
434                 log.Warn("Error cleaning AppContext after add instruction  failure", log.Fields{\r
435                     "cluster-provider": cluster.Specification.ClusterProvider,\r
436                     "cluster":          cluster.Specification.ClusterName,\r
437                     "logical-cloud": logicalCloudName,\r
438 \r
439                 })\r
440             }\r
441             return pkgerrors.Wrap(err, "Error adding instruction dependency to AppContext")\r
442         }\r
443 \r
444 \r
445 \r
446     }\r
447 \r
448     return nil\r
449 \r
450 \r
451 }\r