2 * Copyright 2018 Intel Corporation, Inc
3 * Copyright © 2021 Samsung Electronics
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
24 "github.com/onap/multicloud-k8s/src/k8splugin/internal/db"
26 pkgerrors "github.com/pkg/errors"
29 // Config contains the parameters needed for configuration
31 ConfigName string `json:"config-name"`
32 TemplateName string `json:"template-name"`
33 Description string `json:"description"`
34 Values map[string]interface{} `json:"values"`
37 //ConfigResult output for Create, Update and delete
38 type ConfigResult struct {
39 InstanceName string `json:"instance-id"`
40 DefinitionName string `json:"rb-name"`
41 DefinitionVersion string `json:"rb-version"`
42 ProfileName string `json:"profile-name"`
43 ConfigName string `json:"config-name"`
44 TemplateName string `json:"template-name"`
45 ConfigVersion uint `json:"config-verion"`
48 //ConfigRollback input
49 type ConfigRollback struct {
51 ConfigVersion string `json:"config-version,omitempty"`
52 ConfigTag string `json:"config-tag,omitempty"`
56 //ConfigTagit for Tagging configurations
57 type ConfigTagit struct {
58 TagName string `json:"tag-name"`
61 // ConfigManager is an interface exposes the config functionality
62 type ConfigManager interface {
63 Create(instanceID string, p Config) (ConfigResult, error)
64 Get(instanceID, configName string) (Config, error)
65 Help() map[string]string
66 Update(instanceID, configName string, p Config) (ConfigResult, error)
67 Delete(instanceID, configName string) (ConfigResult, error)
68 Rollback(instanceID string, p ConfigRollback) error
69 Tagit(instanceID string, p ConfigTagit) error
72 // ConfigClient implements the ConfigManager
73 // It will also be used to maintain some localized state
74 type ConfigClient struct {
78 // NewConfigClient returns an instance of the ConfigClient
79 // which implements the ConfigManager
80 func NewConfigClient() *ConfigClient {
86 // Help returns some information on how to create the content
87 // for the config in the form of html formatted page
88 func (v *ConfigClient) Help() map[string]string {
89 ret := make(map[string]string)
94 // Create an entry for the config in the database
95 func (v *ConfigClient) Create(instanceID string, p Config) (ConfigResult, error) {
97 // Check required fields
98 if p.ConfigName == "" || p.TemplateName == "" || len(p.Values) == 0 {
99 return ConfigResult{}, pkgerrors.New("Incomplete Configuration Provided")
101 // Resolving rbName, Version, etc. not to break response
102 rbName, rbVersion, profileName, _, err := resolveModelFromInstance(instanceID)
104 return ConfigResult{}, pkgerrors.Wrap(err, "Retrieving model info")
107 instanceID: instanceID,
108 configName: p.ConfigName,
110 _, err = cs.getConfig()
112 return ConfigResult{}, pkgerrors.Wrap(err, "Create Error - Config exists")
114 if strings.Contains(err.Error(), "Key doesn't exist") == false {
115 return ConfigResult{}, pkgerrors.Wrap(err, "Create Error")
118 lock, profileChannel := getProfileData(instanceID)
119 // Acquire per profile Mutex
122 err = applyConfig(instanceID, p, profileChannel, "POST")
124 return ConfigResult{}, pkgerrors.Wrap(err, "Apply Config failed")
126 // Create Config DB Entry
127 err = cs.createConfig(p)
129 return ConfigResult{}, pkgerrors.Wrap(err, "Create Config DB Entry")
131 // Create Version Entry in DB for Config
132 cvs := ConfigVersionStore{
133 instanceID: instanceID,
135 version, err := cvs.createConfigVersion(p, Config{}, "POST")
137 return ConfigResult{}, pkgerrors.Wrap(err, "Create Config Version DB Entry")
140 // Create Result structure
141 cfgRes := ConfigResult{
142 InstanceName: instanceID,
143 DefinitionName: rbName,
144 DefinitionVersion: rbVersion,
145 ProfileName: profileName,
146 ConfigName: p.ConfigName,
147 TemplateName: p.TemplateName,
148 ConfigVersion: version,
153 // Update an entry for the config in the database
154 func (v *ConfigClient) Update(instanceID, configName string, p Config) (ConfigResult, error) {
156 // Check required fields
157 if len(p.Values) == 0 {
158 return ConfigResult{}, pkgerrors.New("Incomplete Configuration Provided")
160 // Resolving rbName, Version, etc. not to break response
161 rbName, rbVersion, profileName, _, err := resolveModelFromInstance(instanceID)
163 return ConfigResult{}, pkgerrors.Wrap(err, "Retrieving model info")
165 // Check if Config exists
167 instanceID: instanceID,
168 configName: configName,
170 _, err = cs.getConfig()
172 return ConfigResult{}, pkgerrors.Wrap(err, "Update Error - Config doesn't exist")
174 lock, profileChannel := getProfileData(instanceID)
175 // Acquire per profile Mutex
178 err = applyConfig(instanceID, p, profileChannel, "PUT")
180 return ConfigResult{}, pkgerrors.Wrap(err, "Apply Config failed")
182 // Update Config DB Entry
183 configPrev, err := cs.updateConfig(p)
185 return ConfigResult{}, pkgerrors.Wrap(err, "Update Config DB Entry")
187 // Create Version Entry in DB for Config
188 cvs := ConfigVersionStore{
189 instanceID: instanceID,
191 version, err := cvs.createConfigVersion(p, configPrev, "PUT")
193 return ConfigResult{}, pkgerrors.Wrap(err, "Create Config Version DB Entry")
196 // Create Result structure
197 cfgRes := ConfigResult{
198 InstanceName: instanceID,
199 DefinitionName: rbName,
200 DefinitionVersion: rbVersion,
201 ProfileName: profileName,
202 ConfigName: p.ConfigName,
203 TemplateName: p.TemplateName,
204 ConfigVersion: version,
209 // Get config entry in the database
210 func (v *ConfigClient) Get(instanceID, configName string) (Config, error) {
212 // Acquire per profile Mutex
213 lock, _ := getProfileData(instanceID)
218 instanceID: instanceID,
219 configName: configName,
221 cfg, err := cs.getConfig()
223 return Config{}, pkgerrors.Wrap(err, "Get Config DB Entry")
228 // Delete the Config from database
229 func (v *ConfigClient) Delete(instanceID, configName string) (ConfigResult, error) {
231 // Resolving rbName, Version, etc. not to break response
232 rbName, rbVersion, profileName, _, err := resolveModelFromInstance(instanceID)
234 return ConfigResult{}, pkgerrors.Wrap(err, "Retrieving model info")
236 // Check if Config exists
238 instanceID: instanceID,
239 configName: configName,
241 p, err := cs.getConfig()
243 return ConfigResult{}, pkgerrors.Wrap(err, "Update Error - Config doesn't exist")
245 lock, profileChannel := getProfileData(instanceID)
246 // Acquire per profile Mutex
249 err = applyConfig(instanceID, p, profileChannel, "DELETE")
251 return ConfigResult{}, pkgerrors.Wrap(err, "Apply Config failed")
253 // Delete Config from DB
254 configPrev, err := cs.deleteConfig()
256 return ConfigResult{}, pkgerrors.Wrap(err, "Delete Config DB Entry")
258 // Create Version Entry in DB for Config
259 cvs := ConfigVersionStore{
260 instanceID: instanceID,
262 version, err := cvs.createConfigVersion(Config{}, configPrev, "DELETE")
264 return ConfigResult{}, pkgerrors.Wrap(err, "Delete Config Version DB Entry")
267 // Create Result structure
268 cfgRes := ConfigResult{
269 InstanceName: instanceID,
270 DefinitionName: rbName,
271 DefinitionVersion: rbVersion,
272 ProfileName: profileName,
273 ConfigName: configName,
274 TemplateName: configPrev.TemplateName,
275 ConfigVersion: version,
280 // Rollback starts from current version and rollbacks to the version desired
281 func (v *ConfigClient) Rollback(instanceID string, rback ConfigRollback) error {
283 var reqVersion string
286 if rback.AnyOf.ConfigTag != "" {
287 reqVersion, err = v.GetTagVersion(instanceID, rback.AnyOf.ConfigTag)
289 return pkgerrors.Wrap(err, "Rollback Invalid tag")
291 } else if rback.AnyOf.ConfigVersion != "" {
292 reqVersion = rback.AnyOf.ConfigVersion
294 return pkgerrors.Errorf("No valid Index for Rollback")
297 index, err := strconv.Atoi(reqVersion)
299 return pkgerrors.Wrap(err, "Rollback Invalid Index")
301 rollbackIndex := uint(index)
303 lock, profileChannel := getProfileData(instanceID)
304 // Acquire per profile Mutex
308 cvs := ConfigVersionStore{
309 instanceID: instanceID,
311 currentVersion, err := cvs.getCurrentVersion()
313 return pkgerrors.Wrap(err, "Rollback Get Current Config Version ")
316 if rollbackIndex < 1 && rollbackIndex >= currentVersion {
317 return pkgerrors.Wrap(err, "Rollback Invalid Config Version")
320 //Rollback all the intermettinent configurations
321 for i := currentVersion; i > rollbackIndex; i-- {
322 configNew, configPrev, action, err := cvs.getConfigVersion(i)
324 return pkgerrors.Wrap(err, "Rollback Get Config Version")
327 instanceID: instanceID,
328 configName: configNew.ConfigName,
331 // PUT is proceeded by PUT or POST
332 err = applyConfig(instanceID, configPrev, profileChannel, "PUT")
334 return pkgerrors.Wrap(err, "Apply Config failed")
336 _, err = cs.updateConfig(configPrev)
338 return pkgerrors.Wrap(err, "Update Config DB Entry")
340 } else if action == "POST" {
341 // POST is always preceeded by Config not existing
342 err = applyConfig(instanceID, configNew, profileChannel, "DELETE")
344 return pkgerrors.Wrap(err, "Delete Config failed")
346 _, err = cs.deleteConfig()
348 return pkgerrors.Wrap(err, "Delete Config DB Entry")
350 } else if action == "DELETE" {
351 // DELETE is proceeded by PUT or POST
352 err = applyConfig(instanceID, configPrev, profileChannel, "PUT")
354 return pkgerrors.Wrap(err, "Delete Config failed")
356 _, err = cs.updateConfig(configPrev)
358 return pkgerrors.Wrap(err, "Update Config DB Entry")
362 for i := currentVersion; i > rollbackIndex; i-- {
363 // Delete rolled back items
364 err = cvs.deleteConfigVersion()
366 return pkgerrors.Wrap(err, "Delete Config Version ")
372 // Tagit tags the current version with the tag provided
373 func (v *ConfigClient) Tagit(instanceID string, tag ConfigTagit) error {
375 rbName, rbVersion, profileName, _, err := resolveModelFromInstance(instanceID)
377 return pkgerrors.Wrap(err, "Retrieving model info")
379 lock, _ := getProfileData(instanceID)
380 // Acquire per profile Mutex
384 cvs := ConfigVersionStore{
385 instanceID: instanceID,
387 currentVersion, err := cvs.getCurrentVersion()
389 return pkgerrors.Wrap(err, "Get Current Config Version ")
391 tagKey := constructKey(rbName, rbVersion, profileName, instanceID, v.tagTag, tag.TagName)
393 err = db.Etcd.Put(tagKey, strconv.Itoa(int(currentVersion)))
395 return pkgerrors.Wrap(err, "TagIt store DB")
400 // GetTagVersion returns the version associated with the tag
401 func (v *ConfigClient) GetTagVersion(instanceID, tagName string) (string, error) {
403 rbName, rbVersion, profileName, _, err := resolveModelFromInstance(instanceID)
405 return "", pkgerrors.Wrap(err, "Retrieving model info")
407 tagKey := constructKey(rbName, rbVersion, profileName, instanceID, v.tagTag, tagName)
409 value, err := db.Etcd.Get(tagKey)
411 return "", pkgerrors.Wrap(err, "Config DB Entry Not found")
413 return string(value), nil
416 // ApplyAllConfig starts from first configuration version and applies all versions in sequence
417 func (v *ConfigClient) ApplyAllConfig(instanceID string) error {
419 lock, profileChannel := getProfileData(instanceID)
420 // Acquire per profile Mutex
424 cvs := ConfigVersionStore{
425 instanceID: instanceID,
427 currentVersion, err := cvs.getCurrentVersion()
429 return pkgerrors.Wrap(err, "Get Current Config Version ")
431 if currentVersion < 1 {
432 return pkgerrors.Wrap(err, "No Config Version to Apply")
434 //Apply all configurations
436 for i = 1; i <= currentVersion; i++ {
437 configNew, _, action, err := cvs.getConfigVersion(i)
439 return pkgerrors.Wrap(err, "Get Config Version")
441 err = applyConfig(instanceID, configNew, profileChannel, action)
443 return pkgerrors.Wrap(err, "Apply Config failed")