mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-06 11:41:41 +00:00
191 lines
5.7 KiB
Go
191 lines
5.7 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
|
|
"github.com/ghodss/yaml"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
|
"github.com/oam-dev/kubevela/apis/types"
|
|
"github.com/oam-dev/kubevela/pkg/appfile/config"
|
|
"github.com/oam-dev/kubevela/pkg/appfile/template"
|
|
"github.com/oam-dev/kubevela/pkg/builtin"
|
|
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
|
|
"github.com/oam-dev/kubevela/pkg/oam"
|
|
)
|
|
|
|
// error msg used in Appfile
|
|
var (
|
|
ErrImageNotDefined = errors.New("image not defined")
|
|
)
|
|
|
|
// DefaultAppfilePath defines the default file path that used by `vela up` command
|
|
const (
|
|
DefaultJSONAppfilePath = "./vela.json"
|
|
DefaultAppfilePath = "./vela.yaml"
|
|
DefaultUnknowFormatAppfilePath = "./Appfile"
|
|
)
|
|
|
|
// AppFile defines the spec of KubeVela Appfile
|
|
type AppFile struct {
|
|
Name string `json:"name"`
|
|
CreateTime time.Time `json:"createTime,omitempty"`
|
|
UpdateTime time.Time `json:"updateTime,omitempty"`
|
|
Services map[string]Service `json:"services"`
|
|
Secrets map[string]string `json:"secrets,omitempty"`
|
|
|
|
configGetter config.Store
|
|
initialized bool
|
|
}
|
|
|
|
// NewAppFile init an empty AppFile struct
|
|
func NewAppFile() *AppFile {
|
|
return &AppFile{
|
|
Services: make(map[string]Service),
|
|
Secrets: make(map[string]string),
|
|
configGetter: &config.Local{},
|
|
}
|
|
}
|
|
|
|
// Load will load appfile from default path
|
|
func Load() (*AppFile, error) {
|
|
if _, err := os.Stat(DefaultAppfilePath); err == nil {
|
|
return LoadFromFile(DefaultAppfilePath)
|
|
}
|
|
if _, err := os.Stat(DefaultJSONAppfilePath); err == nil {
|
|
return LoadFromFile(DefaultJSONAppfilePath)
|
|
}
|
|
return LoadFromFile(DefaultUnknowFormatAppfilePath)
|
|
}
|
|
|
|
// JSONToYaml will convert JSON format appfile to yaml and load the AppFile struct
|
|
func JSONToYaml(data []byte, appFile *AppFile) (*AppFile, error) {
|
|
j, e := yaml.JSONToYAML(data)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
err := yaml.Unmarshal(j, appFile)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return appFile, nil
|
|
}
|
|
|
|
// LoadFromFile will read the file and load the AppFile struct
|
|
func LoadFromFile(filename string) (*AppFile, error) {
|
|
b, err := ioutil.ReadFile(filepath.Clean(filename))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
af := NewAppFile()
|
|
// Add JSON format appfile support
|
|
ext := filepath.Ext(filename)
|
|
switch ext {
|
|
case ".yaml", ".yml":
|
|
err = yaml.Unmarshal(b, af)
|
|
case ".json":
|
|
af, err = JSONToYaml(b, af)
|
|
default:
|
|
if json.Valid(b) {
|
|
af, err = JSONToYaml(b, af)
|
|
} else {
|
|
err = yaml.Unmarshal(b, af)
|
|
}
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return af, nil
|
|
}
|
|
|
|
// ExecuteAppfileTasks will execute built-in tasks(such as image builder, etc.) and generate locally executed application
|
|
func (app *AppFile) ExecuteAppfileTasks(io cmdutil.IOStreams) error {
|
|
if app.initialized {
|
|
return nil
|
|
}
|
|
for name, svc := range app.Services {
|
|
newSvc, err := builtin.RunBuildInTasks(svc, io)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
app.Services[name] = newSvc
|
|
}
|
|
app.initialized = true
|
|
return nil
|
|
}
|
|
|
|
// BuildOAMApplication renders Appfile into Application, Scopes and other K8s Resources.
|
|
func (app *AppFile) BuildOAMApplication(env *types.EnvMeta, io cmdutil.IOStreams, tm template.Manager, silence bool) (*v1alpha2.Application, []oam.Object, error) {
|
|
if err := app.ExecuteAppfileTasks(io); err != nil {
|
|
if strings.Contains(err.Error(), "'image' : not found") {
|
|
return nil, nil, ErrImageNotDefined
|
|
}
|
|
return nil, nil, err
|
|
}
|
|
// auxiliaryObjects currently include OAM Scope Custom Resources and ConfigMaps
|
|
var auxiliaryObjects []oam.Object
|
|
servApp := new(v1alpha2.Application)
|
|
servApp.SetNamespace(env.Namespace)
|
|
servApp.SetName(app.Name)
|
|
servApp.Spec.Components = []v1alpha2.ApplicationComponent{}
|
|
for serviceName, svc := range app.GetServices() {
|
|
if !silence {
|
|
io.Infof("\nRendering configs for service (%s)...\n", serviceName)
|
|
}
|
|
configname := svc.GetUserConfigName()
|
|
if configname != "" {
|
|
configData, err := app.configGetter.GetConfigData(configname, env.Name)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
decodedData, err := config.DecodeConfigFormat(configData)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
cm, err := config.ToConfigMap(app.configGetter, config.GenConfigMapName(app.Name, serviceName, configname), env.Name, decodedData)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
auxiliaryObjects = append(auxiliaryObjects, cm)
|
|
}
|
|
comp, err := svc.RenderServiceToApplicationComponent(tm, serviceName)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
servApp.Spec.Components = append(servApp.Spec.Components, comp)
|
|
}
|
|
servApp.SetGroupVersionKind(v1alpha2.SchemeGroupVersion.WithKind("Application"))
|
|
auxiliaryObjects = append(auxiliaryObjects, addDefaultHealthScopeToApplication(servApp))
|
|
return servApp, auxiliaryObjects, nil
|
|
}
|
|
|
|
func addDefaultHealthScopeToApplication(app *v1alpha2.Application) *v1alpha2.HealthScope {
|
|
health := &v1alpha2.HealthScope{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: v1alpha2.HealthScopeGroupVersionKind.GroupVersion().String(),
|
|
Kind: v1alpha2.HealthScopeKind,
|
|
},
|
|
}
|
|
health.Name = FormatDefaultHealthScopeName(app.Name)
|
|
health.Namespace = app.Namespace
|
|
health.Spec.WorkloadReferences = make([]v1alpha1.TypedReference, 0)
|
|
for i := range app.Spec.Components {
|
|
// FIXME(wonderflow): the hardcode health scope should be fixed.
|
|
app.Spec.Components[i].Scopes = map[string]string{"healthscopes.core.oam.dev": health.Name}
|
|
}
|
|
return health
|
|
}
|
|
|
|
// FormatDefaultHealthScopeName will create a default health scope name.
|
|
func FormatDefaultHealthScopeName(appName string) string {
|
|
return appName + "-default-health"
|
|
}
|