| /* |
| Copyright 2014 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package kubectl |
| |
| import ( |
| "k8s.io/api/core/v1" |
| "k8s.io/apimachinery/pkg/api/meta" |
| "k8s.io/apimachinery/pkg/runtime" |
| ) |
| |
| var metadataAccessor = meta.NewAccessor() |
| |
| // GetOriginalConfiguration retrieves the original configuration of the object |
| // from the annotation, or nil if no annotation was found. |
| func GetOriginalConfiguration(obj runtime.Object) ([]byte, error) { |
| annots, err := metadataAccessor.Annotations(obj) |
| if err != nil { |
| return nil, err |
| } |
| |
| if annots == nil { |
| return nil, nil |
| } |
| |
| original, ok := annots[v1.LastAppliedConfigAnnotation] |
| if !ok { |
| return nil, nil |
| } |
| |
| return []byte(original), nil |
| } |
| |
| // SetOriginalConfiguration sets the original configuration of the object |
| // as the annotation on the object for later use in computing a three way patch. |
| func setOriginalConfiguration(obj runtime.Object, original []byte) error { |
| if len(original) < 1 { |
| return nil |
| } |
| |
| annots, err := metadataAccessor.Annotations(obj) |
| if err != nil { |
| return err |
| } |
| |
| if annots == nil { |
| annots = map[string]string{} |
| } |
| |
| annots[v1.LastAppliedConfigAnnotation] = string(original) |
| return metadataAccessor.SetAnnotations(obj, annots) |
| } |
| |
| // GetModifiedConfiguration retrieves the modified configuration of the object. |
| // If annotate is true, it embeds the result as an annotation in the modified |
| // configuration. If an object was read from the command input, it will use that |
| // version of the object. Otherwise, it will use the version from the server. |
| func GetModifiedConfiguration(obj runtime.Object, annotate bool, codec runtime.Encoder) ([]byte, error) { |
| // First serialize the object without the annotation to prevent recursion, |
| // then add that serialization to it as the annotation and serialize it again. |
| var modified []byte |
| |
| // Otherwise, use the server side version of the object. |
| // Get the current annotations from the object. |
| annots, err := metadataAccessor.Annotations(obj) |
| if err != nil { |
| return nil, err |
| } |
| |
| if annots == nil { |
| annots = map[string]string{} |
| } |
| |
| original := annots[v1.LastAppliedConfigAnnotation] |
| delete(annots, v1.LastAppliedConfigAnnotation) |
| if err := metadataAccessor.SetAnnotations(obj, annots); err != nil { |
| return nil, err |
| } |
| |
| modified, err = runtime.Encode(codec, obj) |
| if err != nil { |
| return nil, err |
| } |
| |
| if annotate { |
| annots[v1.LastAppliedConfigAnnotation] = string(modified) |
| if err := metadataAccessor.SetAnnotations(obj, annots); err != nil { |
| return nil, err |
| } |
| |
| modified, err = runtime.Encode(codec, obj) |
| if err != nil { |
| return nil, err |
| } |
| } |
| |
| // Restore the object to its original condition. |
| annots[v1.LastAppliedConfigAnnotation] = original |
| if err := metadataAccessor.SetAnnotations(obj, annots); err != nil { |
| return nil, err |
| } |
| |
| return modified, nil |
| } |
| |
| // UpdateApplyAnnotation calls CreateApplyAnnotation if the last applied |
| // configuration annotation is already present. Otherwise, it does nothing. |
| func UpdateApplyAnnotation(obj runtime.Object, codec runtime.Encoder) error { |
| if original, err := GetOriginalConfiguration(obj); err != nil || len(original) <= 0 { |
| return err |
| } |
| return CreateApplyAnnotation(obj, codec) |
| } |
| |
| // CreateApplyAnnotation gets the modified configuration of the object, |
| // without embedding it again, and then sets it on the object as the annotation. |
| func CreateApplyAnnotation(obj runtime.Object, codec runtime.Encoder) error { |
| modified, err := GetModifiedConfiguration(obj, false, codec) |
| if err != nil { |
| return err |
| } |
| return setOriginalConfiguration(obj, modified) |
| } |
| |
| // CreateOrUpdateAnnotation creates the annotation used by |
| // kubectl apply only when createAnnotation is true |
| // Otherwise, only update the annotation when it already exists |
| func CreateOrUpdateAnnotation(createAnnotation bool, obj runtime.Object, codec runtime.Encoder) error { |
| if createAnnotation { |
| return CreateApplyAnnotation(obj, codec) |
| } |
| return UpdateApplyAnnotation(obj, codec) |
| } |