github.com/oam-dev/kubevela@v1.9.11/references/appfile/app.go (about) 1 /* 2 Copyright 2021 The KubeVela Authors. 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 appfile 18 19 import ( 20 "context" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "reflect" 25 "sort" 26 27 "sigs.k8s.io/controller-runtime/pkg/client" 28 29 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 30 "github.com/oam-dev/kubevela/pkg/utils/common" 31 "github.com/oam-dev/kubevela/references/appfile/api" 32 "github.com/oam-dev/kubevela/references/appfile/template" 33 ) 34 35 // NewEmptyApplication new empty application, only set tm 36 func NewEmptyApplication(namespace string, c common.Args) (*api.Application, error) { 37 tm, err := template.Load(namespace, c) 38 if err != nil { 39 return nil, err 40 } 41 return NewApplication(nil, tm), nil 42 } 43 44 // NewApplication will create application object 45 func NewApplication(f *api.AppFile, tm template.Manager) *api.Application { 46 if f == nil { 47 f = api.NewAppFile() 48 } 49 return &api.Application{AppFile: f, Tm: tm} 50 } 51 52 // Validate will validate whether an Appfile is valid. 53 func Validate(app *api.Application) error { 54 if app.Name == "" { 55 return errors.New("name is required") 56 } 57 if len(app.Services) == 0 { 58 return errors.New("at least one service is required") 59 } 60 for name, svc := range app.Services { 61 for traitName, traitData := range svc.GetApplicationConfig() { 62 if app.Tm.IsTrait(traitName) { 63 if _, ok := traitData.(map[string]interface{}); !ok { 64 return fmt.Errorf("trait %s in '%s' must be map", traitName, name) 65 } 66 } 67 } 68 } 69 return nil 70 } 71 72 // LoadApplication will load application from cluster. 73 func LoadApplication(namespace, appName string, c common.Args) (*v1beta1.Application, error) { 74 newClient, err := c.GetClient() 75 if err != nil { 76 return nil, err 77 } 78 app := &v1beta1.Application{} 79 if err := newClient.Get(context.TODO(), client.ObjectKey{Namespace: namespace, Name: appName}, app); err != nil { 80 return nil, fmt.Errorf("failed to load application %s from namespace %s: %w", appName, namespace, err) 81 } 82 return app, nil 83 } 84 85 // GetComponents will get oam components from v1beta1.Application. 86 func GetComponents(app *v1beta1.Application) []string { 87 var components []string 88 for _, cmp := range app.Spec.Components { 89 components = append(components, cmp.Name) 90 } 91 sort.Strings(components) 92 return components 93 } 94 95 // GetServiceConfig will get service type and it's configuration 96 func GetServiceConfig(app *api.Application, componentName string) (string, map[string]interface{}) { 97 svc, ok := app.Services[componentName] 98 if !ok { 99 return "", make(map[string]interface{}) 100 } 101 return svc.GetType(), svc.GetApplicationConfig() 102 } 103 104 // GetApplicationSettings will get service type and it's configuration 105 func GetApplicationSettings(app *v1beta1.Application, componentName string) (string, map[string]interface{}) { 106 for _, comp := range app.Spec.Components { 107 if comp.Name == componentName { 108 data := map[string]interface{}{} 109 if comp.Properties != nil { 110 _ = json.Unmarshal(comp.Properties.Raw, &data) 111 } 112 return comp.Type, data 113 } 114 } 115 return "", make(map[string]interface{}) 116 } 117 118 // GetWorkload will get workload type and it's configuration 119 func GetWorkload(app *api.Application, componentName string) (string, map[string]interface{}) { 120 svcType, config := GetServiceConfig(app, componentName) 121 if svcType == "" { 122 return "", make(map[string]interface{}) 123 } 124 workloadData := make(map[string]interface{}) 125 for k, v := range config { 126 if app.Tm.IsTrait(k) { 127 continue 128 } 129 workloadData[k] = v 130 } 131 return svcType, workloadData 132 } 133 134 // GetTraits will list all traits and it's configurations attached to the specified component. 135 func GetTraits(app *api.Application, componentName string) (map[string]map[string]interface{}, error) { 136 _, config := GetServiceConfig(app, componentName) 137 traitsData := make(map[string]map[string]interface{}) 138 for k, v := range config { 139 if !app.Tm.IsTrait(k) { 140 continue 141 } 142 newV, ok := v.(map[string]interface{}) 143 if !ok { 144 return nil, fmt.Errorf("%s is trait, but with invalid format %s, should be map[string]interface{}", k, reflect.TypeOf(v)) 145 } 146 traitsData[k] = newV 147 } 148 return traitsData, nil 149 }