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