mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
vendor files
This commit is contained in:
35
vendor/k8s.io/kubernetes/pkg/kubectl/apply/BUILD
generated
vendored
Normal file
35
vendor/k8s.io/kubernetes/pkg/kubectl/apply/BUILD
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"element.go",
|
||||
"empty_element.go",
|
||||
"list_element.go",
|
||||
"map_element.go",
|
||||
"primitive_element.go",
|
||||
"type_element.go",
|
||||
"visitor.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/apply",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/kubectl/apply/parse:all-srcs",
|
||||
"//pkg/kubectl/apply/strategy:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
34
vendor/k8s.io/kubernetes/pkg/kubectl/apply/doc.go
generated
vendored
Normal file
34
vendor/k8s.io/kubernetes/pkg/kubectl/apply/doc.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright 2017 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 apply
|
||||
|
||||
// This package is used for creating and applying patches generated
|
||||
// from a last recorded config, local config, remote object.
|
||||
// Example usage for a test:
|
||||
//
|
||||
//fakeSchema := tst.Fake{Path: swaggerPath}
|
||||
//s, err := fakeSchema.OpenAPISchema()
|
||||
//Expect(err).To(BeNil())
|
||||
//resources, err := openapi.NewOpenAPIData(s)
|
||||
//Expect(err).To(BeNil())
|
||||
//elementParser := parse.Factory{resources}
|
||||
//
|
||||
//obj, err := parser.CreateElement(recorded, local, remote)
|
||||
//Expect(err).Should(Not(HaveOccurred()))
|
||||
//
|
||||
//merged, err := obj.Merge(strategy.Create(strategy.Options{}))
|
||||
//
|
421
vendor/k8s.io/kubernetes/pkg/kubectl/apply/element.go
generated
vendored
Normal file
421
vendor/k8s.io/kubernetes/pkg/kubectl/apply/element.go
generated
vendored
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
Copyright 2017 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 apply
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Element contains the record, local, and remote value for a field in an object
|
||||
// and metadata about the field read from openapi.
|
||||
// Calling Merge on an element will apply the passed in strategy to Element -
|
||||
// e.g. either replacing the whole element with the local copy or merging each
|
||||
// of the recorded, local and remote fields of the element.
|
||||
type Element interface {
|
||||
// FieldMeta specifies which merge strategy to use for this element
|
||||
FieldMeta
|
||||
|
||||
// Merge merges the recorded, local and remote values in the element using the Strategy
|
||||
// provided as an argument. Calls the type specific method on the Strategy - following the
|
||||
// "Accept" method from the "Visitor" pattern.
|
||||
// e.g. Merge on a ListElement will call Strategy.MergeList(self)
|
||||
// Returns the Result of the merged elements
|
||||
Merge(Strategy) (Result, error)
|
||||
|
||||
// HasRecorded returns true if the field was explicitly
|
||||
// present in the recorded source. This is to differentiate between
|
||||
// undefined and set to null
|
||||
HasRecorded() bool
|
||||
|
||||
// GetRecorded returns the field value from the recorded source of the object
|
||||
GetRecorded() interface{}
|
||||
|
||||
// HasLocal returns true if the field was explicitly
|
||||
// present in the recorded source. This is to differentiate between
|
||||
// undefined and set to null
|
||||
HasLocal() bool
|
||||
|
||||
// GetLocal returns the field value from the local source of the object
|
||||
GetLocal() interface{}
|
||||
|
||||
// HasRemote returns true if the field was explicitly
|
||||
// present in the remote source. This is to differentiate between
|
||||
// undefined and set to null
|
||||
HasRemote() bool
|
||||
|
||||
// GetRemote returns the field value from the remote source of the object
|
||||
GetRemote() interface{}
|
||||
}
|
||||
|
||||
// FieldMeta defines the strategy used to apply a Patch for an element
|
||||
type FieldMeta interface {
|
||||
// GetFieldMergeType returns the type of merge strategy to use for this field
|
||||
// maybe "merge", "replace" or "retainkeys"
|
||||
// TODO: There maybe multiple strategies, so this may need to be a slice, map, or struct
|
||||
// Address this in a follow up in the PR to introduce retainkeys strategy
|
||||
GetFieldMergeType() string
|
||||
|
||||
// GetFieldMergeKeys returns the merge key to use when the MergeType is "merge" and underlying type is a list
|
||||
GetFieldMergeKeys() MergeKeys
|
||||
|
||||
// GetFieldType returns the openapi field type - e.g. primitive, array, map, type, reference
|
||||
GetFieldType() string
|
||||
}
|
||||
|
||||
// FieldMetaImpl implements FieldMeta
|
||||
type FieldMetaImpl struct {
|
||||
// MergeType is the type of merge strategy to use for this field
|
||||
// maybe "merge", "replace" or "retainkeys"
|
||||
MergeType string
|
||||
|
||||
// MergeKeys are the merge keys to use when the MergeType is "merge" and underlying type is a list
|
||||
MergeKeys MergeKeys
|
||||
|
||||
// Type is the openapi type of the field - "list", "primitive", "map"
|
||||
Type string
|
||||
|
||||
// Name contains of the field
|
||||
Name string
|
||||
}
|
||||
|
||||
// GetFieldMergeType implements FieldMeta.GetFieldMergeType
|
||||
func (s FieldMetaImpl) GetFieldMergeType() string {
|
||||
return s.MergeType
|
||||
}
|
||||
|
||||
// GetFieldMergeKeys implements FieldMeta.GetFieldMergeKeys
|
||||
func (s FieldMetaImpl) GetFieldMergeKeys() MergeKeys {
|
||||
return s.MergeKeys
|
||||
}
|
||||
|
||||
// GetFieldType implements FieldMeta.GetFieldType
|
||||
func (s FieldMetaImpl) GetFieldType() string {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// MergeKeyValue records the value of the mergekey for an item in a list
|
||||
type MergeKeyValue map[string]string
|
||||
|
||||
// Equal returns true if the MergeKeyValues share the same value,
|
||||
// representing the same item in a list
|
||||
func (v MergeKeyValue) Equal(o MergeKeyValue) bool {
|
||||
if len(v) != len(o) {
|
||||
return false
|
||||
}
|
||||
|
||||
for key, v1 := range v {
|
||||
if v2, found := o[key]; !found || v1 != v2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// MergeKeys is the set of fields on an object that uniquely identify
|
||||
// and is used when merging lists to identify the "same" object
|
||||
// independent of the ordering of the objects
|
||||
type MergeKeys []string
|
||||
|
||||
// GetMergeKeyValue parses the MergeKeyValue from an item in a list
|
||||
func (mk MergeKeys) GetMergeKeyValue(i interface{}) (MergeKeyValue, error) {
|
||||
result := MergeKeyValue{}
|
||||
if len(mk) <= 0 {
|
||||
return result, fmt.Errorf("merge key must have at least 1 value to merge")
|
||||
}
|
||||
m, ok := i.(map[string]interface{})
|
||||
if !ok {
|
||||
return result, fmt.Errorf("cannot use mergekey %v for primitive item in list %v", mk, i)
|
||||
}
|
||||
for _, field := range mk {
|
||||
if value, found := m[field]; !found {
|
||||
result[field] = ""
|
||||
} else {
|
||||
result[field] = fmt.Sprintf("%v", value)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type source int
|
||||
|
||||
const (
|
||||
recorded source = iota
|
||||
local
|
||||
remote
|
||||
)
|
||||
|
||||
// CombinedPrimitiveSlice implements a slice of primitives
|
||||
type CombinedPrimitiveSlice struct {
|
||||
Items []*PrimitiveListItem
|
||||
}
|
||||
|
||||
// PrimitiveListItem represents a single value in a slice of primitives
|
||||
type PrimitiveListItem struct {
|
||||
// Value is the value of the primitive, should match recorded, local and remote
|
||||
Value interface{}
|
||||
|
||||
RawElementData
|
||||
}
|
||||
|
||||
// Contains returns true if the slice contains the l
|
||||
func (s *CombinedPrimitiveSlice) lookup(l interface{}) *PrimitiveListItem {
|
||||
val := fmt.Sprintf("%v", l)
|
||||
for _, i := range s.Items {
|
||||
if fmt.Sprintf("%v", i.Value) == val {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CombinedPrimitiveSlice) upsert(l interface{}) *PrimitiveListItem {
|
||||
// Return the item if it exists
|
||||
if item := s.lookup(l); item != nil {
|
||||
return item
|
||||
}
|
||||
|
||||
// Otherwise create a new item and append to the list
|
||||
item := &PrimitiveListItem{
|
||||
Value: l,
|
||||
}
|
||||
s.Items = append(s.Items, item)
|
||||
return item
|
||||
}
|
||||
|
||||
// UpsertRecorded adds l to the slice. If there is already a value of l in the
|
||||
// slice for either the local or remote, set on that value as the recorded value
|
||||
// Otherwise append a new item to the list with the recorded value.
|
||||
func (s *CombinedPrimitiveSlice) UpsertRecorded(l interface{}) {
|
||||
v := s.upsert(l)
|
||||
v.recorded = l
|
||||
v.recordedSet = true
|
||||
}
|
||||
|
||||
// UpsertLocal adds l to the slice. If there is already a value of l in the
|
||||
// slice for either the recorded or remote, set on that value as the local value
|
||||
// Otherwise append a new item to the list with the local value.
|
||||
func (s *CombinedPrimitiveSlice) UpsertLocal(l interface{}) {
|
||||
v := s.upsert(l)
|
||||
v.local = l
|
||||
v.localSet = true
|
||||
}
|
||||
|
||||
// UpsertRemote adds l to the slice. If there is already a value of l in the
|
||||
// slice for either the local or recorded, set on that value as the remote value
|
||||
// Otherwise append a new item to the list with the remote value.
|
||||
func (s *CombinedPrimitiveSlice) UpsertRemote(l interface{}) {
|
||||
v := s.upsert(l)
|
||||
v.remote = l
|
||||
v.remoteSet = true
|
||||
}
|
||||
|
||||
// ListItem represents a single value in a slice of maps or types
|
||||
type ListItem struct {
|
||||
// KeyValue is the merge key value of the item
|
||||
KeyValue MergeKeyValue
|
||||
|
||||
// RawElementData contains the field values
|
||||
RawElementData
|
||||
}
|
||||
|
||||
// CombinedMapSlice is a slice of maps or types with merge keys
|
||||
type CombinedMapSlice struct {
|
||||
Items []*ListItem
|
||||
}
|
||||
|
||||
// Lookup returns the ListItem matching the merge key, or nil if not found.
|
||||
func (s *CombinedMapSlice) lookup(v MergeKeyValue) *ListItem {
|
||||
for _, i := range s.Items {
|
||||
if i.KeyValue.Equal(v) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CombinedMapSlice) upsert(key MergeKeys, l interface{}) (*ListItem, error) {
|
||||
// Get the identity of the item
|
||||
val, err := key.GetMergeKeyValue(l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return the item if it exists
|
||||
if item := s.lookup(val); item != nil {
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// Otherwise create a new item and append to the list
|
||||
item := &ListItem{
|
||||
KeyValue: val,
|
||||
}
|
||||
s.Items = append(s.Items, item)
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// UpsertRecorded adds l to the slice. If there is already a value of l sharing
|
||||
// l's merge key in the slice for either the local or remote, set l the recorded value
|
||||
// Otherwise append a new item to the list with the recorded value.
|
||||
func (s *CombinedMapSlice) UpsertRecorded(key MergeKeys, l interface{}) error {
|
||||
item, err := s.upsert(key, l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
item.recorded = l
|
||||
item.recordedSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpsertLocal adds l to the slice. If there is already a value of l sharing
|
||||
// l's merge key in the slice for either the recorded or remote, set l the local value
|
||||
// Otherwise append a new item to the list with the local value.
|
||||
func (s *CombinedMapSlice) UpsertLocal(key MergeKeys, l interface{}) error {
|
||||
item, err := s.upsert(key, l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
item.local = l
|
||||
item.localSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpsertRemote adds l to the slice. If there is already a value of l sharing
|
||||
// l's merge key in the slice for either the recorded or local, set l the remote value
|
||||
// Otherwise append a new item to the list with the remote value.
|
||||
func (s *CombinedMapSlice) UpsertRemote(key MergeKeys, l interface{}) error {
|
||||
item, err := s.upsert(key, l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
item.remote = l
|
||||
item.remoteSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsDrop returns true if the field represented by e should be dropped from the merged object
|
||||
func IsDrop(e Element) bool {
|
||||
// Specified in the last value recorded value and since deleted from the local
|
||||
removed := e.HasRecorded() && !e.HasLocal()
|
||||
|
||||
// Specified locally and explicitly set to null
|
||||
setToNil := e.HasLocal() && e.GetLocal() == nil
|
||||
|
||||
return removed || setToNil
|
||||
}
|
||||
|
||||
// IsAdd returns true if the field represented by e should have the local value directly
|
||||
// added to the merged object instead of merging the recorded, local and remote values
|
||||
func IsAdd(e Element) bool {
|
||||
// If it isn't already present in the remote value and is present in the local value
|
||||
return e.HasLocal() && !e.HasRemote()
|
||||
}
|
||||
|
||||
// NewRawElementData returns a new RawElementData, setting IsSet to true for
|
||||
// non-nil values, and leaving IsSet false for nil values.
|
||||
// Note: use this only when you want a nil-value to be considered "unspecified"
|
||||
// (ignore) and not "unset" (deleted).
|
||||
func NewRawElementData(recorded, local, remote interface{}) RawElementData {
|
||||
data := RawElementData{}
|
||||
if recorded != nil {
|
||||
data.SetRecorded(recorded)
|
||||
}
|
||||
if local != nil {
|
||||
data.SetLocal(local)
|
||||
}
|
||||
if remote != nil {
|
||||
data.SetRemote(remote)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// RawElementData contains the raw recorded, local and remote data
|
||||
// and metadata about whethere or not each was set
|
||||
type RawElementData struct {
|
||||
HasElementData
|
||||
|
||||
recorded interface{}
|
||||
local interface{}
|
||||
remote interface{}
|
||||
}
|
||||
|
||||
// SetRecorded sets the recorded value
|
||||
func (b *RawElementData) SetRecorded(value interface{}) {
|
||||
b.recorded = value
|
||||
b.recordedSet = true
|
||||
}
|
||||
|
||||
// SetLocal sets the recorded value
|
||||
func (b *RawElementData) SetLocal(value interface{}) {
|
||||
b.local = value
|
||||
b.localSet = true
|
||||
}
|
||||
|
||||
// SetRemote sets the recorded value
|
||||
func (b *RawElementData) SetRemote(value interface{}) {
|
||||
b.remote = value
|
||||
b.remoteSet = true
|
||||
}
|
||||
|
||||
// GetRecorded implements Element.GetRecorded
|
||||
func (b RawElementData) GetRecorded() interface{} {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if b.recorded == nil {
|
||||
return nil
|
||||
}
|
||||
return b.recorded
|
||||
}
|
||||
|
||||
// GetLocal implements Element.GetLocal
|
||||
func (b RawElementData) GetLocal() interface{} {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if b.local == nil {
|
||||
return nil
|
||||
}
|
||||
return b.local
|
||||
}
|
||||
|
||||
// GetRemote implements Element.GetRemote
|
||||
func (b RawElementData) GetRemote() interface{} {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if b.remote == nil {
|
||||
return nil
|
||||
}
|
||||
return b.remote
|
||||
}
|
||||
|
||||
// HasElementData contains whether a field was set in the recorded, local and remote sources
|
||||
type HasElementData struct {
|
||||
recordedSet bool
|
||||
localSet bool
|
||||
remoteSet bool
|
||||
}
|
||||
|
||||
// HasRecorded implements Element.HasRecorded
|
||||
func (e HasElementData) HasRecorded() bool {
|
||||
return e.recordedSet
|
||||
}
|
||||
|
||||
// HasLocal implements Element.HasLocal
|
||||
func (e HasElementData) HasLocal() bool {
|
||||
return e.localSet
|
||||
}
|
||||
|
||||
// HasRemote implements Element.HasRemote
|
||||
func (e HasElementData) HasRemote() bool {
|
||||
return e.remoteSet
|
||||
}
|
70
vendor/k8s.io/kubernetes/pkg/kubectl/apply/empty_element.go
generated
vendored
Normal file
70
vendor/k8s.io/kubernetes/pkg/kubectl/apply/empty_element.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright 2017 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 apply
|
||||
|
||||
// EmptyElement is a placeholder for when no value is set for a field so its type is unknown
|
||||
type EmptyElement struct {
|
||||
// FieldMetaImpl contains metadata about the field from openapi
|
||||
FieldMetaImpl
|
||||
}
|
||||
|
||||
// Merge implements Element.Merge
|
||||
func (e EmptyElement) Merge(v Strategy) (Result, error) {
|
||||
return v.MergeEmpty(e)
|
||||
}
|
||||
|
||||
// IsAdd implements Element.IsAdd
|
||||
func (e EmptyElement) IsAdd() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDelete implements Element.IsDelete
|
||||
func (e EmptyElement) IsDelete() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetRecorded implements Element.GetRecorded
|
||||
func (e EmptyElement) GetRecorded() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLocal implements Element.GetLocal
|
||||
func (e EmptyElement) GetLocal() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRemote implements Element.GetRemote
|
||||
func (e EmptyElement) GetRemote() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasRecorded implements Element.HasRecorded
|
||||
func (e EmptyElement) HasRecorded() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// HasLocal implements Element.HasLocal
|
||||
func (e EmptyElement) HasLocal() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// HasRemote implements Element.IsAdd
|
||||
func (e EmptyElement) HasRemote() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var _ Element = &EmptyElement{}
|
67
vendor/k8s.io/kubernetes/pkg/kubectl/apply/list_element.go
generated
vendored
Normal file
67
vendor/k8s.io/kubernetes/pkg/kubectl/apply/list_element.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright 2017 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 apply
|
||||
|
||||
// ListElement contains the recorded, local and remote values for a field
|
||||
// of type list
|
||||
type ListElement struct {
|
||||
// FieldMetaImpl contains metadata about the field from openapi
|
||||
FieldMetaImpl
|
||||
|
||||
ListElementData
|
||||
|
||||
// Values contains the combined recorded-local-remote value of each item in the list
|
||||
// Present for lists that can be merged only. Contains the items
|
||||
// from each of the 3 lists merged into single Elements using
|
||||
// the merge-key.
|
||||
Values []Element
|
||||
}
|
||||
|
||||
// Merge implements Element.Merge
|
||||
func (e ListElement) Merge(v Strategy) (Result, error) {
|
||||
return v.MergeList(e)
|
||||
}
|
||||
|
||||
var _ Element = &ListElement{}
|
||||
|
||||
// ListElementData contains the recorded, local and remote data for a list
|
||||
type ListElementData struct {
|
||||
RawElementData
|
||||
}
|
||||
|
||||
// GetRecordedList returns the Recorded value as a list
|
||||
func (e ListElementData) GetRecordedList() []interface{} {
|
||||
return sliceCast(e.recorded)
|
||||
}
|
||||
|
||||
// GetLocalList returns the Local value as a list
|
||||
func (e ListElementData) GetLocalList() []interface{} {
|
||||
return sliceCast(e.local)
|
||||
}
|
||||
|
||||
// GetRemoteList returns the Remote value as a list
|
||||
func (e ListElementData) GetRemoteList() []interface{} {
|
||||
return sliceCast(e.remote)
|
||||
}
|
||||
|
||||
// sliceCast casts i to a slice if it is non-nil, otherwise returns nil
|
||||
func sliceCast(i interface{}) []interface{} {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
return i.([]interface{})
|
||||
}
|
72
vendor/k8s.io/kubernetes/pkg/kubectl/apply/map_element.go
generated
vendored
Normal file
72
vendor/k8s.io/kubernetes/pkg/kubectl/apply/map_element.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright 2017 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 apply
|
||||
|
||||
// MapElement contains the recorded, local and remote values for a field
|
||||
// of type map
|
||||
type MapElement struct {
|
||||
// FieldMetaImpl contains metadata about the field from openapi
|
||||
FieldMetaImpl
|
||||
|
||||
// MapElementData contains the value a field was set to
|
||||
MapElementData
|
||||
|
||||
// Values contains the combined recorded-local-remote value of each item in the map
|
||||
// Values contains the values in mapElement. Element must contain
|
||||
// a Name matching its key in Values
|
||||
Values map[string]Element
|
||||
}
|
||||
|
||||
// Merge implements Element.Merge
|
||||
func (e MapElement) Merge(v Strategy) (Result, error) {
|
||||
return v.MergeMap(e)
|
||||
}
|
||||
|
||||
// GetValues implements Element.GetValues
|
||||
func (e MapElement) GetValues() map[string]Element {
|
||||
return e.Values
|
||||
}
|
||||
|
||||
var _ Element = &MapElement{}
|
||||
|
||||
// MapElementData contains the recorded, local and remote data for a map or type
|
||||
type MapElementData struct {
|
||||
RawElementData
|
||||
}
|
||||
|
||||
// GetRecordedMap returns the Recorded value as a map
|
||||
func (e MapElementData) GetRecordedMap() map[string]interface{} {
|
||||
return mapCast(e.recorded)
|
||||
}
|
||||
|
||||
// GetLocalMap returns the Local value as a map
|
||||
func (e MapElementData) GetLocalMap() map[string]interface{} {
|
||||
return mapCast(e.local)
|
||||
}
|
||||
|
||||
// GetRemoteMap returns the Remote value as a map
|
||||
func (e MapElementData) GetRemoteMap() map[string]interface{} {
|
||||
return mapCast(e.remote)
|
||||
}
|
||||
|
||||
// mapCast casts i to a map if it is non-nil, otherwise returns nil
|
||||
func mapCast(i interface{}) map[string]interface{} {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
return i.(map[string]interface{})
|
||||
}
|
53
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/BUILD
generated
vendored
Normal file
53
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/BUILD
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"factory.go",
|
||||
"item.go",
|
||||
"list_element.go",
|
||||
"map_element.go",
|
||||
"openapi.go",
|
||||
"primitive_element.go",
|
||||
"type_element.go",
|
||||
"util.go",
|
||||
"visitor.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/apply/parse",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/kubectl/apply:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_xtest",
|
||||
srcs = ["suite_test.go"],
|
||||
data = [
|
||||
"//api/openapi-spec:swagger-spec",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/apply/parse_test",
|
||||
deps = [
|
||||
"//vendor/github.com/onsi/ginkgo:go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo/config:go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo/types:go_default_library",
|
||||
"//vendor/github.com/onsi/gomega:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
120
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/factory.go
generated
vendored
Normal file
120
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/factory.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright 2017 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 parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
)
|
||||
|
||||
// Factory creates an Element by combining object values from recorded, local and remote sources with
|
||||
// the metadata from an openapi schema.
|
||||
type Factory struct {
|
||||
// Resources contains the openapi field metadata for the object models
|
||||
Resources openapi.Resources
|
||||
}
|
||||
|
||||
// CreateElement returns an Element by collating the recorded, local and remote field values
|
||||
func (b *Factory) CreateElement(recorded, local, remote map[string]interface{}) (apply.Element, error) {
|
||||
// Create an Item from the 3 values. Use empty name for field
|
||||
visitor := &ElementBuildingVisitor{b.Resources}
|
||||
|
||||
gvk, err := getCommonGroupVersionKind(recorded, local, remote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the openapi object metadata
|
||||
s := visitor.resources.LookupResource(gvk)
|
||||
oapiKind, err := getKind(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := apply.NewRawElementData(recorded, local, remote)
|
||||
fieldName := ""
|
||||
item, err := visitor.getItem(oapiKind, fieldName, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Collate each field of the item into a combined Element
|
||||
return item.CreateElement(visitor)
|
||||
}
|
||||
|
||||
// getItem returns the appropriate Item based on the underlying type of the arguments
|
||||
func (v *ElementBuildingVisitor) getItem(s proto.Schema, name string, data apply.RawElementData) (Item, error) {
|
||||
kind, err := getType(data.GetRecorded(), data.GetLocal(), data.GetRemote())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if kind == nil {
|
||||
// All of the items values are nil.
|
||||
return &emptyItem{Name: name}, nil
|
||||
}
|
||||
|
||||
// Create an item matching the type
|
||||
switch kind.Kind() {
|
||||
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint,
|
||||
reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64,
|
||||
reflect.String:
|
||||
p, err := getPrimitive(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("expected openapi Primitive, was %T for %v (%v)", s, kind, err)
|
||||
}
|
||||
return &primitiveItem{name, p, data}, nil
|
||||
case reflect.Array, reflect.Slice:
|
||||
a, err := getArray(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("expected openapi Array, was %T for %v (%v)", s, kind, err)
|
||||
}
|
||||
return &listItem{
|
||||
Name: name,
|
||||
Array: a,
|
||||
ListElementData: apply.ListElementData{
|
||||
RawElementData: data,
|
||||
},
|
||||
}, nil
|
||||
case reflect.Map:
|
||||
if k, err := getKind(s); err == nil {
|
||||
return &typeItem{
|
||||
Name: name,
|
||||
Type: k,
|
||||
MapElementData: apply.MapElementData{
|
||||
RawElementData: data,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
// If it looks like a map, and no openapi type is found, default to mapItem
|
||||
m, err := getMap(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("expected openapi Kind or Map, was %T for %v (%v)", s, kind, err)
|
||||
}
|
||||
return &mapItem{
|
||||
Name: name,
|
||||
Map: m,
|
||||
MapElementData: apply.MapElementData{
|
||||
RawElementData: data,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported type type %v", kind)
|
||||
}
|
120
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/item.go
generated
vendored
Normal file
120
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/item.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright 2017 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 parse
|
||||
|
||||
import (
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// Item wraps values from 3 sources (recorded, local, remote).
|
||||
// The values are not collated
|
||||
type Item interface {
|
||||
// CreateElement merges the values in the item into a combined Element
|
||||
CreateElement(ItemVisitor) (apply.Element, error)
|
||||
}
|
||||
|
||||
// primitiveItem contains a recorded, local, and remote value
|
||||
type primitiveItem struct {
|
||||
Name string
|
||||
Primitive *proto.Primitive
|
||||
|
||||
apply.RawElementData
|
||||
}
|
||||
|
||||
func (i *primitiveItem) CreateElement(v ItemVisitor) (apply.Element, error) {
|
||||
return v.CreatePrimitiveElement(i)
|
||||
}
|
||||
|
||||
func (i *primitiveItem) GetMeta() proto.Schema {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if i.Primitive != nil {
|
||||
return i.Primitive
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// listItem contains a recorded, local, and remote list
|
||||
type listItem struct {
|
||||
Name string
|
||||
Array *proto.Array
|
||||
|
||||
apply.ListElementData
|
||||
}
|
||||
|
||||
func (i *listItem) CreateElement(v ItemVisitor) (apply.Element, error) {
|
||||
return v.CreateListElement(i)
|
||||
}
|
||||
|
||||
func (i *listItem) GetMeta() proto.Schema {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if i.Array != nil {
|
||||
return i.Array
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mapItem contains a recorded, local, and remote map
|
||||
type mapItem struct {
|
||||
Name string
|
||||
Map *proto.Map
|
||||
|
||||
apply.MapElementData
|
||||
}
|
||||
|
||||
func (i *mapItem) CreateElement(v ItemVisitor) (apply.Element, error) {
|
||||
return v.CreateMapElement(i)
|
||||
}
|
||||
|
||||
func (i *mapItem) GetMeta() proto.Schema {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if i.Map != nil {
|
||||
return i.Map
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mapItem contains a recorded, local, and remote map
|
||||
type typeItem struct {
|
||||
Name string
|
||||
Type *proto.Kind
|
||||
|
||||
apply.MapElementData
|
||||
}
|
||||
|
||||
func (i *typeItem) GetMeta() proto.Schema {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if i.Type != nil {
|
||||
return i.Type
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *typeItem) CreateElement(v ItemVisitor) (apply.Element, error) {
|
||||
return v.CreateTypeElement(i)
|
||||
}
|
||||
|
||||
// emptyItem contains no values
|
||||
type emptyItem struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (i *emptyItem) CreateElement(v ItemVisitor) (apply.Element, error) {
|
||||
e := &apply.EmptyElement{}
|
||||
e.Name = i.Name
|
||||
return e, nil
|
||||
}
|
199
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/list_element.go
generated
vendored
Normal file
199
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/list_element.go
generated
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
Copyright 2017 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 parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// Contains the heavy lifting for finding tuples of matching elements in lists based on the merge key
|
||||
// and then uses the canonical order derived from the orders in the recorded, local and remote lists.
|
||||
|
||||
// replaceListElement builds a ListElement for a listItem.
|
||||
// Uses the "merge" strategy to identify "same" elements across lists by a "merge key"
|
||||
func (v ElementBuildingVisitor) mergeListElement(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
|
||||
subtype := getSchemaType(item.Array.SubType)
|
||||
switch subtype {
|
||||
case "primitive":
|
||||
return v.doPrimitiveList(meta, item)
|
||||
case "map", "kind", "reference":
|
||||
return v.doMapList(meta, item)
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot merge lists with subtype %s", subtype)
|
||||
}
|
||||
}
|
||||
|
||||
// doPrimitiveList merges 3 lists of primitives together
|
||||
// tries to maintain ordering
|
||||
func (v ElementBuildingVisitor) doPrimitiveList(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
|
||||
result := &apply.ListElement{
|
||||
FieldMetaImpl: apply.FieldMetaImpl{
|
||||
MergeType: apply.MergeStrategy,
|
||||
Name: item.Name,
|
||||
},
|
||||
ListElementData: item.ListElementData,
|
||||
Values: []apply.Element{},
|
||||
}
|
||||
|
||||
// Use locally defined order, then add remote, then add recorded.
|
||||
orderedKeys := &apply.CombinedPrimitiveSlice{}
|
||||
|
||||
// Locally defined items come first and retain their order
|
||||
// as defined locally
|
||||
for _, l := range item.GetLocalList() {
|
||||
orderedKeys.UpsertLocal(l)
|
||||
}
|
||||
// Mixin remote values, adding any that are not present locally
|
||||
for _, l := range item.GetRemoteList() {
|
||||
orderedKeys.UpsertRemote(l)
|
||||
}
|
||||
// Mixin recorded values, adding any that are not present locally
|
||||
// or remotely
|
||||
for _, l := range item.GetRecordedList() {
|
||||
orderedKeys.UpsertRecorded(l)
|
||||
}
|
||||
|
||||
for i, l := range orderedKeys.Items {
|
||||
var s proto.Schema
|
||||
if item.Array != nil && item.Array.SubType != nil {
|
||||
s = item.Array.SubType
|
||||
}
|
||||
|
||||
subitem, err := v.getItem(s, fmt.Sprintf("%d", i), l.RawElementData)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert the Item to an Element
|
||||
newelem, err := subitem.CreateElement(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Append the element to the list
|
||||
result.Values = append(result.Values, newelem)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// doMapList merges 3 lists of maps together by collating their values.
|
||||
// tries to retain ordering
|
||||
func (v ElementBuildingVisitor) doMapList(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
|
||||
key := meta.GetFieldMergeKeys()
|
||||
result := &apply.ListElement{
|
||||
FieldMetaImpl: apply.FieldMetaImpl{
|
||||
MergeType: apply.MergeStrategy,
|
||||
MergeKeys: key,
|
||||
Name: item.Name,
|
||||
},
|
||||
ListElementData: item.ListElementData,
|
||||
Values: []apply.Element{},
|
||||
}
|
||||
|
||||
// Use locally defined order, then add remote, then add recorded.
|
||||
orderedKeys := &apply.CombinedMapSlice{}
|
||||
|
||||
// Locally defined items come first and retain their order
|
||||
// as defined locally
|
||||
for _, l := range item.GetLocalList() {
|
||||
orderedKeys.UpsertLocal(key, l)
|
||||
}
|
||||
// Mixin remote values, adding any that are not present locally
|
||||
for _, l := range item.GetRemoteList() {
|
||||
orderedKeys.UpsertRemote(key, l)
|
||||
}
|
||||
// Mixin recorded values, adding any that are not present locally
|
||||
// or remotely
|
||||
for _, l := range item.GetRecordedList() {
|
||||
orderedKeys.UpsertRecorded(key, l)
|
||||
}
|
||||
|
||||
for i, l := range orderedKeys.Items {
|
||||
var s proto.Schema
|
||||
if item.Array != nil && item.Array.SubType != nil {
|
||||
s = item.Array.SubType
|
||||
}
|
||||
subitem, err := v.getItem(s, fmt.Sprintf("%d", i), l.RawElementData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build the element fully
|
||||
newelem, err := subitem.CreateElement(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Append the element to the list
|
||||
result.Values = append(result.Values, newelem)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// replaceListElement builds a new ListElement from a listItem
|
||||
// Uses the "replace" strategy and identify "same" elements across lists by their index
|
||||
func (v ElementBuildingVisitor) replaceListElement(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
|
||||
meta.Name = item.Name
|
||||
result := &apply.ListElement{
|
||||
FieldMetaImpl: meta,
|
||||
ListElementData: item.ListElementData,
|
||||
Values: []apply.Element{},
|
||||
}
|
||||
|
||||
// Use the max length to iterate over the slices
|
||||
for i := 0; i < max(len(item.GetRecordedList()), len(item.GetLocalList()), len(item.GetRemoteList())); i++ {
|
||||
|
||||
// Lookup the item from each list
|
||||
data := apply.RawElementData{}
|
||||
if recorded, recordedSet := boundsSafeLookup(i, item.GetRecordedList()); recordedSet {
|
||||
data.SetRecorded(recorded)
|
||||
}
|
||||
if local, localSet := boundsSafeLookup(i, item.GetLocalList()); localSet {
|
||||
data.SetLocal(local)
|
||||
}
|
||||
if remote, remoteSet := boundsSafeLookup(i, item.GetRemoteList()); remoteSet {
|
||||
data.SetRemote(remote)
|
||||
}
|
||||
|
||||
// Create the Item
|
||||
var s proto.Schema
|
||||
if item.Array != nil && item.Array.SubType != nil {
|
||||
s = item.Array.SubType
|
||||
}
|
||||
subitem, err := v.getItem(s, fmt.Sprintf("%d", i), data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build the element
|
||||
newelem, err := subitem.CreateElement(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Append the element to the list
|
||||
result.Values = append(result.Values, newelem)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
89
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/map_element.go
generated
vendored
Normal file
89
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/map_element.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2017 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 parse
|
||||
|
||||
import (
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// mapElement builds a new mapElement from a mapItem
|
||||
func (v ElementBuildingVisitor) mapElement(meta apply.FieldMetaImpl, item *mapItem) (*apply.MapElement, error) {
|
||||
// Function to return schema type of the map values
|
||||
var fn schemaFn = func(string) proto.Schema {
|
||||
// All map values share the same schema
|
||||
if item.Map != nil && item.Map.SubType != nil {
|
||||
return item.Map.SubType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect same fields from multiple maps into a map of elements
|
||||
values, err := v.createMapValues(fn, meta, item.MapElementData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return the result
|
||||
return &apply.MapElement{
|
||||
FieldMetaImpl: meta,
|
||||
MapElementData: item.MapElementData,
|
||||
Values: values,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// schemaFn returns the schema for a field or map value based on its name or key
|
||||
type schemaFn func(key string) proto.Schema
|
||||
|
||||
// createMapValues combines the recorded, local and remote values from
|
||||
// data into a map of elements.
|
||||
func (v ElementBuildingVisitor) createMapValues(
|
||||
schemaFn schemaFn,
|
||||
meta apply.FieldMetaImpl,
|
||||
data apply.MapElementData) (map[string]apply.Element, error) {
|
||||
|
||||
// Collate each key in the map
|
||||
values := map[string]apply.Element{}
|
||||
for _, key := range keysUnion(data.GetRecordedMap(), data.GetLocalMap(), data.GetRemoteMap()) {
|
||||
combined := apply.RawElementData{}
|
||||
if recorded, recordedSet := nilSafeLookup(key, data.GetRecordedMap()); recordedSet {
|
||||
combined.SetRecorded(recorded)
|
||||
}
|
||||
if local, localSet := nilSafeLookup(key, data.GetLocalMap()); localSet {
|
||||
combined.SetLocal(local)
|
||||
}
|
||||
if remote, remoteSet := nilSafeLookup(key, data.GetRemoteMap()); remoteSet {
|
||||
combined.SetRemote(remote)
|
||||
}
|
||||
|
||||
// Create an item for the field
|
||||
field, err := v.getItem(schemaFn(key), key, combined)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build the element for this field
|
||||
element, err := field.CreateElement(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add the field element to the map
|
||||
values[key] = element
|
||||
}
|
||||
return values, nil
|
||||
}
|
227
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/openapi.go
generated
vendored
Normal file
227
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/openapi.go
generated
vendored
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
Copyright 2017 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 parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
)
|
||||
|
||||
// Contains functions for casting openapi interfaces to their underlying types
|
||||
|
||||
// getSchemaType returns the string type of the schema - e.g. array, primitive, map, kind, reference
|
||||
func getSchemaType(schema proto.Schema) string {
|
||||
if schema == nil {
|
||||
return ""
|
||||
}
|
||||
visitor := &baseSchemaVisitor{}
|
||||
schema.Accept(visitor)
|
||||
return visitor.Kind
|
||||
}
|
||||
|
||||
// getKind converts schema to an *proto.Kind object
|
||||
func getKind(schema proto.Schema) (*proto.Kind, error) {
|
||||
if schema == nil {
|
||||
return nil, nil
|
||||
}
|
||||
visitor := &kindSchemaVisitor{}
|
||||
schema.Accept(visitor)
|
||||
return visitor.Result, visitor.Err
|
||||
}
|
||||
|
||||
// getArray converts schema to an *proto.Array object
|
||||
func getArray(schema proto.Schema) (*proto.Array, error) {
|
||||
if schema == nil {
|
||||
return nil, nil
|
||||
}
|
||||
visitor := &arraySchemaVisitor{}
|
||||
schema.Accept(visitor)
|
||||
return visitor.Result, visitor.Err
|
||||
}
|
||||
|
||||
// getMap converts schema to an *proto.Map object
|
||||
func getMap(schema proto.Schema) (*proto.Map, error) {
|
||||
if schema == nil {
|
||||
return nil, nil
|
||||
}
|
||||
visitor := &mapSchemaVisitor{}
|
||||
schema.Accept(visitor)
|
||||
return visitor.Result, visitor.Err
|
||||
}
|
||||
|
||||
// getPrimitive converts schema to an *proto.Primitive object
|
||||
func getPrimitive(schema proto.Schema) (*proto.Primitive, error) {
|
||||
if schema == nil {
|
||||
return nil, nil
|
||||
}
|
||||
visitor := &primitiveSchemaVisitor{}
|
||||
schema.Accept(visitor)
|
||||
return visitor.Result, visitor.Err
|
||||
}
|
||||
|
||||
type baseSchemaVisitor struct {
|
||||
Err error
|
||||
Kind string
|
||||
}
|
||||
|
||||
// VisitArray implements openapi
|
||||
func (v *baseSchemaVisitor) VisitArray(array *proto.Array) {
|
||||
v.Kind = "array"
|
||||
v.Err = fmt.Errorf("Array type not expected")
|
||||
}
|
||||
|
||||
// MergeMap implements openapi
|
||||
func (v *baseSchemaVisitor) VisitMap(*proto.Map) {
|
||||
v.Kind = "map"
|
||||
v.Err = fmt.Errorf("Map type not expected")
|
||||
}
|
||||
|
||||
// MergePrimitive implements openapi
|
||||
func (v *baseSchemaVisitor) VisitPrimitive(*proto.Primitive) {
|
||||
v.Kind = "primitive"
|
||||
v.Err = fmt.Errorf("Primitive type not expected")
|
||||
}
|
||||
|
||||
// VisitKind implements openapi
|
||||
func (v *baseSchemaVisitor) VisitKind(*proto.Kind) {
|
||||
v.Kind = "kind"
|
||||
v.Err = fmt.Errorf("Kind type not expected")
|
||||
}
|
||||
|
||||
// VisitReference implements openapi
|
||||
func (v *baseSchemaVisitor) VisitReference(reference proto.Reference) {
|
||||
v.Kind = "reference"
|
||||
v.Err = fmt.Errorf("Reference type not expected")
|
||||
}
|
||||
|
||||
type kindSchemaVisitor struct {
|
||||
baseSchemaVisitor
|
||||
Result *proto.Kind
|
||||
}
|
||||
|
||||
// VisitKind implements openapi
|
||||
func (v *kindSchemaVisitor) VisitKind(result *proto.Kind) {
|
||||
v.Result = result
|
||||
v.Kind = "kind"
|
||||
}
|
||||
|
||||
// VisitReference implements openapi
|
||||
func (v *kindSchemaVisitor) VisitReference(reference proto.Reference) {
|
||||
reference.SubSchema().Accept(v)
|
||||
if v.Err == nil {
|
||||
v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
|
||||
}
|
||||
}
|
||||
|
||||
func copyExtensions(field string, from, to map[string]interface{}) error {
|
||||
// Copy extensions from field to type for references
|
||||
for key, val := range from {
|
||||
if curr, found := to[key]; found {
|
||||
// Don't allow the same extension to be defined both on the field and on the type
|
||||
return fmt.Errorf("Cannot override value for extension %s on field %s from %v to %v",
|
||||
key, field, curr, val)
|
||||
}
|
||||
to[key] = val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type mapSchemaVisitor struct {
|
||||
baseSchemaVisitor
|
||||
Result *proto.Map
|
||||
}
|
||||
|
||||
// MergeMap implements openapi
|
||||
func (v *mapSchemaVisitor) VisitMap(result *proto.Map) {
|
||||
v.Result = result
|
||||
v.Kind = "map"
|
||||
}
|
||||
|
||||
// VisitReference implements openapi
|
||||
func (v *mapSchemaVisitor) VisitReference(reference proto.Reference) {
|
||||
reference.SubSchema().Accept(v)
|
||||
if v.Err == nil {
|
||||
v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
|
||||
}
|
||||
}
|
||||
|
||||
type arraySchemaVisitor struct {
|
||||
baseSchemaVisitor
|
||||
Result *proto.Array
|
||||
}
|
||||
|
||||
// VisitArray implements openapi
|
||||
func (v *arraySchemaVisitor) VisitArray(result *proto.Array) {
|
||||
v.Result = result
|
||||
v.Kind = "array"
|
||||
v.Err = copySubElementPatchStrategy(result.Path.String(), result.GetExtensions(), result.SubType.GetExtensions())
|
||||
}
|
||||
|
||||
// copyPatchStrategy copies the strategies to subelements to the subtype
|
||||
// e.g. PodTemplate.Volumes is a []Volume with "x-kubernetes-patch-strategy": "merge,retainKeys"
|
||||
// the "retainKeys" strategy applies to merging Volumes, and must be copied to the sub element
|
||||
func copySubElementPatchStrategy(field string, from, to map[string]interface{}) error {
|
||||
// Check if the parent has a patch strategy extension
|
||||
if ext, found := from["x-kubernetes-patch-strategy"]; found {
|
||||
strategy, ok := ext.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected string value for x-kubernetes-patch-strategy on %s, was %T",
|
||||
field, ext)
|
||||
}
|
||||
// Check of the parent patch strategy has a sub patch strategy, and if so copy to the sub type
|
||||
if strings.Contains(strategy, ",") {
|
||||
strategies := strings.Split(strategy, ",")
|
||||
if len(strategies) != 2 {
|
||||
// Only 1 sub strategy is supported
|
||||
return fmt.Errorf(
|
||||
"Expected between 0 and 2 elements for x-kubernetes-patch-merge-strategy by got %v",
|
||||
strategies)
|
||||
}
|
||||
to["x-kubernetes-patch-strategy"] = strategies[1]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MergePrimitive implements openapi
|
||||
func (v *arraySchemaVisitor) VisitReference(reference proto.Reference) {
|
||||
reference.SubSchema().Accept(v)
|
||||
if v.Err == nil {
|
||||
v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
|
||||
}
|
||||
}
|
||||
|
||||
type primitiveSchemaVisitor struct {
|
||||
baseSchemaVisitor
|
||||
Result *proto.Primitive
|
||||
}
|
||||
|
||||
// MergePrimitive implements openapi
|
||||
func (v *primitiveSchemaVisitor) VisitPrimitive(result *proto.Primitive) {
|
||||
v.Result = result
|
||||
v.Kind = "primitive"
|
||||
}
|
||||
|
||||
// VisitReference implements openapi
|
||||
func (v *primitiveSchemaVisitor) VisitReference(reference proto.Reference) {
|
||||
reference.SubSchema().Accept(v)
|
||||
if v.Err == nil {
|
||||
v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
|
||||
}
|
||||
}
|
28
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/primitive_element.go
generated
vendored
Normal file
28
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/primitive_element.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
Copyright 2017 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 parse
|
||||
|
||||
import "k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
|
||||
// primitiveElement builds a new primitiveElement from a PrimitiveItem
|
||||
func (v ElementBuildingVisitor) primitiveElement(item *primitiveItem) (*apply.PrimitiveElement, error) {
|
||||
meta := apply.FieldMetaImpl{Name: item.Name}
|
||||
return &apply.PrimitiveElement{
|
||||
FieldMetaImpl: meta,
|
||||
RawElementData: item.RawElementData,
|
||||
}, nil
|
||||
}
|
49
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/suite_test.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/suite_test.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2017 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 parse_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/config"
|
||||
. "github.com/onsi/ginkgo/types"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOpenapi(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "Openapi Suite", []Reporter{newlineReporter{}})
|
||||
}
|
||||
|
||||
// Print a newline after the default newlineReporter due to issue
|
||||
// https://github.com/jstemmer/go-junit-report/issues/31
|
||||
type newlineReporter struct{}
|
||||
|
||||
func (newlineReporter) SpecSuiteWillBegin(config GinkgoConfigType, summary *SuiteSummary) {}
|
||||
|
||||
func (newlineReporter) BeforeSuiteDidRun(setupSummary *SetupSummary) {}
|
||||
|
||||
func (newlineReporter) AfterSuiteDidRun(setupSummary *SetupSummary) {}
|
||||
|
||||
func (newlineReporter) SpecWillRun(specSummary *SpecSummary) {}
|
||||
|
||||
func (newlineReporter) SpecDidComplete(specSummary *SpecSummary) {}
|
||||
|
||||
// SpecSuiteDidEnd Prints a newline between "35 Passed | 0 Failed | 0 Pending | 0 Skipped" and "--- PASS:"
|
||||
func (newlineReporter) SpecSuiteDidEnd(summary *SuiteSummary) { fmt.Printf("\n") }
|
46
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/type_element.go
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/type_element.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2017 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 parse
|
||||
|
||||
import (
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// typeElement builds a new mapElement from a typeItem
|
||||
func (v ElementBuildingVisitor) typeElement(meta apply.FieldMetaImpl, item *typeItem) (*apply.TypeElement, error) {
|
||||
// Function to get the schema of a field from its key
|
||||
var fn schemaFn = func(key string) proto.Schema {
|
||||
if item.Type != nil && item.Type.Fields != nil {
|
||||
return item.Type.Fields[key]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect same fields from multiple maps into a map of elements
|
||||
values, err := v.createMapValues(fn, meta, item.MapElementData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return the result
|
||||
return &apply.TypeElement{
|
||||
FieldMetaImpl: meta,
|
||||
MapElementData: item.MapElementData,
|
||||
Values: values,
|
||||
}, nil
|
||||
}
|
181
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/util.go
generated
vendored
Normal file
181
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/util.go
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright 2017 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 parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// nilSafeLookup returns the value from the map if the map is non-nil
|
||||
func nilSafeLookup(key string, from map[string]interface{}) (interface{}, bool) {
|
||||
if from != nil {
|
||||
value, found := from[key]
|
||||
return value, found
|
||||
}
|
||||
// Not present
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// boundsSafeLookup returns the value from the slice if the slice is non-nil and
|
||||
// the index is in bounds.
|
||||
func boundsSafeLookup(index int, from []interface{}) (interface{}, bool) {
|
||||
if from != nil && len(from) > index {
|
||||
return from[index], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// keysUnion returns a slice containing the union of the keys present in the arguments
|
||||
func keysUnion(maps ...map[string]interface{}) []string {
|
||||
keys := map[string]interface{}{}
|
||||
for _, m := range maps {
|
||||
for k := range m {
|
||||
keys[k] = nil
|
||||
}
|
||||
}
|
||||
result := []string{}
|
||||
for key := range keys {
|
||||
result = append(result, key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// max returns the argument with the highest value
|
||||
func max(values ...int) int {
|
||||
v := 0
|
||||
for _, i := range values {
|
||||
if i > v {
|
||||
v = i
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// getType returns the type of the arguments. If the arguments don't have matching
|
||||
// types, getType returns an error. Nil types matching everything.
|
||||
func getType(args ...interface{}) (reflect.Type, error) {
|
||||
var last interface{}
|
||||
for _, next := range args {
|
||||
// Skip nil values
|
||||
if next == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Set the first non-nil value we find and continue
|
||||
if last == nil {
|
||||
last = next
|
||||
continue
|
||||
}
|
||||
|
||||
// Verify the types of the values match
|
||||
if reflect.TypeOf(last).Kind() != reflect.TypeOf(next).Kind() {
|
||||
return nil, fmt.Errorf("missmatching non-nil types for the same field: %T %T", last, next)
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.TypeOf(last), nil
|
||||
}
|
||||
|
||||
// getFieldMeta parses the metadata about the field from the openapi spec
|
||||
func getFieldMeta(s proto.Schema, name string) (apply.FieldMetaImpl, error) {
|
||||
m := apply.FieldMetaImpl{}
|
||||
if s != nil {
|
||||
ext := s.GetExtensions()
|
||||
if e, found := ext["x-kubernetes-patch-strategy"]; found {
|
||||
strategy, ok := e.(string)
|
||||
if !ok {
|
||||
return apply.FieldMetaImpl{}, fmt.Errorf("Expected string for x-kubernetes-patch-strategy by got %T", s)
|
||||
}
|
||||
|
||||
// Take the first strategy if there are substrategies.
|
||||
// Sub strategies are copied to sub types in openapi.go
|
||||
strategies := strings.Split(strategy, ",")
|
||||
if len(strategies) > 2 {
|
||||
return apply.FieldMetaImpl{}, fmt.Errorf("Expected between 0 and 2 elements for x-kubernetes-patch-merge-strategy by got %v", strategies)
|
||||
}
|
||||
// For lists, choose the strategy for this type, not the subtype
|
||||
m.MergeType = strategies[0]
|
||||
}
|
||||
if k, found := ext["x-kubernetes-patch-merge-key"]; found {
|
||||
key, ok := k.(string)
|
||||
if !ok {
|
||||
return apply.FieldMetaImpl{}, fmt.Errorf("Expected string for x-kubernetes-patch-merge-key by got %T", k)
|
||||
}
|
||||
m.MergeKeys = apply.MergeKeys(strings.Split(key, ","))
|
||||
}
|
||||
}
|
||||
m.Name = name
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// getCommonGroupVersionKind verifies that the recorded, local and remote all share
|
||||
// the same GroupVersionKind and returns the value
|
||||
func getCommonGroupVersionKind(recorded, local, remote map[string]interface{}) (schema.GroupVersionKind, error) {
|
||||
recordedGVK, err := getGroupVersionKind(recorded)
|
||||
if err != nil {
|
||||
return schema.GroupVersionKind{}, err
|
||||
}
|
||||
localGVK, err := getGroupVersionKind(local)
|
||||
if err != nil {
|
||||
return schema.GroupVersionKind{}, err
|
||||
}
|
||||
remoteGVK, err := getGroupVersionKind(remote)
|
||||
if err != nil {
|
||||
return schema.GroupVersionKind{}, err
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(recordedGVK, localGVK) || !reflect.DeepEqual(localGVK, remoteGVK) {
|
||||
return schema.GroupVersionKind{},
|
||||
fmt.Errorf("group version kinds do not match (recorded: %v local: %v remote: %v)",
|
||||
recordedGVK, localGVK, remoteGVK)
|
||||
}
|
||||
return recordedGVK, nil
|
||||
}
|
||||
|
||||
// getGroupVersionKind returns the GroupVersionKind of the object
|
||||
func getGroupVersionKind(config map[string]interface{}) (schema.GroupVersionKind, error) {
|
||||
gvk := schema.GroupVersionKind{}
|
||||
if gv, found := config["apiVersion"]; found {
|
||||
casted, ok := gv.(string)
|
||||
if !ok {
|
||||
return gvk, fmt.Errorf("Expected string for apiVersion, found %T", gv)
|
||||
}
|
||||
s := strings.Split(casted, "/")
|
||||
if len(s) != 1 {
|
||||
gvk.Group = s[0]
|
||||
}
|
||||
gvk.Version = s[len(s)-1]
|
||||
} else {
|
||||
return gvk, fmt.Errorf("Missing apiVersion in Kind %v", config)
|
||||
}
|
||||
if k, found := config["kind"]; found {
|
||||
casted, ok := k.(string)
|
||||
if !ok {
|
||||
return gvk, fmt.Errorf("Expected string for kind, found %T", k)
|
||||
}
|
||||
gvk.Kind = casted
|
||||
} else {
|
||||
return gvk, fmt.Errorf("Missing kind in Kind %v", config)
|
||||
}
|
||||
return gvk, nil
|
||||
}
|
79
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/visitor.go
generated
vendored
Normal file
79
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/visitor.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright 2017 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 parse
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
)
|
||||
|
||||
// ItemVisitor provides an interface for Items to Accept and call
|
||||
// the Visit function that corresponds to its actual type.
|
||||
type ItemVisitor interface {
|
||||
// CreatePrimitiveElement builds an Element for a primitiveItem
|
||||
CreatePrimitiveElement(*primitiveItem) (apply.Element, error)
|
||||
|
||||
// CreateListElement builds an Element for a listItem
|
||||
CreateListElement(*listItem) (apply.Element, error)
|
||||
|
||||
// CreateMapElement builds an Element for a mapItem
|
||||
CreateMapElement(*mapItem) (apply.Element, error)
|
||||
|
||||
// CreateTypeElement builds an Element for a typeItem
|
||||
CreateTypeElement(*typeItem) (apply.Element, error)
|
||||
}
|
||||
|
||||
// ElementBuildingVisitor creates an Elements from Items
|
||||
// An Element combines the values from the Item with the field metadata.
|
||||
type ElementBuildingVisitor struct {
|
||||
resources openapi.Resources
|
||||
}
|
||||
|
||||
// CreatePrimitiveElement creates a primitiveElement
|
||||
func (v ElementBuildingVisitor) CreatePrimitiveElement(item *primitiveItem) (apply.Element, error) {
|
||||
return v.primitiveElement(item)
|
||||
}
|
||||
|
||||
// CreateListElement creates a ListElement
|
||||
func (v ElementBuildingVisitor) CreateListElement(item *listItem) (apply.Element, error) {
|
||||
meta, err := getFieldMeta(item.GetMeta(), item.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if meta.GetFieldMergeType() == apply.MergeStrategy {
|
||||
return v.mergeListElement(meta, item)
|
||||
}
|
||||
return v.replaceListElement(meta, item)
|
||||
}
|
||||
|
||||
// CreateMapElement creates a mapElement
|
||||
func (v ElementBuildingVisitor) CreateMapElement(item *mapItem) (apply.Element, error) {
|
||||
meta, err := getFieldMeta(item.GetMeta(), item.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v.mapElement(meta, item)
|
||||
}
|
||||
|
||||
// CreateTypeElement creates a typeElement
|
||||
func (v ElementBuildingVisitor) CreateTypeElement(item *typeItem) (apply.Element, error) {
|
||||
meta, err := getFieldMeta(item.GetMeta(), item.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v.typeElement(meta, item)
|
||||
}
|
34
vendor/k8s.io/kubernetes/pkg/kubectl/apply/primitive_element.go
generated
vendored
Normal file
34
vendor/k8s.io/kubernetes/pkg/kubectl/apply/primitive_element.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright 2017 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 apply
|
||||
|
||||
// PrimitiveElement contains the recorded, local and remote values for a field
|
||||
// of type primitive
|
||||
type PrimitiveElement struct {
|
||||
// FieldMetaImpl contains metadata about the field from openapi
|
||||
FieldMetaImpl
|
||||
|
||||
// RawElementData contains the values the field was set to
|
||||
RawElementData
|
||||
}
|
||||
|
||||
// Merge implements Element.Merge
|
||||
func (e PrimitiveElement) Merge(v Strategy) (Result, error) {
|
||||
return v.MergePrimitive(e)
|
||||
}
|
||||
|
||||
var _ Element = &PrimitiveElement{}
|
71
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/BUILD
generated
vendored
Normal file
71
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/BUILD
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"merge.go",
|
||||
"merge_visitor.go",
|
||||
"replace_visitor.go",
|
||||
"retain_keys_visitor.go",
|
||||
"strategic_visitor.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/apply/strategy",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//pkg/kubectl/apply:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_xtest",
|
||||
srcs = [
|
||||
"merge_map_list_test.go",
|
||||
"merge_map_test.go",
|
||||
"merge_primitive_list_test.go",
|
||||
"merge_primitive_test.go",
|
||||
"replace_map_list_test.go",
|
||||
"replace_map_test.go",
|
||||
"replace_primitive_list_test.go",
|
||||
"retain_keys_test.go",
|
||||
"suite_test.go",
|
||||
"utils_test.go",
|
||||
],
|
||||
data = [
|
||||
":swagger-spec",
|
||||
"//api/openapi-spec:swagger-spec",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/apply/strategy_test",
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//pkg/kubectl/apply:go_default_library",
|
||||
"//pkg/kubectl/apply/parse:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi/testing:go_default_library",
|
||||
"//vendor/github.com/ghodss/yaml:go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo:go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo/config:go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo/types:go_default_library",
|
||||
"//vendor/github.com/onsi/gomega:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "swagger-spec",
|
||||
srcs = glob([
|
||||
"**/*.json",
|
||||
]),
|
||||
)
|
17
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/doc.go
generated
vendored
Normal file
17
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/doc.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy
|
33
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge.go
generated
vendored
Normal file
33
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy
|
||||
|
||||
import "k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
|
||||
// Options controls how a merge will be executed
|
||||
type Options struct {
|
||||
// FailOnConflict when true will fail patch creation if the recorded and remote
|
||||
// have 2 fields set for the same value that cannot be merged.
|
||||
// e.g. primitive values, list values with replace strategy, and map values with do
|
||||
// strategy
|
||||
FailOnConflict bool
|
||||
}
|
||||
|
||||
// Create returns a new apply.Visitor for merging multiple objects together
|
||||
func Create(options Options) apply.Strategy {
|
||||
return createDelegatingStrategy(options)
|
||||
}
|
650
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_map_list_test.go
generated
vendored
Normal file
650
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_map_list_test.go
generated
vendored
Normal file
@ -0,0 +1,650 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
tst "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/testing"
|
||||
)
|
||||
|
||||
var _ = Describe("Merging fields of type list-of-map with openapi", func() {
|
||||
Context("where one of the items has been deleted resulting in the containers being empty", func() {
|
||||
It("should set the containers field to null", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image
|
||||
- name: item2
|
||||
image: image2
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items has been deleted", func() {
|
||||
It("should be deleted from the result", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item-keep
|
||||
image: image-keep
|
||||
- name: item-delete
|
||||
image: image-delete
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item-keep
|
||||
image: image-keep
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item-keep
|
||||
image: image-keep
|
||||
- name: item-delete
|
||||
image: image-delete
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item-keep
|
||||
image: image-keep
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items is only in the remote", func() {
|
||||
It("should leave the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item2
|
||||
image: image2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item2
|
||||
image: image2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item2
|
||||
image: image2
|
||||
- name: item
|
||||
image: image
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items differs from the remote value and is missing from the recorded", func() {
|
||||
It("should update the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:1
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items differs from the remote value but matches the recorded", func() {
|
||||
It("should update the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:1
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items is missing from the remote but matches the recorded", func() {
|
||||
It("should add the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items is missing from the remote and missing from the recorded ", func() {
|
||||
It("should add the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where the order of the resolved, local and remote lists differs", func() {
|
||||
It("should keep the order specified in local and append items appears only in remote", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: recorded-local
|
||||
image: recorded:b
|
||||
timeoutSeconds: 2
|
||||
- name: recorded-remote
|
||||
image: recorded:c
|
||||
timeoutSeconds: 3
|
||||
- name: recorded-local-remote
|
||||
image: recorded:d
|
||||
timeoutSeconds: 4
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: local
|
||||
image: local:a
|
||||
initialDelaySeconds: 15
|
||||
- name: recorded-local-remote
|
||||
image: local:b
|
||||
initialDelaySeconds: 16
|
||||
- name: local-remote
|
||||
image: local:c
|
||||
initialDelaySeconds: 17
|
||||
- name: recorded-local
|
||||
image: local:d
|
||||
initialDelaySeconds: 18
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: remote
|
||||
image: remote:a
|
||||
imagePullPolicy: Always
|
||||
- name: recorded-remote
|
||||
image: remote:b
|
||||
imagePullPolicy: Always
|
||||
- name: local-remote
|
||||
image: remote:c
|
||||
imagePullPolicy: Always
|
||||
- name: recorded-local-remote
|
||||
image: remote:d
|
||||
imagePullPolicy: Always
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: local
|
||||
image: local:a
|
||||
initialDelaySeconds: 15
|
||||
- name: recorded-local-remote
|
||||
image: local:b
|
||||
imagePullPolicy: Always
|
||||
initialDelaySeconds: 16
|
||||
- name: local-remote
|
||||
image: local:c
|
||||
imagePullPolicy: Always
|
||||
initialDelaySeconds: 17
|
||||
- name: recorded-local
|
||||
image: local:d
|
||||
initialDelaySeconds: 18
|
||||
- name: remote
|
||||
image: remote:a
|
||||
imagePullPolicy: Always
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Merging fields of type list-of-map with openapi containing a multi-field mergekey", func() {
|
||||
var resources openapi.Resources
|
||||
BeforeEach(func() {
|
||||
resources = tst.NewFakeResources("test_swagger.json")
|
||||
})
|
||||
|
||||
Context("where one of the items has been deleted", func() {
|
||||
It("should delete the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
hostIP: "127.0.0.1"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
`)
|
||||
runWith(strategy.Create(strategy.Options{}), recorded, local, remote, expected, resources)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items has been updated", func() {
|
||||
It("should merge updates to the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2021
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2023
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2021
|
||||
hostIP: "127.0.0.1"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2023
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
hostIP: "127.0.0.1"
|
||||
`)
|
||||
runWith(strategy.Create(strategy.Options{}), recorded, local, remote, expected, resources)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items has been added", func() {
|
||||
It("should add the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
hostIP: "127.0.0.1"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
hostIP: "127.0.0.1"
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
`)
|
||||
runWith(strategy.Create(strategy.Options{}), recorded, local, remote, expected, resources)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Merging fields of type list-of-map with openapi", func() {
|
||||
Context("containing a replace-keys sub strategy", func() {
|
||||
It("should apply the replace-key strategy when merging the item", func() {
|
||||
})
|
||||
})
|
||||
})
|
164
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_map_test.go
generated
vendored
Normal file
164
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_map_test.go
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Merging fields of type map with openapi for some fields", func() {
|
||||
Context("where a field has been deleted", func() {
|
||||
It("should delete the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1"
|
||||
image: "1"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo2: null
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1"
|
||||
image: "1"
|
||||
foo2:
|
||||
bar: "baz2"
|
||||
image: "2"
|
||||
foo3:
|
||||
bar: "baz3"
|
||||
image: "3"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo3:
|
||||
bar: "baz3"
|
||||
image: "3"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been added", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo1:
|
||||
bar: "baz1"
|
||||
image: "1"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1"
|
||||
image: "1"
|
||||
foo2:
|
||||
bar: "baz2"
|
||||
image: "2"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1"
|
||||
image: "1"
|
||||
foo2:
|
||||
bar: "baz2"
|
||||
image: "2"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should update the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo1:
|
||||
bar: "baz1=1"
|
||||
image: "1-1"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1-1"
|
||||
image: "1-1"
|
||||
foo2:
|
||||
bar: "baz2-1"
|
||||
image: "2-1"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 2
|
||||
foo1:
|
||||
bar: "baz1-0"
|
||||
image: "1-0"
|
||||
foo2:
|
||||
bar: "baz2-0"
|
||||
image: "2-0"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1-1"
|
||||
image: "1-1"
|
||||
foo2:
|
||||
bar: "baz2-1"
|
||||
image: "2-1"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
189
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_primitive_list_test.go
generated
vendored
Normal file
189
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_primitive_list_test.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Merging fields of type list-of-primitive with openapi", func() {
|
||||
Context("where one of the items has been deleted", func() {
|
||||
It("should delete the deleted item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "c"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "c"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "c"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "c"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items is only on the remote", func() {
|
||||
It("should move the remote-only item to the end but keep it", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "c"
|
||||
- "b"
|
||||
- "a"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "c"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items is repeated", func() {
|
||||
It("should de-duplicate the repeated items", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "a"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where some items are deleted and others are on remote only", func() {
|
||||
It("should retain the correct items in the correct order", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "c"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "c"
|
||||
- "a"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "d"
|
||||
- "b"
|
||||
- "c"
|
||||
- "a"
|
||||
- "e"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "c"
|
||||
- "d"
|
||||
- "e"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
540
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_primitive_test.go
generated
vendored
Normal file
540
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_primitive_test.go
generated
vendored
Normal file
@ -0,0 +1,540 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Merging fields of type map with openapi", func() {
|
||||
Context("where a field has been deleted", func() {
|
||||
It("should delete the field when it is the only field in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
paused: true
|
||||
# delete - recorded/remote differ
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
replicas: null
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should delete the field when there are other fields in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
paused: true
|
||||
# delete - recorded/remote differ
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
replicas: null
|
||||
# keep
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been added", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
paused: true
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# Add this - it is missing from recorded and remote
|
||||
replicas: 3
|
||||
# Add this - it is missing from remote but matches recorded
|
||||
paused: true
|
||||
# Add this - it is missing from remote and differs from recorded
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
paused: true
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# Missing from recorded
|
||||
replicas: 3
|
||||
# Matches the recorded
|
||||
paused: true
|
||||
# Differs from recorded
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 2
|
||||
paused: false
|
||||
progressDeadlineSeconds: 3
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should update the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 2
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Merging fields of type map without openapi", func() {
|
||||
Context("where a field has been deleted", func() {
|
||||
It("should delete the field when it is the only field in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
paused: true
|
||||
# delete - recorded/remote differ
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
replicas: null
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should delete the field when there are other fields in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
paused: true
|
||||
# delete - recorded/remote differ
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
replicas: null
|
||||
# keep
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been added", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
paused: true
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# Add this - it is missing from recorded and remote
|
||||
replicas: 3
|
||||
# Add this - it is missing from remote but matches recorded
|
||||
paused: true
|
||||
# Add this - it is missing from remote and differs from recorded
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
paused: true
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# Matches recorded
|
||||
replicas: 3
|
||||
# Matches the recorded
|
||||
paused: true
|
||||
# Differs from recorded
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 2
|
||||
paused: false
|
||||
progressDeadlineSeconds: 3
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should update the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 2
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Merging fields of type map with openapi", func() {
|
||||
Context("where a field has been deleted", func() {
|
||||
It("should delete the field when it is the only field in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
foo: true
|
||||
# delete - recorded/remote differ
|
||||
bar: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
baz: null
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
baz: 3
|
||||
foo: true
|
||||
bar: 2
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should delete the field when there are other fields in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
foo: true
|
||||
# delete - recorded/remote differ
|
||||
bar: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
baz: null
|
||||
# keep
|
||||
biz: 1
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
bar: 3
|
||||
foo: true
|
||||
baz: 2
|
||||
biz: 1
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
biz: 1
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been added", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo: true
|
||||
biz: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# Add this - it is missing from recorded and remote
|
||||
baz: 3
|
||||
# Add this - it is missing from remote but matches recorded
|
||||
foo: true
|
||||
# Add this - it is missing from remote and differs from recorded
|
||||
biz: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
baz: 3
|
||||
foo: true
|
||||
biz: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo: true
|
||||
baz: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# Missing from recorded
|
||||
bar: 3
|
||||
# Matches the recorded
|
||||
foo: true
|
||||
# Differs from recorded
|
||||
baz: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
bar: 2
|
||||
foo: false
|
||||
baz: 3
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
bar: 3
|
||||
foo: true
|
||||
baz: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
148
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_visitor.go
generated
vendored
Normal file
148
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_visitor.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
func createMergeStrategy(options Options, strategic *delegatingStrategy) mergeStrategy {
|
||||
return mergeStrategy{
|
||||
strategic,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
// mergeStrategy merges the values in an Element into a single Result
|
||||
type mergeStrategy struct {
|
||||
strategic *delegatingStrategy
|
||||
options Options
|
||||
}
|
||||
|
||||
// MergeList merges the lists in a ListElement into a single Result
|
||||
func (v mergeStrategy) MergeList(e apply.ListElement) (apply.Result, error) {
|
||||
// No merge logic if adding or deleting a field
|
||||
if result, done := v.doAddOrDelete(e); done {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Merge each item in the list and append it to the list
|
||||
merged := []interface{}{}
|
||||
for _, value := range e.Values {
|
||||
// Recursively merge the list element before adding the value to the list
|
||||
m, err := value.Merge(v.strategic)
|
||||
if err != nil {
|
||||
return apply.Result{}, err
|
||||
}
|
||||
|
||||
switch m.Operation {
|
||||
case apply.SET:
|
||||
// Keep the list item value
|
||||
merged = append(merged, m.MergedResult)
|
||||
case apply.DROP:
|
||||
// Drop the list item value
|
||||
default:
|
||||
panic(fmt.Errorf("Unexpected result operation type %+v", m))
|
||||
}
|
||||
}
|
||||
|
||||
if len(merged) == 0 {
|
||||
// If the list is empty, return a nil entry
|
||||
return apply.Result{Operation: apply.SET, MergedResult: nil}, nil
|
||||
}
|
||||
// Return the merged list, and tell the caller to keep it
|
||||
return apply.Result{Operation: apply.SET, MergedResult: merged}, nil
|
||||
}
|
||||
|
||||
// MergeMap merges the maps in a MapElement into a single Result
|
||||
func (v mergeStrategy) MergeMap(e apply.MapElement) (apply.Result, error) {
|
||||
// No merge logic if adding or deleting a field
|
||||
if result, done := v.doAddOrDelete(e); done {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
return v.doMergeMap(e.GetValues())
|
||||
}
|
||||
|
||||
// MergeMap merges the type instances in a TypeElement into a single Result
|
||||
func (v mergeStrategy) MergeType(e apply.TypeElement) (apply.Result, error) {
|
||||
// No merge logic if adding or deleting a field
|
||||
if result, done := v.doAddOrDelete(e); done {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
return v.doMergeMap(e.GetValues())
|
||||
}
|
||||
|
||||
// do merges a recorded, local and remote map into a new object
|
||||
func (v mergeStrategy) doMergeMap(e map[string]apply.Element) (apply.Result, error) {
|
||||
|
||||
// Merge each item in the list
|
||||
merged := map[string]interface{}{}
|
||||
for key, value := range e {
|
||||
// Recursively merge the map element before adding the value to the map
|
||||
result, err := value.Merge(v.strategic)
|
||||
if err != nil {
|
||||
return apply.Result{}, err
|
||||
}
|
||||
|
||||
switch result.Operation {
|
||||
case apply.SET:
|
||||
// Keep the map item value
|
||||
merged[key] = result.MergedResult
|
||||
case apply.DROP:
|
||||
// Drop the map item value
|
||||
default:
|
||||
panic(fmt.Errorf("Unexpected result operation type %+v", result))
|
||||
}
|
||||
}
|
||||
|
||||
// Return the merged map, and tell the caller to keep it
|
||||
if len(merged) == 0 {
|
||||
// Special case the empty map to set the field value to nil, but keep the field key
|
||||
// This is how the tests expect the structures to look when parsed from yaml
|
||||
return apply.Result{Operation: apply.SET, MergedResult: nil}, nil
|
||||
}
|
||||
return apply.Result{Operation: apply.SET, MergedResult: merged}, nil
|
||||
}
|
||||
|
||||
func (v mergeStrategy) doAddOrDelete(e apply.Element) (apply.Result, bool) {
|
||||
if apply.IsAdd(e) {
|
||||
return apply.Result{Operation: apply.SET, MergedResult: e.GetLocal()}, true
|
||||
}
|
||||
|
||||
// Delete the List
|
||||
if apply.IsDrop(e) {
|
||||
return apply.Result{Operation: apply.DROP}, true
|
||||
}
|
||||
|
||||
return apply.Result{}, false
|
||||
}
|
||||
|
||||
// MergePrimitive returns and error. Primitive elements can't be merged, only replaced.
|
||||
func (v mergeStrategy) MergePrimitive(diff apply.PrimitiveElement) (apply.Result, error) {
|
||||
return apply.Result{}, fmt.Errorf("Cannot merge primitive element %v", diff.Name)
|
||||
}
|
||||
|
||||
// MergeEmpty returns an empty result
|
||||
func (v mergeStrategy) MergeEmpty(diff apply.EmptyElement) (apply.Result, error) {
|
||||
return apply.Result{Operation: apply.SET}, nil
|
||||
}
|
||||
|
||||
var _ apply.Strategy = &mergeStrategy{}
|
73
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_map_list_test.go
generated
vendored
Normal file
73
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_map_list_test.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Replacing fields of type list without openapi", func() {
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should replace the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
bar:
|
||||
- name: bar1
|
||||
value: 1
|
||||
- name: bar2
|
||||
value: 2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
bar:
|
||||
- name: bar1
|
||||
value: 1
|
||||
- name: bar2
|
||||
value: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
bar:
|
||||
- name: bar1
|
||||
value: 1
|
||||
- name: bar3
|
||||
value: 3
|
||||
- name: bar4
|
||||
value: 4
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
bar:
|
||||
- name: bar1
|
||||
value: 1
|
||||
- name: bar2
|
||||
value: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
80
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_map_test.go
generated
vendored
Normal file
80
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_map_test.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
tst "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/testing"
|
||||
)
|
||||
|
||||
var _ = Describe("Replacing fields of type map with openapi for some fields", func() {
|
||||
var resources openapi.Resources
|
||||
BeforeEach(func() {
|
||||
resources = tst.NewFakeResources("test_swagger.json")
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should update the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ReplicaSet
|
||||
spec:
|
||||
template:
|
||||
containers:
|
||||
- name: container1
|
||||
image: image1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ReplicaSet
|
||||
spec:
|
||||
template:
|
||||
containers:
|
||||
- name: container1
|
||||
image: image1
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ReplicaSet
|
||||
spec:
|
||||
template:
|
||||
containers:
|
||||
- name: container1
|
||||
image: image1
|
||||
- name: container2
|
||||
image: image2
|
||||
- name: container3
|
||||
image: image3
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ReplicaSet
|
||||
spec:
|
||||
template:
|
||||
containers:
|
||||
- name: container1
|
||||
image: image1
|
||||
`)
|
||||
|
||||
// Use modified swagger for ReplicaSet spec
|
||||
runWith(strategy.Create(strategy.Options{}), recorded, local, remote, expected, resources)
|
||||
})
|
||||
})
|
||||
})
|
723
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_primitive_list_test.go
generated
vendored
Normal file
723
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_primitive_list_test.go
generated
vendored
Normal file
@ -0,0 +1,723 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Replacing fields of type list with openapi", func() {
|
||||
Context("where the field has been deleted", func() {
|
||||
It("should delete the field if present in recorded and missing from local.", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should delete the field if missing in recorded and set to null in local.", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
`)
|
||||
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where the field is has been added", func() {
|
||||
It("should add the field when missing from recorded", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should add the field when even when present in recorded", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should add the field when the parent field is missing as well", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should replace the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should replace the field even if recorded matches", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should replace the field even if the only change is ordering", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- e
|
||||
- c
|
||||
- f
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- f
|
||||
- e
|
||||
- c
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Replacing fields of type list with openapi for the type, but not the field", func() {
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should replace the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
otherstuff:
|
||||
- name: container1
|
||||
command:
|
||||
- e
|
||||
- f
|
||||
- g
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
- z
|
||||
- "y"
|
||||
otherstuff:
|
||||
- name: container1
|
||||
command:
|
||||
- s
|
||||
- d
|
||||
- f
|
||||
- name: container2
|
||||
command:
|
||||
- h
|
||||
- i
|
||||
- j
|
||||
- name: container3
|
||||
command:
|
||||
- k
|
||||
- l
|
||||
- m
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
otherstuff:
|
||||
- name: container1
|
||||
command:
|
||||
- e
|
||||
- f
|
||||
- g
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Replacing fields of type list without openapi", func() {
|
||||
Context("where the field has been deleted", func() {
|
||||
It("should delete the field.", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
arguments:
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
# explicitly delete this
|
||||
arguments:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
# keep this
|
||||
env:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
env:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where the field is has been added", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
# missing from recorded - add
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
# missing from recorded - add
|
||||
arguments:
|
||||
- c
|
||||
- d
|
||||
- q
|
||||
- w
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
arguments:
|
||||
- c
|
||||
- d
|
||||
- q
|
||||
- w
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should replace field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
env:
|
||||
- s
|
||||
- "t"
|
||||
- u
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
arguments:
|
||||
- c
|
||||
- d
|
||||
- q
|
||||
- w
|
||||
env:
|
||||
- s
|
||||
- "t"
|
||||
- u
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
- z
|
||||
- "y"
|
||||
arguments:
|
||||
- c
|
||||
- d
|
||||
- i
|
||||
env:
|
||||
- u
|
||||
- s
|
||||
- "t"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
arguments:
|
||||
- c
|
||||
- d
|
||||
- q
|
||||
- w
|
||||
env:
|
||||
- s
|
||||
- "t"
|
||||
- u
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
100
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_visitor.go
generated
vendored
Normal file
100
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_visitor.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// replaceVisitor creates a patch to replace a remote field value with a local field value
|
||||
type replaceStrategy struct {
|
||||
strategic *delegatingStrategy
|
||||
options Options
|
||||
}
|
||||
|
||||
func createReplaceStrategy(options Options, strategic *delegatingStrategy) replaceStrategy {
|
||||
return replaceStrategy{
|
||||
strategic,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
// MergeList returns a result by merging the recorded, local and remote values
|
||||
// - replacing the remote value with the local value
|
||||
func (v replaceStrategy) MergeList(e apply.ListElement) (apply.Result, error) {
|
||||
return v.doReplace(e)
|
||||
}
|
||||
|
||||
// MergeMap returns a result by merging the recorded, local and remote values
|
||||
// - replacing the remote value with the local value
|
||||
func (v replaceStrategy) MergeMap(e apply.MapElement) (apply.Result, error) {
|
||||
return v.doReplace(e)
|
||||
}
|
||||
|
||||
// MergeType returns a result by merging the recorded, local and remote values
|
||||
// - replacing the remote value with the local value
|
||||
func (v replaceStrategy) MergeType(e apply.TypeElement) (apply.Result, error) {
|
||||
return v.doReplace(e)
|
||||
}
|
||||
|
||||
// MergePrimitive returns a result by merging the recorded, local and remote values
|
||||
// - replacing the remote value with the local value
|
||||
func (v replaceStrategy) MergePrimitive(e apply.PrimitiveElement) (apply.Result, error) {
|
||||
return v.doReplace(e)
|
||||
}
|
||||
|
||||
// MergeEmpty
|
||||
func (v replaceStrategy) MergeEmpty(e apply.EmptyElement) (apply.Result, error) {
|
||||
return apply.Result{Operation: apply.SET}, nil
|
||||
}
|
||||
|
||||
// replace returns the local value if specified, otherwise it returns the remote value
|
||||
// this works regardless of the approach
|
||||
func (v replaceStrategy) doReplace(e apply.Element) (apply.Result, error) {
|
||||
// TODO: Check for conflicts
|
||||
if result, done := v.doAddOrDelete(e); done {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if e.HasLocal() {
|
||||
// Specified locally, set the local value
|
||||
return apply.Result{Operation: apply.SET, MergedResult: e.GetLocal()}, nil
|
||||
} else if e.HasRemote() {
|
||||
// Not specified locally, set the remote value
|
||||
return apply.Result{Operation: apply.SET, MergedResult: e.GetRemote()}, nil
|
||||
} else {
|
||||
// Only specified in the recorded, drop the field.
|
||||
return apply.Result{Operation: apply.DROP, MergedResult: e.GetRemote()}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// doAddOrDelete will check if the field should be either added or deleted. If either is true, it will
|
||||
// true the operation and true. Otherwise it will return false.
|
||||
func (v replaceStrategy) doAddOrDelete(e apply.Element) (apply.Result, bool) {
|
||||
if apply.IsAdd(e) {
|
||||
return apply.Result{Operation: apply.SET, MergedResult: e.GetLocal()}, true
|
||||
}
|
||||
|
||||
// Delete the List
|
||||
if apply.IsDrop(e) {
|
||||
return apply.Result{Operation: apply.DROP}, true
|
||||
}
|
||||
|
||||
return apply.Result{}, false
|
||||
}
|
||||
|
||||
var _ apply.Strategy = &replaceStrategy{}
|
195
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/retain_keys_test.go
generated
vendored
Normal file
195
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/retain_keys_test.go
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Merging fields with the retainkeys strategy", func() {
|
||||
Context("where some fields are only defined remotely", func() {
|
||||
It("should drop those fields ", func() {
|
||||
recorded := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 1
|
||||
maxSurge: 1
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where some fields are defined both locally and remotely", func() {
|
||||
It("should merge those fields", func() {
|
||||
recorded := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 2
|
||||
maxSurge: 1
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where the elements are in a list and some fields are only defined remotely", func() {
|
||||
It("should drop those fields ", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
emptyDir:
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
hostPath:
|
||||
path: /tmp/cache-volume
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
emptyDir:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where the elements are in a list", func() {
|
||||
It("the fields defined both locally and remotely should be merged", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
hostPath:
|
||||
path: /tmp/cache-volume
|
||||
emptyDir:
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
hostPath:
|
||||
path: /tmp/cache-volume
|
||||
type: Directory
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
hostPath:
|
||||
path: /tmp/cache-volume
|
||||
type: Directory
|
||||
emptyDir:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
77
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/retain_keys_visitor.go
generated
vendored
Normal file
77
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/retain_keys_visitor.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
func createRetainKeysStrategy(options Options, strategic *delegatingStrategy) retainKeysStrategy {
|
||||
return retainKeysStrategy{
|
||||
&mergeStrategy{strategic, options},
|
||||
strategic,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
// retainKeysStrategy merges the values in an Element into a single Result,
|
||||
// dropping any fields omitted from the local copy. (but merging values when
|
||||
// defined locally and remotely)
|
||||
type retainKeysStrategy struct {
|
||||
merge *mergeStrategy
|
||||
strategic *delegatingStrategy
|
||||
options Options
|
||||
}
|
||||
|
||||
// MergeMap merges the type instances in a TypeElement into a single Result
|
||||
// keeping only the fields defined locally, but merging their values with
|
||||
// the remote values.
|
||||
func (v retainKeysStrategy) MergeType(e apply.TypeElement) (apply.Result, error) {
|
||||
// No merge logic if adding or deleting a field
|
||||
if result, done := v.merge.doAddOrDelete(&e); done {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
elem := map[string]apply.Element{}
|
||||
for key := range e.GetLocalMap() {
|
||||
elem[key] = e.GetValues()[key]
|
||||
}
|
||||
return v.merge.doMergeMap(elem)
|
||||
}
|
||||
|
||||
// MergeMap returns an error. Only TypeElements can have retainKeys.
|
||||
func (v retainKeysStrategy) MergeMap(e apply.MapElement) (apply.Result, error) {
|
||||
return apply.Result{}, fmt.Errorf("Cannot use retainkeys with map element %v", e.Name)
|
||||
}
|
||||
|
||||
// MergeList returns an error. Only TypeElements can have retainKeys.
|
||||
func (v retainKeysStrategy) MergeList(e apply.ListElement) (apply.Result, error) {
|
||||
return apply.Result{}, fmt.Errorf("Cannot use retainkeys with list element %v", e.Name)
|
||||
}
|
||||
|
||||
// MergePrimitive returns an error. Only TypeElements can have retainKeys.
|
||||
func (v retainKeysStrategy) MergePrimitive(diff apply.PrimitiveElement) (apply.Result, error) {
|
||||
return apply.Result{}, fmt.Errorf("Cannot use retainkeys with primitive element %v", diff.Name)
|
||||
}
|
||||
|
||||
// MergeEmpty returns an empty result
|
||||
func (v retainKeysStrategy) MergeEmpty(diff apply.EmptyElement) (apply.Result, error) {
|
||||
return v.merge.MergeEmpty(diff)
|
||||
}
|
||||
|
||||
var _ apply.Strategy = &retainKeysStrategy{}
|
99
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/strategic_visitor.go
generated
vendored
Normal file
99
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/strategic_visitor.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// delegatingStrategy delegates merging fields to other visitor implementations
|
||||
// based on the merge strategy preferred by the field.
|
||||
type delegatingStrategy struct {
|
||||
options Options
|
||||
merge mergeStrategy
|
||||
replace replaceStrategy
|
||||
retainKeys retainKeysStrategy
|
||||
}
|
||||
|
||||
// createDelegatingStrategy returns a new delegatingStrategy
|
||||
func createDelegatingStrategy(options Options) *delegatingStrategy {
|
||||
v := &delegatingStrategy{
|
||||
options: options,
|
||||
}
|
||||
v.replace = createReplaceStrategy(options, v)
|
||||
v.merge = createMergeStrategy(options, v)
|
||||
v.retainKeys = createRetainKeysStrategy(options, v)
|
||||
return v
|
||||
}
|
||||
|
||||
// MergeList delegates visiting a list based on the field patch strategy.
|
||||
// Defaults to "replace"
|
||||
func (v delegatingStrategy) MergeList(diff apply.ListElement) (apply.Result, error) {
|
||||
switch diff.GetFieldMergeType() {
|
||||
case apply.MergeStrategy:
|
||||
return v.merge.MergeList(diff)
|
||||
case apply.ReplaceStrategy:
|
||||
return v.replace.MergeList(diff)
|
||||
case apply.RetainKeysStrategy:
|
||||
return v.retainKeys.MergeList(diff)
|
||||
default:
|
||||
return v.replace.MergeList(diff)
|
||||
}
|
||||
}
|
||||
|
||||
// MergeMap delegates visiting a map based on the field patch strategy.
|
||||
// Defaults to "merge"
|
||||
func (v delegatingStrategy) MergeMap(diff apply.MapElement) (apply.Result, error) {
|
||||
switch diff.GetFieldMergeType() {
|
||||
case apply.MergeStrategy:
|
||||
return v.merge.MergeMap(diff)
|
||||
case apply.ReplaceStrategy:
|
||||
return v.replace.MergeMap(diff)
|
||||
case apply.RetainKeysStrategy:
|
||||
return v.retainKeys.MergeMap(diff)
|
||||
default:
|
||||
return v.merge.MergeMap(diff)
|
||||
}
|
||||
}
|
||||
|
||||
// MergeType delegates visiting a map based on the field patch strategy.
|
||||
// Defaults to "merge"
|
||||
func (v delegatingStrategy) MergeType(diff apply.TypeElement) (apply.Result, error) {
|
||||
switch diff.GetFieldMergeType() {
|
||||
case apply.MergeStrategy:
|
||||
return v.merge.MergeType(diff)
|
||||
case apply.ReplaceStrategy:
|
||||
return v.replace.MergeType(diff)
|
||||
case apply.RetainKeysStrategy:
|
||||
return v.retainKeys.MergeType(diff)
|
||||
default:
|
||||
return v.merge.MergeType(diff)
|
||||
}
|
||||
}
|
||||
|
||||
// MergePrimitive delegates visiting a primitive to the ReplaceVisitorSingleton.
|
||||
func (v delegatingStrategy) MergePrimitive(diff apply.PrimitiveElement) (apply.Result, error) {
|
||||
// Always replace primitives
|
||||
return v.replace.MergePrimitive(diff)
|
||||
}
|
||||
|
||||
// MergeEmpty
|
||||
func (v delegatingStrategy) MergeEmpty(diff apply.EmptyElement) (apply.Result, error) {
|
||||
return v.merge.MergeEmpty(diff)
|
||||
}
|
||||
|
||||
var _ apply.Strategy = &delegatingStrategy{}
|
49
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/suite_test.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/suite_test.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/config"
|
||||
. "github.com/onsi/ginkgo/types"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOpenapi(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "Openapi Suite", []Reporter{newlineReporter{}})
|
||||
}
|
||||
|
||||
// Print a newline after the default newlineReporter due to issue
|
||||
// https://github.com/jstemmer/go-junit-report/issues/31
|
||||
type newlineReporter struct{}
|
||||
|
||||
func (newlineReporter) SpecSuiteWillBegin(config GinkgoConfigType, summary *SuiteSummary) {}
|
||||
|
||||
func (newlineReporter) BeforeSuiteDidRun(setupSummary *SetupSummary) {}
|
||||
|
||||
func (newlineReporter) AfterSuiteDidRun(setupSummary *SetupSummary) {}
|
||||
|
||||
func (newlineReporter) SpecWillRun(specSummary *SpecSummary) {}
|
||||
|
||||
func (newlineReporter) SpecDidComplete(specSummary *SpecSummary) {}
|
||||
|
||||
// SpecSuiteDidEnd Prints a newline between "35 Passed | 0 Failed | 0 Pending | 0 Skipped" and "--- PASS:"
|
||||
func (newlineReporter) SpecSuiteDidEnd(summary *SuiteSummary) { fmt.Printf("\n") }
|
250
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/test_swagger.json
generated
vendored
Normal file
250
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/test_swagger.json
generated
vendored
Normal file
@ -0,0 +1,250 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "Kubernetes",
|
||||
"version": "v1.9.0"
|
||||
},
|
||||
"paths": {
|
||||
},
|
||||
"definitions": {
|
||||
"io.k8s.api.core.v1.Container": {
|
||||
"description": "A single application container that you want to run within a pod.",
|
||||
"required": [
|
||||
"name",
|
||||
"image"
|
||||
],
|
||||
"properties": {
|
||||
"image": {
|
||||
"description": "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.",
|
||||
"type": "string"
|
||||
},
|
||||
"ports": {
|
||||
"description": "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.ContainerPort"
|
||||
},
|
||||
"x-kubernetes-patch-merge-key": "containerPort,protocol",
|
||||
"x-kubernetes-patch-strategy": "merge"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.core.v1.ContainerPort": {
|
||||
"description": "ContainerPort represents a network port in a single container.",
|
||||
"required": [
|
||||
"containerPort"
|
||||
],
|
||||
"properties": {
|
||||
"containerPort": {
|
||||
"description": "Number of port to expose on the pod's IP address. This must be a valid port number, 0 \u003c x \u003c 65536.",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"hostIP": {
|
||||
"description": "What host IP to bind the external port to.",
|
||||
"type": "string"
|
||||
},
|
||||
"hostPort": {
|
||||
"description": "Number of port to expose on the host. If specified, this must be a valid port number, 0 \u003c x \u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"name": {
|
||||
"description": "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.",
|
||||
"type": "string"
|
||||
},
|
||||
"protocol": {
|
||||
"description": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\".",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.apps.v1beta1.Deployment": {
|
||||
"description": "DEPRECATED - This group version of Deployment is deprecated by apps/v1beta2/Deployment. See the release notes for more information. Deployment enables declarative updates for Pods and ReplicaSets.",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Standard object metadata.",
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Specification of the desired behavior of the Deployment.",
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1beta1.DeploymentSpec"
|
||||
},
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "apps",
|
||||
"kind": "Deployment",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"io.k8s.api.apps.v1beta1.DeploymentSpec": {
|
||||
"description": "DeploymentSpec is the specification of the desired behavior of the Deployment.",
|
||||
"required": [
|
||||
"template"
|
||||
],
|
||||
"properties": {
|
||||
"minReadySeconds": {
|
||||
"description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"paused": {
|
||||
"description": "Indicates that the deployment is paused.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"progressDeadlineSeconds": {
|
||||
"description": "The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Once autoRollback is implemented, the deployment controller will automatically rollback failed deployments. Note that progress will not be estimated during the time a deployment is paused. Defaults to 600s.",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"replicas": {
|
||||
"description": "Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1.",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"revisionHistoryLimit": {
|
||||
"description": "The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 2.",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"template": {
|
||||
"description": "Template describes the pods that will be created.",
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": {
|
||||
"description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"description": "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.core.v1.PodTemplateSpec": {
|
||||
"description": "PodTemplateSpec describes the data a pod should have when created from a template",
|
||||
"properties": {
|
||||
"metadata": {
|
||||
"description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata",
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status",
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.PodSpec"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.core.v1.PodSpec": {
|
||||
"description": "PodSpec is a description of a pod.",
|
||||
"required": [
|
||||
"containers"
|
||||
],
|
||||
"properties": {
|
||||
"containers": {
|
||||
"description": "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.Container"
|
||||
},
|
||||
"x-kubernetes-patch-merge-key": "name",
|
||||
"x-kubernetes-patch-strategy": "merge"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.extensions.v1beta1.ReplicaSet": {
|
||||
"description": "DEPRECATED - This group version of ReplicaSet is deprecated by apps/v1beta2/ReplicaSet. See the release notes for more information. ReplicaSet represents the configuration of a ReplicaSet.",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"description": "If the Labels of a ReplicaSet are empty, they are defaulted to be the same as the Pod(s) that the ReplicaSet manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata",
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Spec defines the specification of the desired behavior of the ReplicaSet. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status",
|
||||
"$ref": "#/definitions/io.k8s.api.extensions.v1beta1.ReplicaSetSpec",
|
||||
"x-kubernetes-patch-strategy": "replace"
|
||||
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "extensions",
|
||||
"kind": "ReplicaSet",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"io.k8s.api.extensions.v1beta1.ReplicaSetSpec": {
|
||||
"description": "ReplicaSetSpec is the specification of a ReplicaSet.",
|
||||
"properties": {
|
||||
"minReadySeconds": {
|
||||
"description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"replicas": {
|
||||
"description": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#what-is-a-replicationcontroller",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"template": {
|
||||
"description": "Template is the object that describes the pod that will be created if insufficient replicas are detected. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template",
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"BearerToken": {
|
||||
"description": "Bearer Token authentication",
|
||||
"type": "apiKey",
|
||||
"name": "authorization",
|
||||
"in": "header"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
]
|
||||
}
|
66
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/utils_test.go
generated
vendored
Normal file
66
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/utils_test.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright 2017 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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/parse"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
tst "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/testing"
|
||||
)
|
||||
|
||||
var fakeResources = tst.NewFakeResources(filepath.Join("..", "..", "..", "..", "api", "openapi-spec", "swagger.json"))
|
||||
|
||||
// run parses the openapi and runs the tests
|
||||
func run(instance apply.Strategy, recorded, local, remote, expected map[string]interface{}) {
|
||||
runWith(instance, recorded, local, remote, expected, fakeResources)
|
||||
}
|
||||
|
||||
func runWith(instance apply.Strategy, recorded, local, remote, expected map[string]interface{}, resources openapi.Resources) {
|
||||
parseFactory := parse.Factory{Resources: resources}
|
||||
|
||||
parsed, err := parseFactory.CreateElement(recorded, local, remote)
|
||||
Expect(err).Should(Not(HaveOccurred()))
|
||||
|
||||
merged, err := parsed.Merge(instance)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(merged.Operation).Should(Equal(apply.SET))
|
||||
Expect(merged.MergedResult).Should(Equal(expected), diff.ObjectDiff(merged.MergedResult, expected))
|
||||
}
|
||||
|
||||
// create parses the yaml string into a map[string]interface{}. Verifies that the string does not have
|
||||
// any tab characters.
|
||||
func create(config string) map[string]interface{} {
|
||||
result := map[string]interface{}{}
|
||||
|
||||
// The yaml parser will throw an obscure error if there are tabs in the yaml. Check for this
|
||||
Expect(strings.Contains(config, "\t")).To(
|
||||
BeFalse(), fmt.Sprintf("Yaml %s cannot contain tabs", config))
|
||||
Expect(yaml.Unmarshal([]byte(config), &result)).Should(
|
||||
Not(HaveOccurred()), fmt.Sprintf("Could not parse config:\n\n%s\n", config))
|
||||
|
||||
return result
|
||||
}
|
43
vendor/k8s.io/kubernetes/pkg/kubectl/apply/type_element.go
generated
vendored
Normal file
43
vendor/k8s.io/kubernetes/pkg/kubectl/apply/type_element.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright 2017 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 apply
|
||||
|
||||
// TypeElement contains the recorded, local and remote values for a field
|
||||
// that is a complex type
|
||||
type TypeElement struct {
|
||||
// FieldMetaImpl contains metadata about the field from openapi
|
||||
FieldMetaImpl
|
||||
|
||||
MapElementData
|
||||
|
||||
// Values contains the combined recorded-local-remote value of each field in the type
|
||||
// Values contains the values in mapElement. Element must contain
|
||||
// a Name matching its key in Values
|
||||
Values map[string]Element
|
||||
}
|
||||
|
||||
// Merge implements Element.Merge
|
||||
func (e TypeElement) Merge(v Strategy) (Result, error) {
|
||||
return v.MergeType(e)
|
||||
}
|
||||
|
||||
// GetValues implements Element.GetValues
|
||||
func (e TypeElement) GetValues() map[string]Element {
|
||||
return e.Values
|
||||
}
|
||||
|
||||
var _ Element = &TypeElement{}
|
68
vendor/k8s.io/kubernetes/pkg/kubectl/apply/visitor.go
generated
vendored
Normal file
68
vendor/k8s.io/kubernetes/pkg/kubectl/apply/visitor.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright 2017 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 apply
|
||||
|
||||
// Strategy implements a strategy for merging recorded, local and remote values contained
|
||||
// in an element and returns the merged result.
|
||||
// Follows the visitor pattern
|
||||
type Strategy interface {
|
||||
// MergeList is invoked by ListElements when Merge is called
|
||||
MergeList(ListElement) (Result, error)
|
||||
|
||||
// MergeMap is invoked by MapElements when Merge is called
|
||||
MergeMap(MapElement) (Result, error)
|
||||
|
||||
// MergeType is invoked by TypeElements when Merge is called
|
||||
MergeType(TypeElement) (Result, error)
|
||||
|
||||
// MergePrimitive is invoked by PrimitiveElements when Merge is called
|
||||
MergePrimitive(PrimitiveElement) (Result, error)
|
||||
|
||||
// MergeEmpty is invoked by EmptyElements when Merge is called
|
||||
MergeEmpty(EmptyElement) (Result, error)
|
||||
}
|
||||
|
||||
// Operation records whether a field should be set or dropped
|
||||
type Operation int
|
||||
|
||||
const (
|
||||
// ERROR is an error during merge
|
||||
ERROR Operation = iota
|
||||
// SET sets the field on an object
|
||||
SET
|
||||
// DROP drops the field from an object
|
||||
DROP
|
||||
)
|
||||
|
||||
// Result is the result of merging fields
|
||||
type Result struct {
|
||||
// Operation is the operation that should be performed for the merged field
|
||||
Operation Operation
|
||||
// MergedResult is the new merged value
|
||||
MergedResult interface{}
|
||||
}
|
||||
|
||||
const (
|
||||
// MergeStrategy is the strategy to merge the local and remote values
|
||||
MergeStrategy = "merge"
|
||||
|
||||
// RetainKeysStrategy is the strategy to merge the local and remote values, but drop any fields not defined locally
|
||||
RetainKeysStrategy = "retainKeys"
|
||||
|
||||
// ReplaceStrategy is the strategy to replace the remote value with the local value
|
||||
ReplaceStrategy = "replace"
|
||||
)
|
Reference in New Issue
Block a user