gke-deploy/core/resource/application.go (111 lines of code) (raw):

package resource import ( "fmt" "sort" "strings" applicationsv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" ) // CreateApplicationObject creates an Application CR object with the given name and fields. // appVersion may be empty, in which case the resulting Application CR will not have a value for spec.descriptor.version. // To add links, to the Application, use the SetApplicationLinks function on the output object, func CreateApplicationObject(name, selectorKey, selectorValue, descriptorType, descriptorVersion string, componentObjs Objects) (*Object, error) { componentKindsRemoveDups := make(map[metav1.GroupKind]bool) for _, obj := range componentObjs { kind := ObjectKind(obj) if kind == "Namespace" || kind == "Application" { continue } apiVersion := obj.GetAPIVersion() // e.g., v1, apps/v1, autoscaling/v2 apiVersionSplit := strings.Split(apiVersion, "/") var group string if len(apiVersionSplit) == 1 { group = "core" } else { group = apiVersionSplit[0] } componentKindsRemoveDups[metav1.GroupKind{ Group: group, Kind: kind, }] = true } componentKinds := make([]metav1.GroupKind, 0, len(componentKindsRemoveDups)) for k := range componentKindsRemoveDups { componentKinds = append(componentKinds, k) } // Sort to make spec.componentKinds deterministic sort.SliceStable(componentKinds, func(i, j int) bool { a := componentKinds[i] b := componentKinds[j] if a.Group == b.Group { return a.Kind < b.Kind } return a.Group < b.Group }) app := &applicationsv1beta1.Application{ TypeMeta: metav1.TypeMeta{ Kind: "Application", APIVersion: "app.k8s.io/v1beta1", }, ObjectMeta: metav1.ObjectMeta{ Name: name, }, Spec: applicationsv1beta1.ApplicationSpec{ ComponentGroupKinds: componentKinds, Descriptor: applicationsv1beta1.Descriptor{ Type: descriptorType, Version: descriptorVersion, }, Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ selectorKey: selectorValue, }, }, }, } asMap, err := convertApplicationToMap(app) if err != nil { return nil, err } return &Object{ &unstructured.Unstructured{ Object: asMap, }, }, nil } // SetApplicationLinks sets a list of links to an Application object's spec.descriptor.links field. func SetApplicationLinks(obj *Object, links []applicationsv1beta1.Link) error { if ObjectKind(obj) != "Application" { return fmt.Errorf("object must be an Application to add links") } app := &applicationsv1beta1.Application{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Unstructured.Object, app) if err != nil { return fmt.Errorf("failed to convert from unstructured") } app.Spec.Descriptor.Links = links asMap, err := convertApplicationToMap(app) if err != nil { return err } obj.Unstructured.Object = asMap return nil } func convertApplicationToMap(app *applicationsv1beta1.Application) (map[string]interface{}, error) { asMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(app) if err != nil { return nil, fmt.Errorf("failed to convert to unstructured") } // The resulting map will have null/empty values for metadata.creationTimestamp and status. // We remove these from the map to make sure they are not included in the output YAML. metadata, ok := asMap["metadata"] if ok { metadataMap, ok := metadata.(map[string]interface{}) if ok { _, ok := metadataMap["creationTimestamp"] if ok { delete(metadataMap, "creationTimestamp") } } } _, ok = asMap["status"] if ok { delete(asMap, "status") } return asMap, nil }