add python compatibility module
[multicloud/k8s.git] / src / orchestrator / pkg / module / deployment_intent_groups.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         "encoding/json"
21         "reflect"
22         "time"
23
24         "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext"
25         "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
26         "github.com/onap/multicloud-k8s/src/orchestrator/pkg/state"
27
28         pkgerrors "github.com/pkg/errors"
29 )
30
31 // DeploymentIntentGroup shall have 2 fields - MetaData and Spec
32 type DeploymentIntentGroup struct {
33         MetaData DepMetaData `json:"metadata"`
34         Spec     DepSpecData `json:"spec"`
35 }
36
37 // DepMetaData has Name, description, userdata1, userdata2
38 type DepMetaData struct {
39         Name        string `json:"name"`
40         Description string `json:"description"`
41         UserData1   string `json:"userData1"`
42         UserData2   string `json:"userData2"`
43 }
44
45 // DepSpecData has profile, version, OverrideValuesObj
46 type DepSpecData struct {
47         Profile           string           `json:"profile"`
48         Version           string           `json:"version"`
49         OverrideValuesObj []OverrideValues `json:"override-values"`
50         LogicalCloud string `json:"logical-cloud"`
51 }
52
53 // OverrideValues has appName and ValuesObj
54 type OverrideValues struct {
55         AppName   string            `json:"app-name"`
56         ValuesObj map[string]string `json:"values"`
57 }
58
59 // Values has ImageRepository
60 // type Values struct {
61 //      ImageRepository string `json:"imageRepository"`
62 // }
63
64 // DeploymentIntentGroupManager is an interface which exposes the DeploymentIntentGroupManager functionality
65 type DeploymentIntentGroupManager interface {
66         CreateDeploymentIntentGroup(d DeploymentIntentGroup, p string, ca string, v string) (DeploymentIntentGroup, error)
67         GetDeploymentIntentGroup(di string, p string, ca string, v string) (DeploymentIntentGroup, error)
68         GetDeploymentIntentGroupState(di string, p string, ca string, v string) (state.StateInfo, error)
69         DeleteDeploymentIntentGroup(di string, p string, ca string, v string) error
70         GetAllDeploymentIntentGroups(p string, ca string, v string) ([]DeploymentIntentGroup, error)
71 }
72
73 // DeploymentIntentGroupKey consists of Name of the deployment group, project name, CompositeApp name, CompositeApp version
74 type DeploymentIntentGroupKey struct {
75         Name         string `json:"deploymentintentgroup"`
76         Project      string `json:"project"`
77         CompositeApp string `json:"compositeapp"`
78         Version      string `json:"compositeappversion"`
79 }
80
81 // We will use json marshalling to convert to string to
82 // preserve the underlying structure.
83 func (dk DeploymentIntentGroupKey) String() string {
84         out, err := json.Marshal(dk)
85         if err != nil {
86                 return ""
87         }
88         return string(out)
89 }
90
91 // DeploymentIntentGroupClient implements the DeploymentIntentGroupManager interface
92 type DeploymentIntentGroupClient struct {
93         storeName   string
94         tagMetaData string
95         tagState    string
96 }
97
98 // NewDeploymentIntentGroupClient return an instance of DeploymentIntentGroupClient which implements DeploymentIntentGroupManager
99 func NewDeploymentIntentGroupClient() *DeploymentIntentGroupClient {
100         return &DeploymentIntentGroupClient{
101                 storeName:   "orchestrator",
102                 tagMetaData: "deploymentintentgroupmetadata",
103                 tagState:    "stateInfo",
104         }
105 }
106
107 // CreateDeploymentIntentGroup creates an entry for a given  DeploymentIntentGroup in the database. Other Input parameters for it - projectName, compositeAppName, version
108 func (c *DeploymentIntentGroupClient) CreateDeploymentIntentGroup(d DeploymentIntentGroup, p string, ca string,
109         v string) (DeploymentIntentGroup, error) {
110
111         res, err := c.GetDeploymentIntentGroup(d.MetaData.Name, p, ca, v)
112         if !reflect.DeepEqual(res, DeploymentIntentGroup{}) {
113                 return DeploymentIntentGroup{}, pkgerrors.New("DeploymentIntent already exists")
114         }
115
116         //Check if project exists
117         _, err = NewProjectClient().GetProject(p)
118         if err != nil {
119                 return DeploymentIntentGroup{}, pkgerrors.New("Unable to find the project")
120         }
121
122         //check if compositeApp exists
123         _, err = NewCompositeAppClient().GetCompositeApp(ca, v, p)
124         if err != nil {
125                 return DeploymentIntentGroup{}, pkgerrors.New("Unable to find the composite-app")
126         }
127
128         gkey := DeploymentIntentGroupKey{
129                 Name:         d.MetaData.Name,
130                 Project:      p,
131                 CompositeApp: ca,
132                 Version:      v,
133         }
134
135         err = db.DBconn.Insert(c.storeName, gkey, nil, c.tagMetaData, d)
136         if err != nil {
137                 return DeploymentIntentGroup{}, pkgerrors.Wrap(err, "Create DB entry error")
138         }
139
140         // Add the stateInfo record
141         s := state.StateInfo{}
142         a := state.ActionEntry{
143                 State:     state.StateEnum.Created,
144                 ContextId: "",
145                 TimeStamp: time.Now(),
146         }
147         s.Actions = append(s.Actions, a)
148
149         err = db.DBconn.Insert(c.storeName, gkey, nil, c.tagState, s)
150         if err != nil {
151                 return DeploymentIntentGroup{}, pkgerrors.Wrap(err, "Error updating the stateInfo of the DeploymentIntentGroup: "+d.MetaData.Name)
152         }
153
154         return d, nil
155 }
156
157 // GetDeploymentIntentGroup returns the DeploymentIntentGroup with a given name, project, compositeApp and version of compositeApp
158 func (c *DeploymentIntentGroupClient) GetDeploymentIntentGroup(di string, p string, ca string, v string) (DeploymentIntentGroup, error) {
159
160         key := DeploymentIntentGroupKey{
161                 Name:         di,
162                 Project:      p,
163                 CompositeApp: ca,
164                 Version:      v,
165         }
166
167         result, err := db.DBconn.Find(c.storeName, key, c.tagMetaData)
168         if err != nil {
169                 return DeploymentIntentGroup{}, pkgerrors.Wrap(err, "Get DeploymentIntentGroup error")
170         }
171
172         if result != nil {
173                 d := DeploymentIntentGroup{}
174                 err = db.DBconn.Unmarshal(result[0], &d)
175                 if err != nil {
176                         return DeploymentIntentGroup{}, pkgerrors.Wrap(err, "Unmarshalling DeploymentIntentGroup")
177                 }
178                 return d, nil
179         }
180
181         return DeploymentIntentGroup{}, pkgerrors.New("Error getting DeploymentIntentGroup")
182
183 }
184
185 // GetAllDeploymentIntentGroups returns all the deploymentIntentGroups under a specific project, compositeApp and version
186 func (c *DeploymentIntentGroupClient) GetAllDeploymentIntentGroups(p string, ca string, v string) ([]DeploymentIntentGroup, error) {
187
188         key := DeploymentIntentGroupKey{
189                 Name:         "",
190                 Project:      p,
191                 CompositeApp: ca,
192                 Version:      v,
193         }
194
195         //Check if project exists
196         _, err := NewProjectClient().GetProject(p)
197         if err != nil {
198                 return []DeploymentIntentGroup{}, pkgerrors.Wrap(err, "Unable to find the project")
199         }
200
201         //check if compositeApp exists
202         _, err = NewCompositeAppClient().GetCompositeApp(ca, v, p)
203         if err != nil {
204                 return []DeploymentIntentGroup{}, pkgerrors.Wrap(err, "Unable to find the composite-app, check CompositeAppName and Version")
205         }
206         var diList []DeploymentIntentGroup
207         result, err := db.DBconn.Find(c.storeName, key, c.tagMetaData)
208         if err != nil {
209                 return []DeploymentIntentGroup{}, pkgerrors.Wrap(err, "Get DeploymentIntentGroup error")
210         }
211
212         for _, value := range result {
213                 di := DeploymentIntentGroup{}
214                 err = db.DBconn.Unmarshal(value, &di)
215                 if err != nil {
216                         return []DeploymentIntentGroup{}, pkgerrors.Wrap(err, "Unmarshaling DeploymentIntentGroup")
217                 }
218                 diList = append(diList, di)
219         }
220
221         return diList, nil
222
223 }
224
225 // GetDeploymentIntentGroupState returns the AppContent with a given DeploymentIntentname, project, compositeAppName and version of compositeApp
226 func (c *DeploymentIntentGroupClient) GetDeploymentIntentGroupState(di string, p string, ca string, v string) (state.StateInfo, error) {
227
228         key := DeploymentIntentGroupKey{
229                 Name:         di,
230                 Project:      p,
231                 CompositeApp: ca,
232                 Version:      v,
233         }
234
235         result, err := db.DBconn.Find(c.storeName, key, c.tagState)
236         if err != nil {
237                 return state.StateInfo{}, pkgerrors.Wrap(err, "Get DeploymentIntentGroup StateInfo error")
238         }
239
240         if result != nil {
241                 s := state.StateInfo{}
242                 err = db.DBconn.Unmarshal(result[0], &s)
243                 if err != nil {
244                         return state.StateInfo{}, pkgerrors.Wrap(err, "Unmarshalling DeploymentIntentGroup StateInfo")
245                 }
246                 return s, nil
247         }
248
249         return state.StateInfo{}, pkgerrors.New("Error getting DeploymentIntentGroup StateInfo")
250 }
251
252 // DeleteDeploymentIntentGroup deletes a DeploymentIntentGroup
253 func (c *DeploymentIntentGroupClient) DeleteDeploymentIntentGroup(di string, p string, ca string, v string) error {
254         k := DeploymentIntentGroupKey{
255                 Name:         di,
256                 Project:      p,
257                 CompositeApp: ca,
258                 Version:      v,
259         }
260         s, err := c.GetDeploymentIntentGroupState(di, p, ca, v)
261         if err != nil {
262                 return pkgerrors.Errorf("Error getting stateInfo from DeploymentIntentGroup: " + di)
263         }
264
265         stateVal, err := state.GetCurrentStateFromStateInfo(s)
266         if err != nil {
267                 return pkgerrors.Errorf("Error getting current state from DeploymentIntentGroup stateInfo: " + di)
268         }
269
270         if stateVal == state.StateEnum.Instantiated {
271                 return pkgerrors.Errorf("DeploymentIntentGroup must be terminated before it can be deleted " + di)
272         }
273
274         // remove the app contexts associated with thie Deployment Intent Group
275         if stateVal == state.StateEnum.Terminated {
276                 // Verify that the appcontext has completed terminating
277                 ctxid := state.GetLastContextIdFromStateInfo(s)
278                 acStatus, err := state.GetAppContextStatus(ctxid)
279                 if err == nil &&
280                         !(acStatus.Status == appcontext.AppContextStatusEnum.Terminated || acStatus.Status == appcontext.AppContextStatusEnum.TerminateFailed) {
281                         return pkgerrors.Errorf("DeploymentIntentGroup has not completed terminating: " + di)
282                 }
283
284                 for _, id := range state.GetContextIdsFromStateInfo(s) {
285                         context, err := state.GetAppContextFromId(id)
286                         if err != nil {
287                                 return pkgerrors.Wrap(err, "Error getting appcontext from Deployment Intent Group StateInfo")
288                         }
289                         err = context.DeleteCompositeApp()
290                         if err != nil {
291                                 return pkgerrors.Wrap(err, "Error deleting appcontext for Deployment Intent Group")
292                         }
293                 }
294         }
295
296         err = db.DBconn.Remove(c.storeName, k)
297         if err != nil {
298                 return pkgerrors.Wrap(err, "Error deleting DeploymentIntentGroup entry")
299         }
300         return nil
301
302 }