...

Source file src/k8s.io/client-go/applyconfigurations/doc.go

Documentation: k8s.io/client-go/applyconfigurations

     1  /*
     2  Copyright 2021 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  /*
    18  Package applyconfigurations provides typesafe go representations of the apply
    19  configurations that are used to constructs Server-side Apply requests.
    20  
    21  # Basics
    22  
    23  The Apply functions in the typed client (see the k8s.io/client-go/kubernetes/typed packages) offer
    24  a direct and typesafe way of calling Server-side Apply. Each Apply function takes an "apply
    25  configuration" type as an argument, which is a structured representation of an Apply request. For
    26  example:
    27  
    28  	import (
    29  	     ...
    30  	     v1ac "k8s.io/client-go/applyconfigurations/autoscaling/v1"
    31  	)
    32  	hpaApplyConfig := v1ac.HorizontalPodAutoscaler(autoscalerName, ns).
    33  	     WithSpec(v1ac.HorizontalPodAutoscalerSpec().
    34  	              WithMinReplicas(0)
    35  	     )
    36  	return hpav1client.Apply(ctx, hpaApplyConfig, metav1.ApplyOptions{FieldManager: "mycontroller", Force: true})
    37  
    38  Note in this example that HorizontalPodAutoscaler is imported from an "applyconfigurations"
    39  package. Each "apply configuration" type represents the same Kubernetes object kind as the
    40  corresponding go struct, but where all fields are pointers to make them optional, allowing apply
    41  requests to be accurately represented. For example, this when the apply configuration in the above
    42  example is marshalled to YAML, it produces:
    43  
    44  	apiVersion: autoscaling/v1
    45  	kind: HorizontalPodAutoscaler
    46  	metadata:
    47  	    name: myHPA
    48  	    namespace: myNamespace
    49  	spec:
    50  	    minReplicas: 0
    51  
    52  To understand why this is needed, the above YAML cannot be produced by the
    53  v1.HorizontalPodAutoscaler go struct. Take for example:
    54  
    55  	hpa := v1.HorizontalPodAutoscaler{
    56  	     TypeMeta: metav1.TypeMeta{
    57  	              APIVersion: "autoscaling/v1",
    58  	              Kind:       "HorizontalPodAutoscaler",
    59  	     },
    60  	     ObjectMeta: ObjectMeta{
    61  	              Namespace: ns,
    62  	              Name:      autoscalerName,
    63  	     },
    64  	     Spec: v1.HorizontalPodAutoscalerSpec{
    65  	              MinReplicas: pointer.Int32Ptr(0),
    66  	     },
    67  	}
    68  
    69  The above code attempts to declare the same apply configuration as shown in the previous examples,
    70  but when marshalled to YAML, produces:
    71  
    72  	kind: HorizontalPodAutoscaler
    73  	apiVersion: autoscaling/v1
    74  	metadata:
    75  	  name: myHPA
    76  	  namespace: myNamespace
    77  	  creationTimestamp: null
    78  	spec:
    79  	  scaleTargetRef:
    80  	    kind: ""
    81  	    name: ""
    82  	  minReplicas: 0
    83  	  maxReplicas: 0
    84  
    85  Which, among other things, contains spec.maxReplicas set to 0. This is almost certainly not what
    86  the caller intended (the intended apply configuration says nothing about the maxReplicas field),
    87  and could have serious consequences on a production system: it directs the autoscaler to downscale
    88  to zero pods. The problem here originates from the fact that the go structs contain required fields
    89  that are zero valued if not set explicitly. The go structs work as intended for create and update
    90  operations, but are fundamentally incompatible with apply, which is why we have introduced the
    91  generated "apply configuration" types.
    92  
    93  The "apply configurations" also have convenience With<FieldName> functions that make it easier to
    94  build apply requests. This allows developers to set fields without having to deal with the fact that
    95  all the fields in the "apply configuration" types are pointers, and are inconvenient to set using
    96  go. For example "MinReplicas: &0" is not legal go code, so without the With functions, developers
    97  would work around this problem by using a library, .e.g. "MinReplicas: pointer.Int32Ptr(0)", but
    98  string enumerations like corev1.Protocol are still a problem since they cannot be supported by a
    99  general purpose library. In addition to the convenience, the With functions also isolate
   100  developers from the underlying representation, which makes it safer for the underlying
   101  representation to be changed to support additional features in the future.
   102  
   103  # Controller Support
   104  
   105  The new client-go support makes it much easier to use Server-side Apply in controllers, by either of
   106  two mechanisms.
   107  
   108  Mechanism 1:
   109  
   110  When authoring new controllers to use Server-side Apply, a good approach is to have the controller
   111  recreate the apply configuration for an object each time it reconciles that object.  This ensures
   112  that the controller fully reconciles all the fields that it is responsible for. Controllers
   113  typically should unconditionally set all the fields they own by setting "Force: true" in the
   114  ApplyOptions. Controllers must also provide a FieldManager name that is unique to the
   115  reconciliation loop that apply is called from.
   116  
   117  When upgrading existing controllers to use Server-side Apply the same approach often works
   118  well--migrate the controllers to recreate the apply configuration each time it reconciles any
   119  object. For cases where this does not work well, see Mechanism 2.
   120  
   121  Mechanism 2:
   122  
   123  When upgrading existing controllers to use Server-side Apply, the controller might have multiple
   124  code paths that update different parts of an object depending on various conditions. Migrating a
   125  controller like this to Server-side Apply can be risky because if the controller forgets to include
   126  any fields in an apply configuration that is included in a previous apply request, a field can be
   127  accidentally deleted. For such cases, an alternative to mechanism 1 is to replace any controller
   128  reconciliation code that performs a "read/modify-in-place/update" (or patch) workflow with a
   129  "extract/modify-in-place/apply" workflow. Here's an example of the new workflow:
   130  
   131  	    fieldMgr := "my-field-manager"
   132  	    deploymentClient := clientset.AppsV1().Deployments("default")
   133  	    // read, could also be read from a shared informer
   134  	    deployment, err := deploymentClient.Get(ctx, "example-deployment", metav1.GetOptions{})
   135  	    if err != nil {
   136  	      // handle error
   137  	    }
   138  	    // extract
   139  	    deploymentApplyConfig, err := appsv1ac.ExtractDeployment(deployment, fieldMgr)
   140  	    if err != nil {
   141  	      // handle error
   142  	    }
   143  	    // modify-in-place
   144  	    deploymentApplyConfig.Spec.Template.Spec.WithContainers(corev1ac.Container().
   145  		WithName("modify-slice").
   146  		WithImage("nginx:1.14.2"),
   147  	    )
   148  	    // apply
   149  	    applied, err := deploymentClient.Apply(ctx, extractedDeployment, metav1.ApplyOptions{FieldManager: fieldMgr})
   150  */
   151  package applyconfigurations // import "k8s.io/client-go/applyconfigurations"
   152  

View as plain text