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  }