@@ -17,13 +17,9 @@ limitations under the License.
17
17
package apply
18
18
19
19
import (
20
- "encoding/json"
21
20
"fmt"
22
- "io"
23
21
"net/http"
24
- "time"
25
22
26
- "github.com/jonboulle/clockwork"
27
23
"github.com/spf13/cobra"
28
24
corev1 "k8s.io/api/core/v1"
29
25
"k8s.io/apimachinery/pkg/api/errors"
@@ -33,18 +29,13 @@ import (
33
29
"k8s.io/apimachinery/pkg/runtime"
34
30
"k8s.io/apimachinery/pkg/runtime/schema"
35
31
"k8s.io/apimachinery/pkg/types"
36
- "k8s.io/apimachinery/pkg/util/jsonmergepatch"
37
- "k8s.io/apimachinery/pkg/util/mergepatch"
38
32
"k8s.io/apimachinery/pkg/util/sets"
39
- "k8s.io/apimachinery/pkg/util/strategicpatch"
40
- "k8s.io/apimachinery/pkg/util/wait"
41
33
"k8s.io/cli-runtime/pkg/genericclioptions"
42
34
"k8s.io/cli-runtime/pkg/printers"
43
35
"k8s.io/cli-runtime/pkg/resource"
44
36
"k8s.io/client-go/discovery"
45
37
"k8s.io/client-go/dynamic"
46
38
"k8s.io/klog"
47
- oapi "k8s.io/kube-openapi/pkg/util/proto"
48
39
"k8s.io/kubectl/pkg/cmd/delete"
49
40
cmdutil "k8s.io/kubectl/pkg/cmd/util"
50
41
"k8s.io/kubectl/pkg/scheme"
@@ -103,15 +94,6 @@ type ApplyOptions struct {
103
94
objectsCached bool
104
95
}
105
96
106
- const (
107
- // maxPatchRetry is the maximum number of conflicts retry for during a patch operation before returning failure
108
- maxPatchRetry = 5
109
- // backOffPeriod is the period to back off when apply patch results in error.
110
- backOffPeriod = 1 * time .Second
111
- // how many times we can retry before back off
112
- triesBeforeBackOff = 1
113
- )
114
-
115
97
var (
116
98
applyLong = templates .LongDesc (i18n .T (`
117
99
Apply a configuration to a resource by filename or stdin.
@@ -352,10 +334,6 @@ func (o *ApplyOptions) GetObjects() ([]*resource.Info, error) {
352
334
353
335
// Run executes the `apply` command.
354
336
func (o * ApplyOptions ) Run () error {
355
- var openapiSchema openapi.Resources
356
- if o .OpenAPIPatch {
357
- openapiSchema = o .OpenAPISchema
358
- }
359
337
360
338
dryRunVerifier := & DryRunVerifier {
361
339
Finder : cmdutil .NewCRDFinder (cmdutil .CRDFromDynamic (o .DynamicClient )),
@@ -521,22 +499,7 @@ See http://k8s.io/docs/reference/using-api/api-concepts/#conflicts`, err)
521
499
fmt .Fprintf (o .ErrOut , warningNoLastAppliedConfigAnnotation , o .cmdBaseName )
522
500
}
523
501
524
- helper := resource .NewHelper (info .Client , info .Mapping )
525
- patcher := & Patcher {
526
- Mapping : info .Mapping ,
527
- Helper : helper ,
528
- DynamicClient : o .DynamicClient ,
529
- Overwrite : o .Overwrite ,
530
- BackOff : clockwork .NewRealClock (),
531
- Force : o .DeleteOptions .ForceDeletion ,
532
- Cascade : o .DeleteOptions .Cascade ,
533
- Timeout : o .DeleteOptions .Timeout ,
534
- GracePeriod : o .DeleteOptions .GracePeriod ,
535
- ServerDryRun : o .ServerDryRun ,
536
- OpenapiSchema : openapiSchema ,
537
- Retries : maxPatchRetry ,
538
- }
539
-
502
+ patcher := newPatcher (o , info )
540
503
patchBytes , patchedObject , err := patcher .Patch (info .Object , modified , info .Source , info .Namespace , info .Name , o .ErrOut )
541
504
if err != nil {
542
505
return cmdutil .AddSourceToErr (fmt .Sprintf ("applying patch:\n %s\n to:\n %v\n for:" , patchBytes , info ), info .Source , err )
@@ -636,34 +599,6 @@ func (o *ApplyOptions) printObjects() error {
636
599
return nil
637
600
}
638
601
639
- func (p * Patcher ) delete (namespace , name string ) error {
640
- return runDelete (namespace , name , p .Mapping , p .DynamicClient , p .Cascade , p .GracePeriod , p .ServerDryRun )
641
- }
642
-
643
- // Patcher defines options to patch OpenAPI objects.
644
- type Patcher struct {
645
- Mapping * meta.RESTMapping
646
- Helper * resource.Helper
647
- DynamicClient dynamic.Interface
648
-
649
- Overwrite bool
650
- BackOff clockwork.Clock
651
-
652
- Force bool
653
- Cascade bool
654
- Timeout time.Duration
655
- GracePeriod int
656
- ServerDryRun bool
657
-
658
- // If set, forces the patch against a specific resourceVersion
659
- ResourceVersion * string
660
-
661
- // Number of retries to make if the patch fails with conflict
662
- Retries int
663
-
664
- OpenapiSchema openapi.Resources
665
- }
666
-
667
602
// DryRunVerifier verifies if a given group-version-kind supports DryRun
668
603
// against the current server. Sending dryRun requests to apiserver that
669
604
// don't support it will result in objects being unwillingly persisted.
@@ -702,165 +637,3 @@ func (v *DryRunVerifier) HasSupport(gvk schema.GroupVersionKind) error {
702
637
}
703
638
return nil
704
639
}
705
-
706
- func addResourceVersion (patch []byte , rv string ) ([]byte , error ) {
707
- var patchMap map [string ]interface {}
708
- err := json .Unmarshal (patch , & patchMap )
709
- if err != nil {
710
- return nil , err
711
- }
712
- u := unstructured.Unstructured {Object : patchMap }
713
- a , err := meta .Accessor (& u )
714
- if err != nil {
715
- return nil , err
716
- }
717
- a .SetResourceVersion (rv )
718
-
719
- return json .Marshal (patchMap )
720
- }
721
-
722
- func (p * Patcher ) patchSimple (obj runtime.Object , modified []byte , source , namespace , name string , errOut io.Writer ) ([]byte , runtime.Object , error ) {
723
- // Serialize the current configuration of the object from the server.
724
- current , err := runtime .Encode (unstructured .UnstructuredJSONScheme , obj )
725
- if err != nil {
726
- return nil , nil , cmdutil .AddSourceToErr (fmt .Sprintf ("serializing current configuration from:\n %v\n for:" , obj ), source , err )
727
- }
728
-
729
- // Retrieve the original configuration of the object from the annotation.
730
- original , err := util .GetOriginalConfiguration (obj )
731
- if err != nil {
732
- return nil , nil , cmdutil .AddSourceToErr (fmt .Sprintf ("retrieving original configuration from:\n %v\n for:" , obj ), source , err )
733
- }
734
-
735
- var patchType types.PatchType
736
- var patch []byte
737
- var lookupPatchMeta strategicpatch.LookupPatchMeta
738
- var schema oapi.Schema
739
- createPatchErrFormat := "creating patch with:\n original:\n %s\n modified:\n %s\n current:\n %s\n for:"
740
-
741
- // Create the versioned struct from the type defined in the restmapping
742
- // (which is the API version we'll be submitting the patch to)
743
- versionedObject , err := scheme .Scheme .New (p .Mapping .GroupVersionKind )
744
- switch {
745
- case runtime .IsNotRegisteredError (err ):
746
- // fall back to generic JSON merge patch
747
- patchType = types .MergePatchType
748
- preconditions := []mergepatch.PreconditionFunc {mergepatch .RequireKeyUnchanged ("apiVersion" ),
749
- mergepatch .RequireKeyUnchanged ("kind" ), mergepatch .RequireMetadataKeyUnchanged ("name" )}
750
- patch , err = jsonmergepatch .CreateThreeWayJSONMergePatch (original , modified , current , preconditions ... )
751
- if err != nil {
752
- if mergepatch .IsPreconditionFailed (err ) {
753
- return nil , nil , fmt .Errorf ("%s" , "At least one of apiVersion, kind and name was changed" )
754
- }
755
- return nil , nil , cmdutil .AddSourceToErr (fmt .Sprintf (createPatchErrFormat , original , modified , current ), source , err )
756
- }
757
- case err != nil :
758
- return nil , nil , cmdutil .AddSourceToErr (fmt .Sprintf ("getting instance of versioned object for %v:" , p .Mapping .GroupVersionKind ), source , err )
759
- case err == nil :
760
- // Compute a three way strategic merge patch to send to server.
761
- patchType = types .StrategicMergePatchType
762
-
763
- // Try to use openapi first if the openapi spec is available and can successfully calculate the patch.
764
- // Otherwise, fall back to baked-in types.
765
- if p .OpenapiSchema != nil {
766
- if schema = p .OpenapiSchema .LookupResource (p .Mapping .GroupVersionKind ); schema != nil {
767
- lookupPatchMeta = strategicpatch.PatchMetaFromOpenAPI {Schema : schema }
768
- if openapiPatch , err := strategicpatch .CreateThreeWayMergePatch (original , modified , current , lookupPatchMeta , p .Overwrite ); err != nil {
769
- fmt .Fprintf (errOut , "warning: error calculating patch from openapi spec: %v\n " , err )
770
- } else {
771
- patchType = types .StrategicMergePatchType
772
- patch = openapiPatch
773
- }
774
- }
775
- }
776
-
777
- if patch == nil {
778
- lookupPatchMeta , err = strategicpatch .NewPatchMetaFromStruct (versionedObject )
779
- if err != nil {
780
- return nil , nil , cmdutil .AddSourceToErr (fmt .Sprintf (createPatchErrFormat , original , modified , current ), source , err )
781
- }
782
- patch , err = strategicpatch .CreateThreeWayMergePatch (original , modified , current , lookupPatchMeta , p .Overwrite )
783
- if err != nil {
784
- return nil , nil , cmdutil .AddSourceToErr (fmt .Sprintf (createPatchErrFormat , original , modified , current ), source , err )
785
- }
786
- }
787
- }
788
-
789
- if string (patch ) == "{}" {
790
- return patch , obj , nil
791
- }
792
-
793
- if p .ResourceVersion != nil {
794
- patch , err = addResourceVersion (patch , * p .ResourceVersion )
795
- if err != nil {
796
- return nil , nil , cmdutil .AddSourceToErr ("Failed to insert resourceVersion in patch" , source , err )
797
- }
798
- }
799
-
800
- options := metav1.PatchOptions {}
801
- if p .ServerDryRun {
802
- options .DryRun = []string {metav1 .DryRunAll }
803
- }
804
-
805
- patchedObj , err := p .Helper .Patch (namespace , name , patchType , patch , & options )
806
- return patch , patchedObj , err
807
- }
808
-
809
- // Patch tries to patch an OpenAPI resource. On success, returns the merge patch as well
810
- // the final patched object. On failure, returns an error.
811
- func (p * Patcher ) Patch (current runtime.Object , modified []byte , source , namespace , name string , errOut io.Writer ) ([]byte , runtime.Object , error ) {
812
- var getErr error
813
- patchBytes , patchObject , err := p .patchSimple (current , modified , source , namespace , name , errOut )
814
- if p .Retries == 0 {
815
- p .Retries = maxPatchRetry
816
- }
817
- for i := 1 ; i <= p .Retries && errors .IsConflict (err ); i ++ {
818
- if i > triesBeforeBackOff {
819
- p .BackOff .Sleep (backOffPeriod )
820
- }
821
- current , getErr = p .Helper .Get (namespace , name , false )
822
- if getErr != nil {
823
- return nil , nil , getErr
824
- }
825
- patchBytes , patchObject , err = p .patchSimple (current , modified , source , namespace , name , errOut )
826
- }
827
- if err != nil && (errors .IsConflict (err ) || errors .IsInvalid (err )) && p .Force {
828
- patchBytes , patchObject , err = p .deleteAndCreate (current , modified , namespace , name )
829
- }
830
- return patchBytes , patchObject , err
831
- }
832
-
833
- func (p * Patcher ) deleteAndCreate (original runtime.Object , modified []byte , namespace , name string ) ([]byte , runtime.Object , error ) {
834
- if err := p .delete (namespace , name ); err != nil {
835
- return modified , nil , err
836
- }
837
- // TODO: use wait
838
- if err := wait .PollImmediate (1 * time .Second , p .Timeout , func () (bool , error ) {
839
- if _ , err := p .Helper .Get (namespace , name , false ); ! errors .IsNotFound (err ) {
840
- return false , err
841
- }
842
- return true , nil
843
- }); err != nil {
844
- return modified , nil , err
845
- }
846
- versionedObject , _ , err := unstructured .UnstructuredJSONScheme .Decode (modified , nil , nil )
847
- if err != nil {
848
- return modified , nil , err
849
- }
850
- options := metav1.CreateOptions {}
851
- if p .ServerDryRun {
852
- options .DryRun = []string {metav1 .DryRunAll }
853
- }
854
- createdObject , err := p .Helper .Create (namespace , true , versionedObject , & options )
855
- if err != nil {
856
- // restore the original object if we fail to create the new one
857
- // but still propagate and advertise error to user
858
- recreated , recreateErr := p .Helper .Create (namespace , true , original , & options )
859
- if recreateErr != nil {
860
- err = fmt .Errorf ("An error occurred force-replacing the existing object with the newly provided one:\n \n %v.\n \n Additionally, an error occurred attempting to restore the original object:\n \n %v" , err , recreateErr )
861
- } else {
862
- createdObject = recreated
863
- }
864
- }
865
- return modified , createdObject , err
866
- }
0 commit comments