...

Source file src/k8s.io/kubectl/pkg/cmd/set/helper.go

Documentation: k8s.io/kubectl/pkg/cmd/set

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package set
    18  
    19  import (
    20  	"strings"
    21  
    22  	"k8s.io/api/core/v1"
    23  	"k8s.io/apimachinery/pkg/runtime"
    24  	"k8s.io/apimachinery/pkg/util/sets"
    25  	"k8s.io/apimachinery/pkg/util/strategicpatch"
    26  	"k8s.io/cli-runtime/pkg/resource"
    27  )
    28  
    29  // selectContainers allows one or more containers to be matched against a string or wildcard
    30  func selectContainers(containers []v1.Container, spec string) ([]*v1.Container, []*v1.Container) {
    31  	out := []*v1.Container{}
    32  	skipped := []*v1.Container{}
    33  	for i, c := range containers {
    34  		if selectString(c.Name, spec) {
    35  			out = append(out, &containers[i])
    36  		} else {
    37  			skipped = append(skipped, &containers[i])
    38  		}
    39  	}
    40  	return out, skipped
    41  }
    42  
    43  // selectString returns true if the provided string matches spec, where spec is a string with
    44  // a non-greedy '*' wildcard operator.
    45  // TODO: turn into a regex and handle greedy matches and backtracking.
    46  func selectString(s, spec string) bool {
    47  	if spec == "*" {
    48  		return true
    49  	}
    50  	if !strings.Contains(spec, "*") {
    51  		return s == spec
    52  	}
    53  
    54  	pos := 0
    55  	match := true
    56  	parts := strings.Split(spec, "*")
    57  Loop:
    58  	for i, part := range parts {
    59  		if len(part) == 0 {
    60  			continue
    61  		}
    62  		next := strings.Index(s[pos:], part)
    63  		switch {
    64  		// next part not in string
    65  		case next < pos:
    66  			fallthrough
    67  		// first part does not match start of string
    68  		case i == 0 && pos != 0:
    69  			fallthrough
    70  		// last part does not exactly match remaining part of string
    71  		case i == (len(parts)-1) && len(s) != (len(part)+next):
    72  			match = false
    73  			break Loop
    74  		default:
    75  			pos = next
    76  		}
    77  	}
    78  	return match
    79  }
    80  
    81  // Patch represents the result of a mutation to an object.
    82  type Patch struct {
    83  	Info *resource.Info
    84  	Err  error
    85  
    86  	Before []byte
    87  	After  []byte
    88  	Patch  []byte
    89  }
    90  
    91  // PatchFn is a function type that accepts an info object and returns a byte slice.
    92  // Implementations of PatchFn should update the object and return it encoded.
    93  type PatchFn func(runtime.Object) ([]byte, error)
    94  
    95  // CalculatePatch calls the mutation function on the provided info object, and generates a strategic merge patch for
    96  // the changes in the object. Encoder must be able to encode the info into the appropriate destination type.
    97  // This function returns whether the mutation function made any change in the original object.
    98  func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn PatchFn) bool {
    99  	patch.Before, patch.Err = runtime.Encode(encoder, patch.Info.Object)
   100  	patch.After, patch.Err = mutateFn(patch.Info.Object)
   101  	if patch.Err != nil {
   102  		return true
   103  	}
   104  	if patch.After == nil {
   105  		return false
   106  	}
   107  
   108  	patch.Patch, patch.Err = strategicpatch.CreateTwoWayMergePatch(patch.Before, patch.After, patch.Info.Object)
   109  	return true
   110  }
   111  
   112  // CalculatePatches calculates patches on each provided info object. If the provided mutateFn
   113  // makes no change in an object, the object is not included in the final list of patches.
   114  func CalculatePatches(infos []*resource.Info, encoder runtime.Encoder, mutateFn PatchFn) []*Patch {
   115  	var patches []*Patch
   116  	for _, info := range infos {
   117  		patch := &Patch{Info: info}
   118  		if CalculatePatch(patch, encoder, mutateFn) {
   119  			patches = append(patches, patch)
   120  		}
   121  	}
   122  	return patches
   123  }
   124  
   125  func findEnv(env []v1.EnvVar, name string) (v1.EnvVar, bool) {
   126  	for _, e := range env {
   127  		if e.Name == name {
   128  			return e, true
   129  		}
   130  	}
   131  	return v1.EnvVar{}, false
   132  }
   133  
   134  // updateEnv adds and deletes specified environment variables from existing environment variables.
   135  // An added variable replaces all existing variables with the same name.
   136  // Removing a variable removes all existing variables with the same name.
   137  // If the existing list contains duplicates that are unrelated to the variables being added and removed,
   138  // those duplicates are left intact in the result.
   139  // If a variable is both added and removed, the removal takes precedence.
   140  func updateEnv(existing []v1.EnvVar, env []v1.EnvVar, remove []string) []v1.EnvVar {
   141  	out := []v1.EnvVar{}
   142  	covered := sets.NewString(remove...)
   143  	for _, e := range existing {
   144  		if covered.Has(e.Name) {
   145  			continue
   146  		}
   147  		newer, ok := findEnv(env, e.Name)
   148  		if ok {
   149  			covered.Insert(e.Name)
   150  			out = append(out, newer)
   151  			continue
   152  		}
   153  		out = append(out, e)
   154  	}
   155  	for _, e := range env {
   156  		if covered.Has(e.Name) {
   157  			continue
   158  		}
   159  		covered.Insert(e.Name)
   160  		out = append(out, e)
   161  	}
   162  	return out
   163  }
   164  

View as plain text