From cc2b58dcdaaad4b02f6b94b9cbb3803586e54f80 Mon Sep 17 00:00:00 2001 From: Tamal Saha Date: Tue, 26 Sep 2023 18:43:24 -0700 Subject: [PATCH] Update deps Signed-off-by: Tamal Saha --- go.mod | 2 +- go.sum | 4 +- .../kmodules.xyz/client-go/api/v1/cluster.go | 26 +- .../kmodules.xyz/client-go/client/sa-token.go | 115 +++++ vendor/kmodules.xyz/client-go/cluster/lib.go | 10 +- vendor/kmodules.xyz/client-go/cluster/ocm.go | 17 +- .../client-go/core/v1/configmap.go | 101 +++++ .../client-go/core/v1/endpoints.go | 78 ++++ .../kmodules.xyz/client-go/core/v1/events.go | 101 +++++ .../client-go/core/v1/kubernetes.go | 428 ++++++++++++++++++ vendor/kmodules.xyz/client-go/core/v1/node.go | 287 ++++++++++++ vendor/kmodules.xyz/client-go/core/v1/pod.go | 210 +++++++++ .../client-go/core/v1/pod_status.go | 110 +++++ vendor/kmodules.xyz/client-go/core/v1/pv.go | 101 +++++ vendor/kmodules.xyz/client-go/core/v1/pvc.go | 101 +++++ vendor/kmodules.xyz/client-go/core/v1/rc.go | 112 +++++ .../client-go/core/v1/sa-token.go | 111 +++++ .../kmodules.xyz/client-go/core/v1/secret.go | 134 ++++++ .../kmodules.xyz/client-go/core/v1/service.go | 153 +++++++ .../client-go/core/v1/serviceaccount.go | 131 ++++++ vendor/modules.txt | 3 +- 21 files changed, 2323 insertions(+), 12 deletions(-) create mode 100644 vendor/kmodules.xyz/client-go/client/sa-token.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/configmap.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/endpoints.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/events.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/kubernetes.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/node.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/pod.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/pod_status.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/pv.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/pvc.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/rc.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/sa-token.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/secret.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/service.go create mode 100644 vendor/kmodules.xyz/client-go/core/v1/serviceaccount.go diff --git a/go.mod b/go.mod index b692524a3..548c72180 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( k8s.io/kube-state-metrics/v2 v2.7.0 kmodules.xyz/apiversion v0.2.0 kmodules.xyz/authorizer v0.25.1 - kmodules.xyz/client-go v0.25.35-0.20230925195212-e59241c267b5 + kmodules.xyz/client-go v0.25.37 kmodules.xyz/custom-resources v0.25.2 kmodules.xyz/go-containerregistry v0.0.11 kmodules.xyz/monitoring-agent-api v0.25.3 diff --git a/go.sum b/go.sum index f32b011ac..6934f7e3c 100644 --- a/go.sum +++ b/go.sum @@ -2118,8 +2118,8 @@ kmodules.xyz/apiversion v0.2.0 h1:vAQYqZFm4xu4pbB1cAdHbFEPES6EQkcR4wc06xdTOWk= kmodules.xyz/apiversion v0.2.0/go.mod h1:oPX8g8LvlPdPX3Yc5YvCzJHQnw3YF/X4/jdW0b1am80= kmodules.xyz/authorizer v0.25.1 h1:W19AtlPD2A1+Q4UqDmNCJKfX9bKIgj+J6bQmkYwsHwY= kmodules.xyz/authorizer v0.25.1/go.mod h1:hKAbHpRkbxZJjc+cMTUiyxQxp7amKUVDiN145IrpnhA= -kmodules.xyz/client-go v0.25.35-0.20230925195212-e59241c267b5 h1:PsfOUk8Dl0e3n2ctl097E2u8xzL5XpvG8bSPg6m2yKo= -kmodules.xyz/client-go v0.25.35-0.20230925195212-e59241c267b5/go.mod h1:YfMtano/sAxZqhoxtLKjvclWtNQQIa6PmkVaooo+VuI= +kmodules.xyz/client-go v0.25.37 h1:/pKNG5ktBxGPtbTD1SIyAksLgZi1tqAPDJj9AwDkV8o= +kmodules.xyz/client-go v0.25.37/go.mod h1:YfMtano/sAxZqhoxtLKjvclWtNQQIa6PmkVaooo+VuI= kmodules.xyz/crd-schema-fuzz v0.25.0 h1:c5ZxNRqJak1bkGhECmyrKpzKGThFMB4088Kynyvngbc= kmodules.xyz/custom-resources v0.25.2 h1:+PJgUZvbbSgyNT7EX9gUZ3PIzY2LAW03TDW8cevvXqo= kmodules.xyz/custom-resources v0.25.2/go.mod h1:b9XjjKQMZ6KrLHXKqQz7YwV3M3BK8Hwi4KEwu5RadCo= diff --git a/vendor/kmodules.xyz/client-go/api/v1/cluster.go b/vendor/kmodules.xyz/client-go/api/v1/cluster.go index 064d38382..004cc9018 100644 --- a/vendor/kmodules.xyz/client-go/api/v1/cluster.go +++ b/vendor/kmodules.xyz/client-go/api/v1/cluster.go @@ -53,7 +53,9 @@ type ClusterManager int const ( ClusterManagerACE ClusterManager = 1 << iota - ClusterManagerOCM + ClusterManagerOCMHub + ClusterManagerOCMSpoke + ClusterManagerOCMMulticlusterControlplane ClusterManagerRancher ClusterManagerOpenShift ) @@ -62,8 +64,16 @@ func (cm ClusterManager) ManagedByACE() bool { return cm&ClusterManagerACE == ClusterManagerACE } -func (cm ClusterManager) ManagedByOCM() bool { - return cm&ClusterManagerOCM == ClusterManagerOCM +func (cm ClusterManager) ManagedByOCMHub() bool { + return cm&ClusterManagerOCMHub == ClusterManagerOCMHub +} + +func (cm ClusterManager) ManagedByOCMSpoke() bool { + return cm&ClusterManagerOCMSpoke == ClusterManagerOCMSpoke +} + +func (cm ClusterManager) ManagedByOCMMulticlusterControlplane() bool { + return cm&ClusterManagerOCMMulticlusterControlplane == ClusterManagerOCMMulticlusterControlplane } func (cm ClusterManager) ManagedByRancher() bool { @@ -79,8 +89,14 @@ func (cm ClusterManager) Strings() []string { if cm.ManagedByACE() { out = append(out, "ACE") } - if cm.ManagedByOCM() { - out = append(out, "OCM") + if cm.ManagedByOCMHub() { + out = append(out, "OCMHub") + } + if cm.ManagedByOCMSpoke() { + out = append(out, "OCMSpoke") + } + if cm.ManagedByOCMMulticlusterControlplane() { + out = append(out, "OCMMulticlusterControlplane") } if cm.ManagedByRancher() { out = append(out, "Rancher") diff --git a/vendor/kmodules.xyz/client-go/client/sa-token.go b/vendor/kmodules.xyz/client-go/client/sa-token.go new file mode 100644 index 000000000..a19177367 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/client/sa-token.go @@ -0,0 +1,115 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 client + +import ( + "context" + "time" + + core_util "kmodules.xyz/client-go/core/v1" + meta_util "kmodules.xyz/client-go/meta" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilrand "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#token-controller +func getServiceAccountTokenSecret(kc client.Client, sa client.ObjectKey) (*core.Secret, error) { + var list core.SecretList + err := kc.List(context.TODO(), &list, client.InNamespace(sa.Namespace)) + if err != nil { + return nil, err + } + if len(list.Items) == 0 { + return nil, errors.New("token secret still haven't created yet") + } + for _, s := range list.Items { + if s.Type == core.SecretTypeServiceAccountToken && + s.Annotations[core.ServiceAccountNameKey] == sa.Name { + + _, caFound := s.Data["ca.crt"] + _, tokenFound := s.Data["token"] + if caFound && tokenFound { + return &s, nil + } + } + } + return nil, errors.New("token secret is not ready yet") +} + +const ( + RetryTimeout = 10 * time.Second +) + +func tryGetServiceAccountTokenSecret(kc client.Client, sa client.ObjectKey) (secret *core.Secret, err error) { + err = wait.PollImmediate(kutil.RetryInterval, RetryTimeout, func() (bool, error) { + var e2 error + secret, e2 = getServiceAccountTokenSecret(kc, sa) + if e2 == nil { + return true, nil + } + klog.V(5).Infof("trying to get token secret for service account %s", sa) + return false, nil + }) + return +} + +func GetServiceAccountTokenSecret(kc client.Client, sa client.ObjectKey) (*core.Secret, error) { + secret, err := tryGetServiceAccountTokenSecret(kc, sa) + if err == nil { + klog.V(5).Infof("secret found for ServiceAccount %s", sa) + return secret, nil + } + + var saObj core.ServiceAccount + err = kc.Get(context.TODO(), sa, &saObj) + if err != nil { + return nil, errors.Wrapf(err, "failed to get ServiceAccount %s", sa) + } + + secretName := sa.Name + "-token-" + utilrand.String(6) + secret = &core.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: sa.Namespace, + }, + } + vt, err := CreateOrPatch(context.TODO(), kc, secret, func(obj client.Object, createOp bool) client.Object { + in := obj.(*core.Secret) + + in.Type = core.SecretTypeServiceAccountToken + ref := metav1.NewControllerRef(&saObj, core.SchemeGroupVersion.WithKind("ServiceAccount")) + core_util.EnsureOwnerReference(in, ref) + in.Annotations = meta_util.OverwriteKeys(in.Annotations, map[string]string{ + core.ServiceAccountNameKey: sa.Name, + }) + + return in + }) + if err != nil { + return nil, err + } + klog.Infof("%s Secret %s/%s", vt, secret.Namespace, secret.Name) + + return tryGetServiceAccountTokenSecret(kc, sa) +} diff --git a/vendor/kmodules.xyz/client-go/cluster/lib.go b/vendor/kmodules.xyz/client-go/cluster/lib.go index 7f3cae7e9..4cdd249b7 100644 --- a/vendor/kmodules.xyz/client-go/cluster/lib.go +++ b/vendor/kmodules.xyz/client-go/cluster/lib.go @@ -121,8 +121,14 @@ func DetectClusterManager(kc client.Client) kmapi.ClusterManager { if IsACEManaged(kc) { result |= kmapi.ClusterManagerACE } - if IsOpenClusterManaged(kc.RESTMapper()) { - result |= kmapi.ClusterManagerOCM + if IsOpenClusterHub(kc.RESTMapper()) { + result |= kmapi.ClusterManagerOCMHub + } + if IsOpenClusterSpoke(kc.RESTMapper()) { + result |= kmapi.ClusterManagerOCMSpoke + } + if IsOpenClusterMulticlusterControlplane(kc.RESTMapper()) { + result |= kmapi.ClusterManagerOCMMulticlusterControlplane } if IsRancherManaged(kc.RESTMapper()) { result |= kmapi.ClusterManagerRancher diff --git a/vendor/kmodules.xyz/client-go/cluster/ocm.go b/vendor/kmodules.xyz/client-go/cluster/ocm.go index 02d760ad9..71e6f0883 100644 --- a/vendor/kmodules.xyz/client-go/cluster/ocm.go +++ b/vendor/kmodules.xyz/client-go/cluster/ocm.go @@ -21,13 +21,17 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" ) -func IsOpenClusterManaged(mapper meta.RESTMapper) bool { +func IsOpenClusterHub(mapper meta.RESTMapper) bool { if _, err := mapper.RESTMappings(schema.GroupKind{ Group: "cluster.open-cluster-management.io", Kind: "ManagedCluster", }); err == nil { return true } + return false +} + +func IsOpenClusterSpoke(mapper meta.RESTMapper) bool { if _, err := mapper.RESTMappings(schema.GroupKind{ Group: "work.open-cluster-management.io", Kind: "AppliedManifestWork", @@ -36,3 +40,14 @@ func IsOpenClusterManaged(mapper meta.RESTMapper) bool { } return false } + +func IsOpenClusterMulticlusterControlplane(mapper meta.RESTMapper) bool { + var missingDeployment bool + if _, err := mapper.RESTMappings(schema.GroupKind{ + Group: "apps", + Kind: "Deployment", + }); meta.IsNoMatchError(err) { + missingDeployment = true + } + return IsOpenClusterHub(mapper) && missingDeployment +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/configmap.go b/vendor/kmodules.xyz/client-go/core/v1/configmap.go new file mode 100644 index 000000000..b114462e7 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/configmap.go @@ -0,0 +1,101 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +func CreateOrPatchConfigMap(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ConfigMap) *core.ConfigMap, opts metav1.PatchOptions) (*core.ConfigMap, kutil.VerbType, error) { + cur, err := c.CoreV1().ConfigMaps(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating ConfigMap %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().ConfigMaps(meta.Namespace).Create(ctx, transform(&core.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + }), metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchConfigMap(ctx, c, cur, transform, opts) +} + +func PatchConfigMap(ctx context.Context, c kubernetes.Interface, cur *core.ConfigMap, transform func(*core.ConfigMap) *core.ConfigMap, opts metav1.PatchOptions) (*core.ConfigMap, kutil.VerbType, error) { + return PatchConfigMapObject(ctx, c, cur, transform(cur.DeepCopy()), opts) +} + +func PatchConfigMapObject(ctx context.Context, c kubernetes.Interface, cur, mod *core.ConfigMap, opts metav1.PatchOptions) (*core.ConfigMap, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(mod) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.ConfigMap{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + klog.V(3).Infof("Patching ConfigMap %s/%s with %s", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().ConfigMaps(cur.Namespace).Patch(ctx, cur.Name, types.StrategicMergePatchType, patch, opts) + return out, kutil.VerbPatched, err +} + +func TryUpdateConfigMap(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ConfigMap) *core.ConfigMap, opts metav1.UpdateOptions) (result *core.ConfigMap, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().ConfigMaps(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().ConfigMaps(cur.Namespace).Update(ctx, transform(cur.DeepCopy()), opts) + return e2 == nil, nil + } + klog.Errorf("Attempt %d failed to update ConfigMap %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = errors.Errorf("failed to update ConfigMap %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/endpoints.go b/vendor/kmodules.xyz/client-go/core/v1/endpoints.go new file mode 100644 index 000000000..32ef8832e --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/endpoints.go @@ -0,0 +1,78 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +func CreateOrPatchEndpoints(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Endpoints) *core.Endpoints, opts metav1.PatchOptions) (*core.Endpoints, kutil.VerbType, error) { + cur, err := c.CoreV1().Endpoints(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating Endpoints %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().Endpoints(meta.Namespace).Create(ctx, transform(&core.Endpoints{ + TypeMeta: metav1.TypeMeta{ + Kind: "Endpoints", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + }), metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchEndpoints(ctx, c, cur, transform, opts) +} + +func PatchEndpoints(ctx context.Context, c kubernetes.Interface, cur *core.Endpoints, transform func(*core.Endpoints) *core.Endpoints, opts metav1.PatchOptions) (*core.Endpoints, kutil.VerbType, error) { + return PatchEndpointsObject(ctx, c, cur, transform(cur.DeepCopy()), opts) +} + +func PatchEndpointsObject(ctx context.Context, c kubernetes.Interface, cur, mod *core.Endpoints, opts metav1.PatchOptions) (*core.Endpoints, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(mod) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.Endpoints{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + klog.V(3).Infof("Patching Endpoints %s/%s with %s.", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().Endpoints(cur.Namespace).Patch(ctx, cur.Name, types.StrategicMergePatchType, patch, opts) + return out, kutil.VerbPatched, err +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/events.go b/vendor/kmodules.xyz/client-go/core/v1/events.go new file mode 100644 index 000000000..1fe9e2348 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/events.go @@ -0,0 +1,101 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +func CreateOrPatchEvent(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Event) *core.Event, opts metav1.PatchOptions) (*core.Event, kutil.VerbType, error) { + cur, err := c.CoreV1().Events(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating Event %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().Events(meta.Namespace).Create(ctx, transform(&core.Event{ + TypeMeta: metav1.TypeMeta{ + Kind: "Event", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + }), metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchEvent(ctx, c, cur, transform, opts) +} + +func PatchEvent(ctx context.Context, c kubernetes.Interface, cur *core.Event, transform func(*core.Event) *core.Event, opts metav1.PatchOptions) (*core.Event, kutil.VerbType, error) { + return PatchEventObject(ctx, c, cur, transform(cur.DeepCopy()), opts) +} + +func PatchEventObject(ctx context.Context, c kubernetes.Interface, cur, mod *core.Event, opts metav1.PatchOptions) (*core.Event, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(mod) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.Event{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + klog.V(3).Infof("Patching Event %s/%s with %s", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().Events(cur.Namespace).Patch(ctx, cur.Name, types.StrategicMergePatchType, patch, opts) + return out, kutil.VerbPatched, err +} + +func TryUpdateEvent(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Event) *core.Event, opts metav1.UpdateOptions) (result *core.Event, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().Events(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().Events(cur.Namespace).Update(ctx, transform(cur.DeepCopy()), opts) + return e2 == nil, nil + } + klog.Errorf("Attempt %d failed to update Event %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = errors.Errorf("failed to update Event %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/kubernetes.go b/vendor/kmodules.xyz/client-go/core/v1/kubernetes.go new file mode 100644 index 000000000..0a7bc4ffb --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/kubernetes.go @@ -0,0 +1,428 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "sort" + + jsoniter "github.com/json-iterator/go" + "gomodules.xyz/mergo" + core "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var json = jsoniter.ConfigFastest + +func AddFinalizer(m metav1.ObjectMeta, finalizer string) metav1.ObjectMeta { + for _, name := range m.Finalizers { + if name == finalizer { + return m + } + } + m.Finalizers = append(m.Finalizers, finalizer) + return m +} + +func HasFinalizer(m metav1.ObjectMeta, finalizer string) bool { + for _, name := range m.Finalizers { + if name == finalizer { + return true + } + } + return false +} + +func RemoveFinalizer(m metav1.ObjectMeta, finalizer string) metav1.ObjectMeta { + // https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating + r := m.Finalizers[:0] + for _, name := range m.Finalizers { + if name != finalizer { + r = append(r, name) + } + } + m.Finalizers = r + return m +} + +func EnsureContainerDeleted(containers []core.Container, name string) []core.Container { + for i, c := range containers { + if c.Name == name { + return append(containers[:i], containers[i+1:]...) + } + } + return containers +} + +func UpsertContainer(containers []core.Container, upsert core.Container) []core.Container { + for i, container := range containers { + if container.Name == upsert.Name { + err := mergo.Merge(&container, upsert, mergo.WithOverride) + if err != nil { + panic(err) + } + // mergo does not overwrite "dst (container)" using empty "src (upsert)" values. + // This causes problem we want to remove args or commands (eg, disable TLS). + // TODO: should this be done for all the []string type fields (eg, EnvFrom etc.)? + container.Command = upsert.Command + container.Args = upsert.Args + for i, env := range upsert.Env { + if env.ValueFrom != nil && + env.ValueFrom.FieldRef != nil && + env.ValueFrom.FieldRef.APIVersion == "" { + env.ValueFrom.FieldRef.APIVersion = "v1" + } + upsert.Env[i] = env + } + container.Env = upsert.Env + container.VolumeMounts = upsert.VolumeMounts + container.VolumeDevices = upsert.VolumeDevices + container.Resources = upsert.Resources + containers[i] = container + return containers + } + } + return append(containers, upsert) +} + +func UpsertContainers(containers []core.Container, addons []core.Container) []core.Container { + out := containers + for _, c := range addons { + out = UpsertContainer(out, c) + } + return out +} + +func DeleteContainer(containers []core.Container, name string) []core.Container { + for i, v := range containers { + if v.Name == name { + return append(containers[:i], containers[i+1:]...) + } + } + return containers +} + +func UpsertVolume(volumes []core.Volume, nv ...core.Volume) []core.Volume { + upsert := func(v core.Volume) { + for i, vol := range volumes { + if vol.Name == v.Name { + err := mergo.Merge(&volumes[i], v, mergo.WithOverride) + if err != nil { + panic(err) + } + return + } + } + volumes = append(volumes, v) + } + + for _, volume := range nv { + upsert(volume) + } + return volumes +} + +func ReplaceVolumes(existing []core.Volume, desired ...core.Volume) ([]core.Volume, error) { + merge := func(cur core.Volume) error { + for i, v := range desired { + if v.Name == cur.Name { + if err := mergo.Merge(&cur, v, mergo.WithOverride); err != nil { + return err + } + desired[i] = cur + break + } + } + return nil + } + + for _, cur := range existing { + if err := merge(cur); err != nil { + return nil, err + } + } + sort.Slice(desired, func(i, j int) bool { + return desired[i].Name < desired[j].Name + }) + return desired, nil +} + +func MustReplaceVolumes(existing []core.Volume, desired ...core.Volume) []core.Volume { + result, err := ReplaceVolumes(existing, desired...) + if err != nil { + panic(err) + } + return result +} + +func UpsertVolumeClaim(volumeClaims []core.PersistentVolumeClaim, upsert core.PersistentVolumeClaim) []core.PersistentVolumeClaim { + for i, vc := range volumeClaims { + if vc.Name == upsert.Name { + volumeClaims[i].Labels = upsert.Labels + volumeClaims[i].Annotations = upsert.Annotations + if err := mergo.Merge(&volumeClaims[i].Spec, upsert.Spec, mergo.WithOverride); err != nil { + panic(err) + } + return volumeClaims + } + } + return append(volumeClaims, upsert) +} + +func EnsureVolumeDeleted(volumes []core.Volume, name string) []core.Volume { + for i, v := range volumes { + if v.Name == name { + return append(volumes[:i], volumes[i+1:]...) + } + } + return volumes +} + +func UpsertVolumeMount(mounts []core.VolumeMount, nv ...core.VolumeMount) []core.VolumeMount { + upsert := func(m core.VolumeMount) { + for i, vol := range mounts { + if vol.Name == m.Name { + mounts[i] = m + return + } + } + mounts = append(mounts, m) + } + + for _, mount := range nv { + upsert(mount) + } + return mounts +} + +func EnsureVolumeMountDeleted(mounts []core.VolumeMount, name string) []core.VolumeMount { + for i, v := range mounts { + if v.Name == name { + return append(mounts[:i], mounts[i+1:]...) + } + } + return mounts +} + +func UpsertVolumeMountByPath(mounts []core.VolumeMount, nv core.VolumeMount) []core.VolumeMount { + for i, vol := range mounts { + if vol.MountPath == nv.MountPath { + mounts[i] = nv + return mounts + } + } + return append(mounts, nv) +} + +func EnsureVolumeMountDeletedByPath(mounts []core.VolumeMount, mountPath string) []core.VolumeMount { + for i, v := range mounts { + if v.MountPath == mountPath { + return append(mounts[:i], mounts[i+1:]...) + } + } + return mounts +} + +func UpsertEnvVars(vars []core.EnvVar, nv ...core.EnvVar) []core.EnvVar { + upsert := func(env core.EnvVar) { + if env.ValueFrom != nil && + env.ValueFrom.FieldRef != nil && + env.ValueFrom.FieldRef.APIVersion == "" { + env.ValueFrom.FieldRef.APIVersion = "v1" + } + + for i, v := range vars { + if v.Name == env.Name { + vars[i] = env + return + } + } + vars = append(vars, env) + } + + for _, env := range nv { + upsert(env) + } + return vars +} + +func EnsureEnvVarDeleted(vars []core.EnvVar, name string) []core.EnvVar { + for i, v := range vars { + if v.Name == name { + return append(vars[:i], vars[i+1:]...) + } + } + return vars +} + +func MergeLocalObjectReferences(l1, l2 []core.LocalObjectReference) []core.LocalObjectReference { + result := make([]core.LocalObjectReference, 0, len(l1)+len(l2)) + m := make(map[string]core.LocalObjectReference) + for _, ref := range l1 { + m[ref.Name] = ref + result = append(result, ref) + } + for _, ref := range l2 { + if _, found := m[ref.Name]; !found { + result = append(result, ref) + } + } + sort.Slice(result, func(i, j int) bool { return result[i].Name < result[j].Name }) + return result +} + +// NewOwnerRef creates an OwnerReference pointing to the given owner. +func NewOwnerRef(owner metav1.Object, gvk schema.GroupVersionKind) *metav1.OwnerReference { + blockOwnerDeletion := false + isController := false + return &metav1.OwnerReference{ + APIVersion: gvk.GroupVersion().String(), + Kind: gvk.Kind, + Name: owner.GetName(), + UID: owner.GetUID(), + BlockOwnerDeletion: &blockOwnerDeletion, + Controller: &isController, + } +} + +// EnsureOwnerReference adds owner if absent or syncs owner if already present. +// +// If you are writing a controller or an operator, use the following code snippet for dependent objects. +// Here, `controller = true` and `blockOwnerDeletion = true` +// +// owner := metav1.NewControllerRef(foo, samplev1alpha1.SchemeGroupVersion.WithKind("Foo")) +// EnsureOwnerReference(dependent, owner) +// +// If our CRD is not a controller but just want to be a owner, use the following code snippet. +// Here, `controller = false` and `blockOwnerDeletion = false` +// +// owner := NewOwnerRef(foo, samplev1alpha1.SchemeGroupVersion.WithKind("Foo")) +// EnsureOwnerReference(dependent, owner) +// +// To understand the impact of `blockOwnerDeletion`, read: +// - https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/#foreground-cascading-deletion +// - https://github.com/kubernetes/apimachinery/blob/v0.17.0/pkg/apis/meta/v1/types.go#L297-L323 +func EnsureOwnerReference(dependent metav1.Object, owner *metav1.OwnerReference) { + if owner == nil { + return + } + + refs := dependent.GetOwnerReferences() + + fi := -1 + for i := range refs { + if refs[i].UID == owner.UID { + fi = i + break + } + } + if fi == -1 { + refs = append(refs, *owner) + } else { + refs[fi] = *owner + } + + dependent.SetOwnerReferences(refs) +} + +func RemoveOwnerReference(dependent metav1.Object, owner metav1.Object) { + refs := dependent.GetOwnerReferences() + for i := range refs { + if refs[i].UID == owner.GetUID() { + refs = append(refs[:i], refs[i+1:]...) + break + } + } + dependent.SetOwnerReferences(refs) +} + +// IsOwnedBy checks if the dependent has a owner reference to the given owner +func IsOwnedBy(dependent metav1.Object, owner metav1.Object) (owned bool, controller bool) { + refs := dependent.GetOwnerReferences() + for i := range refs { + if refs[i].UID == owner.GetUID() { + return true, refs[i].Controller != nil && *refs[i].Controller + } + } + return false, false +} + +func IsOwnerOfGroup(ctrl *metav1.OwnerReference, group string) (bool, string, error) { + if ctrl == nil { + return false, "", nil + } + gv, err := schema.ParseGroupVersion(ctrl.APIVersion) + if err != nil { + return false, "", err + } + if gv.Group != group { + return false, "", nil + } + return true, ctrl.Kind, nil +} + +func IsOwnerOfGroupKind(ctrl *metav1.OwnerReference, group, kind string) (bool, error) { + if ctrl == nil { + return false, nil + } + gv, err := schema.ParseGroupVersion(ctrl.APIVersion) + if err != nil { + return false, err + } + if gv.Group != group { + return false, nil + } + if ctrl.Kind != kind { + return false, nil + } + return true, nil +} + +func UpsertToleration(tolerations []core.Toleration, upsert core.Toleration) []core.Toleration { + for i, toleration := range tolerations { + if toleration.Key == upsert.Key { + tolerations[i] = upsert + return tolerations + } + } + return append(tolerations, upsert) +} + +func RemoveToleration(tolerations []core.Toleration, key string) []core.Toleration { + for i, toleration := range tolerations { + if toleration.Key == key { + return append(tolerations[:i], tolerations[i+1:]...) + } + } + return tolerations +} + +func UpsertImagePullSecrets(refs []core.LocalObjectReference, upsert ...core.LocalObjectReference) []core.LocalObjectReference { + for i := range upsert { + var found bool + for j := range refs { + if refs[j].Name == upsert[i].Name { + found = true + break + } + } + if !found { + refs = append(refs, upsert[i]) + } + } + return refs +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/node.go b/vendor/kmodules.xyz/client-go/core/v1/node.go new file mode 100644 index 000000000..4fdb54615 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/node.go @@ -0,0 +1,287 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + + meta_util "kmodules.xyz/client-go/meta" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/metadata" + "k8s.io/client-go/tools/pager" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +func CreateOrPatchNode(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Node) *core.Node, opts metav1.PatchOptions) (*core.Node, kutil.VerbType, error) { + cur, err := c.CoreV1().Nodes().Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating Node %s", meta.Name) + out, err := c.CoreV1().Nodes().Create(ctx, transform(&core.Node{ + TypeMeta: metav1.TypeMeta{ + Kind: "Node", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + }), metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchNode(ctx, c, cur, transform, opts) +} + +func PatchNode(ctx context.Context, c kubernetes.Interface, cur *core.Node, transform func(*core.Node) *core.Node, opts metav1.PatchOptions) (*core.Node, kutil.VerbType, error) { + return PatchNodeObject(ctx, c, cur, transform(cur.DeepCopy()), opts) +} + +func PatchNodeObject(ctx context.Context, c kubernetes.Interface, cur, mod *core.Node, opts metav1.PatchOptions) (*core.Node, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(mod) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.Node{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + klog.V(3).Infof("Patching Node %s with %s", cur.Name, string(patch)) + out, err := c.CoreV1().Nodes().Patch(ctx, cur.Name, types.StrategicMergePatchType, patch, opts) + return out, kutil.VerbPatched, err +} + +func TryUpdateNode(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Node) *core.Node, opts metav1.UpdateOptions) (result *core.Node, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().Nodes().Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().Nodes().Update(ctx, transform(cur.DeepCopy()), opts) + return e2 == nil, nil + } + klog.Errorf("Attempt %d failed to update Node %s due to %v.", attempt, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = errors.Errorf("failed to update Node %s after %d attempts due to %v", meta.Name, attempt, err) + } + return +} + +// NodeReady returns whether a node is ready. +func NodeReady(node core.Node) bool { + for _, cond := range node.Status.Conditions { + if cond.Type != core.NodeReady { + continue + } + return cond.Status == core.ConditionTrue + } + return false +} + +// IsMaster returns whether a node is a master. +func IsMaster(node core.Node) bool { + _, ok17 := node.Labels["node-role.kubernetes.io/master"] + role16, ok16 := node.Labels["kubernetes.io/role"] + return ok17 || (ok16 && role16 == "master") +} + +type Topology struct { + Regions map[string][]string + TotalNodes int + InstanceTypes map[string]int + + LabelZone string + LabelRegion string + LabelInstanceType string + + // https://github.com/kubernetes/kubernetes/blob/v1.17.2/staging/src/k8s.io/api/core/v1/well_known_labels.go + + //LabelHostname = "kubernetes.io/hostname" + // + //LabelZoneFailureDomain = "failure-domain.beta.kubernetes.io/zone" + //LabelZoneRegion = "failure-domain.beta.kubernetes.io/region" + //LabelZoneFailureDomainStable = "topology.kubernetes.io/zone" + //LabelZoneRegionStable = "topology.kubernetes.io/region" + // + //LabelInstanceType = "beta.kubernetes.io/instance-type" + //LabelInstanceTypeStable = "node.kubernetes.io/instance-type" +} + +func (t Topology) ConvertAffinity(affinity *core.Affinity) { + if affinity == nil { + return + } + + if affinity.PodAffinity != nil { + t.convertPodAffinityTerm(affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution) + t.convertWeightedPodAffinityTerm(affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution) + } + + if affinity.PodAntiAffinity != nil { + t.convertPodAffinityTerm(affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution) + t.convertWeightedPodAffinityTerm(affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution) + } +} + +func isZoneKey(key string) bool { + return key == core.LabelZoneFailureDomain || key == "topology.kubernetes.io/zone" +} + +func isRegionKey(key string) bool { + return key == core.LabelZoneRegion || key == "topology.kubernetes.io/region" +} + +func isInstanceTypeKey(key string) bool { + return key == core.LabelInstanceType || key == "node.kubernetes.io/instance-type" +} + +func (t Topology) convertPodAffinityTerm(terms []core.PodAffinityTerm) { + for i := range terms { + if isZoneKey(terms[i].TopologyKey) { + terms[i].TopologyKey = t.LabelZone + } else if isRegionKey(terms[i].TopologyKey) { + terms[i].TopologyKey = t.LabelRegion + } else if isInstanceTypeKey(terms[i].TopologyKey) { + terms[i].TopologyKey = t.LabelInstanceType + } + } +} + +func (t Topology) convertWeightedPodAffinityTerm(terms []core.WeightedPodAffinityTerm) { + for i := range terms { + if isZoneKey(terms[i].PodAffinityTerm.TopologyKey) { + terms[i].PodAffinityTerm.TopologyKey = t.LabelZone + } else if isRegionKey(terms[i].PodAffinityTerm.TopologyKey) { + terms[i].PodAffinityTerm.TopologyKey = t.LabelRegion + } else if isInstanceTypeKey(terms[i].PodAffinityTerm.TopologyKey) { + terms[i].PodAffinityTerm.TopologyKey = t.LabelInstanceType + } + } +} + +func DetectTopology(ctx context.Context, mc metadata.Interface) (*Topology, error) { + var topology Topology + topology.TotalNodes = 0 + + mapRegion := make(map[string]sets.String) + instances := make(map[string]int) + first := true + + nc := mc.Resource(schema.GroupVersionResource{ + Version: "v1", + Resource: "nodes", + }) + lister := pager.New(pager.SimplePageFunc(func(opts metav1.ListOptions) (runtime.Object, error) { + return nc.List(ctx, opts) + })) + err := lister.EachListItem(context.Background(), metav1.ListOptions{}, func(obj runtime.Object) error { + topology.TotalNodes++ + + m, err := meta.Accessor(obj) + if err != nil { + return err + } + + labels := m.GetLabels() + + if first { + if _, ok := labels[core.LabelZoneRegionStable]; ok { + topology.LabelRegion = core.LabelZoneRegionStable + } else { + topology.LabelRegion = core.LabelZoneRegion + } + + if _, ok := labels[core.LabelZoneFailureDomainStable]; ok { + topology.LabelZone = core.LabelZoneFailureDomainStable + } else { + topology.LabelZone = core.LabelZoneFailureDomain + } + + if _, ok := labels[core.LabelInstanceTypeStable]; ok { + topology.LabelInstanceType = core.LabelInstanceTypeStable + } else { + topology.LabelInstanceType = core.LabelInstanceType + } + + first = false + } + + os, _ := meta_util.GetStringValueForKeys(labels, core.LabelOSStable, "beta.kubernetes.io/os") + if os != "linux" { + return nil + } + arch, _ := meta_util.GetStringValueForKeys(labels, core.LabelArchStable, "beta.kubernetes.io/arch") + if arch != "amd64" { + return nil + } + + region, _ := meta_util.GetStringValueForKeys(labels, topology.LabelRegion) + zone, _ := meta_util.GetStringValueForKeys(labels, topology.LabelZone) + if _, ok := mapRegion[region]; !ok { + mapRegion[region] = sets.NewString() + } + mapRegion[region].Insert(zone) + + instance, _ := meta_util.GetStringValueForKeys(labels, topology.LabelInstanceType) + if n, ok := instances[instance]; ok { + instances[instance] = n + 1 + } else { + instances[instance] = 1 + } + + return nil + }) + if err != nil { + return nil, err + } + + regions := make(map[string][]string) + for k, v := range mapRegion { + regions[k] = v.List() + } + topology.Regions = regions + topology.InstanceTypes = instances + + return &topology, nil +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/pod.go b/vendor/kmodules.xyz/client-go/core/v1/pod.go new file mode 100644 index 000000000..3decd3093 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/pod.go @@ -0,0 +1,210 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +func CreateOrPatchPod(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Pod) *core.Pod, opts metav1.PatchOptions) (*core.Pod, kutil.VerbType, error) { + cur, err := c.CoreV1().Pods(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating Pod %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().Pods(meta.Namespace).Create(ctx, transform(&core.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + }), metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchPod(ctx, c, cur, transform, opts) +} + +func PatchPod(ctx context.Context, c kubernetes.Interface, cur *core.Pod, transform func(*core.Pod) *core.Pod, opts metav1.PatchOptions) (*core.Pod, kutil.VerbType, error) { + return PatchPodObject(ctx, c, cur, transform(cur.DeepCopy()), opts) +} + +func PatchPodObject(ctx context.Context, c kubernetes.Interface, cur, mod *core.Pod, opts metav1.PatchOptions) (*core.Pod, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(mod) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.Pod{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + klog.V(3).Infof("Patching Pod %s/%s with %s", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().Pods(cur.Namespace).Patch(ctx, cur.Name, types.StrategicMergePatchType, patch, opts) + return out, kutil.VerbPatched, err +} + +func TryUpdatePod(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Pod) *core.Pod, opts metav1.UpdateOptions) (result *core.Pod, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().Pods(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().Pods(cur.Namespace).Update(ctx, transform(cur.DeepCopy()), opts) + return e2 == nil, nil + } + klog.Errorf("Attempt %d failed to update Pod %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = errors.Errorf("failed to update Pod %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} + +// IsPodReady returns true if a pod is ready considering readiness gates; false otherwise. +func IsPodReady(pod *core.Pod) bool { + conditions := []core.PodConditionType{ + core.PodReady, + } + for _, gate := range pod.Spec.ReadinessGates { + conditions = append(conditions, gate.ConditionType) + } + + for _, condition := range conditions { + if !IsPodConditionTrue(pod.Status.Conditions, condition) { + return false + } + } + return true +} + +// ref: https://github.com/coreos/prometheus-operator/blob/c79166fcff3dae7bb8bc1e6bddc81837c2d97c04/pkg/k8sutil/k8sutil.go#L64 +// PodRunningAndReady returns whether a pod is running and each container has +// passed it's ready state. +func PodRunningAndReady(pod core.Pod) (bool, error) { + switch pod.Status.Phase { + case core.PodFailed, core.PodSucceeded: + return false, errors.New("pod completed") + case core.PodRunning: + if IsPodReady(&pod) { + return true, nil + } + return false, errors.New("pod ready condition not found") + } + return false, nil +} + +func RestartPods(ctx context.Context, c kubernetes.Interface, namespace string, selector *metav1.LabelSelector) error { + r, err := metav1.LabelSelectorAsSelector(selector) + if err != nil { + return err + } + return c.CoreV1().Pods(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{ + LabelSelector: r.String(), + }) +} + +func WaitUntilPodRunning(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta) error { + return wait.PollImmediate(kutil.RetryInterval, kutil.ReadinessTimeout, func() (bool, error) { + if pod, err := c.CoreV1().Pods(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}); err == nil { + runningAndReady, _ := PodRunningAndReady(*pod) + return runningAndReady, nil + } + return false, nil + }) +} + +func WaitUntilPodRunningBySelector(ctx context.Context, c kubernetes.Interface, namespace string, selector *metav1.LabelSelector, count int) error { + r, err := metav1.LabelSelectorAsSelector(selector) + if err != nil { + return err + } + + return wait.PollImmediate(kutil.RetryInterval, kutil.ReadinessTimeout, func() (bool, error) { + podList, err := c.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ + LabelSelector: r.String(), + }) + if err != nil { + return false, nil + } + + if len(podList.Items) != count { + return false, nil + } + + for _, pod := range podList.Items { + runningAndReady, _ := PodRunningAndReady(pod) + if !runningAndReady { + return false, nil + } + } + return true, nil + }) +} + +func WaitUntilPodDeletedBySelector(ctx context.Context, c kubernetes.Interface, namespace string, selector *metav1.LabelSelector) error { + sel, err := metav1.LabelSelectorAsSelector(selector) + if err != nil { + return err + } + + return wait.PollImmediate(kutil.RetryInterval, kutil.ReadinessTimeout, func() (bool, error) { + podList, err := c.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ + LabelSelector: sel.String(), + }) + if err != nil { + return false, nil + } + return len(podList.Items) == 0, nil + }) +} + +// WaitUntillPodTerminatedByLabel waits until all pods with the label are terminated. Timeout is 5 minutes. +func WaitUntillPodTerminatedByLabel(ctx context.Context, c kubernetes.Interface, namespace string, label string) error { + return wait.PollImmediate(kutil.RetryInterval, kutil.GCTimeout, func() (bool, error) { + podList, err := c.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: label}) + if err != nil { + return false, nil + } + return len(podList.Items) == 0, nil + }) +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/pod_status.go b/vendor/kmodules.xyz/client-go/core/v1/pod_status.go new file mode 100644 index 000000000..eab9e7664 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/pod_status.go @@ -0,0 +1,110 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + core "k8s.io/api/core/v1" +) + +const ( + PodConditionTypeReady = core.PodConditionType("kubedb.com/Ready") +) + +// HasCondition returns "true" if the desired condition provided in "condType" is present in the condition list. +// Otherwise, it returns "false". +func HasPodCondition(conditions []core.PodCondition, condType core.PodConditionType) bool { + for i := range conditions { + if conditions[i].Type == condType { + return true + } + } + return false +} + +// GetPodCondition returns a pointer to the desired condition referred by "condType". Otherwise, it returns nil. +func GetPodCondition(conditions []core.PodCondition, condType core.PodConditionType) (int, *core.PodCondition) { + for i := range conditions { + c := conditions[i] + if c.Type == condType { + return i, &c + } + } + return -1, nil +} + +// SetPodCondition add/update the desired condition to the condition list. It does nothing if the condition is already in +// its desired state. +func SetPodCondition(conditions []core.PodCondition, newCondition core.PodCondition) []core.PodCondition { + idx, curCond := GetPodCondition(conditions, newCondition.Type) + // The desired conditions is not in the condition list or is not in its desired state. + // If the current condition status is in its desired state, we have nothing to do. Just return the original condition list. + // Update it if present in the condition list, or append the new condition if it does not present. + if curCond == nil || idx == -1 { + return append(conditions, newCondition) + } else if curCond.Status == newCondition.Status { + return conditions + } else if curCond.Status != newCondition.Status { + conditions[idx].Status = newCondition.Status + conditions[idx].LastTransitionTime = newCondition.LastTransitionTime + conditions[idx].Reason = newCondition.Reason + conditions[idx].Message = newCondition.Message + } + return conditions +} + +// RemovePodCondition remove a condition from the condition list referred by "condType" parameter. +func RemovePodCondition(conditions []core.PodCondition, condType core.PodConditionType) []core.PodCondition { + idx, _ := GetPodCondition(conditions, condType) + if idx == -1 { + // The desired condition is not present in the condition list. So, nothing to do. + return conditions + } + return append(conditions[:idx], conditions[idx+1:]...) +} + +// IsPodConditionTrue returns "true" if the desired condition is in true state. +// It returns "false" if the desired condition is not in "true" state or is not in the condition list. +func IsPodConditionTrue(conditions []core.PodCondition, condType core.PodConditionType) bool { + for i := range conditions { + if conditions[i].Type == condType && conditions[i].Status == core.ConditionTrue { + return true + } + } + return false +} + +// IsPodConditionFalse returns "true" if the desired condition is in false state. +// It returns "false" if the desired condition is not in "false" state or is not in the condition list. +func IsPodConditionFalse(conditions []core.PodCondition, condType core.PodConditionType) bool { + for i := range conditions { + if conditions[i].Type == condType && conditions[i].Status == core.ConditionFalse { + return true + } + } + return false +} + +func UpsertPodReadinessGateConditionType(readinessGates []core.PodReadinessGate, conditionType core.PodConditionType) []core.PodReadinessGate { + for i := range readinessGates { + if readinessGates[i].ConditionType == conditionType { + return readinessGates + } + } + return append(readinessGates, core.PodReadinessGate{ + ConditionType: conditionType, + }) +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/pv.go b/vendor/kmodules.xyz/client-go/core/v1/pv.go new file mode 100644 index 000000000..1640a6ac3 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/pv.go @@ -0,0 +1,101 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +func CreateOrPatchPV(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.PersistentVolume) *core.PersistentVolume, opts metav1.PatchOptions) (*core.PersistentVolume, kutil.VerbType, error) { + cur, err := c.CoreV1().PersistentVolumes().Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating PersistentVolume %s.", meta.Name) + out, err := c.CoreV1().PersistentVolumes().Create(ctx, transform(&core.PersistentVolume{ + TypeMeta: metav1.TypeMeta{ + Kind: "PersistentVolume", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + }), metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchPV(ctx, c, cur, transform, opts) +} + +func PatchPV(ctx context.Context, c kubernetes.Interface, cur *core.PersistentVolume, transform func(*core.PersistentVolume) *core.PersistentVolume, opts metav1.PatchOptions) (*core.PersistentVolume, kutil.VerbType, error) { + return PatchPVObject(ctx, c, cur, transform(cur.DeepCopy()), opts) +} + +func PatchPVObject(ctx context.Context, c kubernetes.Interface, cur, mod *core.PersistentVolume, opts metav1.PatchOptions) (*core.PersistentVolume, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(mod) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.PersistentVolume{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + klog.V(3).Infof("Patching PersistentVolume %s with %s.", cur.Name, string(patch)) + out, err := c.CoreV1().PersistentVolumes().Patch(ctx, cur.Name, types.StrategicMergePatchType, patch, opts) + return out, kutil.VerbPatched, err +} + +func TryUpdatePV(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.PersistentVolume) *core.PersistentVolume, opts metav1.UpdateOptions) (result *core.PersistentVolume, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().PersistentVolumes().Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().PersistentVolumes().Update(ctx, transform(cur.DeepCopy()), opts) + return e2 == nil, nil + } + klog.Errorf("Attempt %d failed to update PersistentVolume %s due to %v.", attempt, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = errors.Errorf("failed to update PersistentVolume %s after %d attempts due to %v", meta.Name, attempt, err) + } + return +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/pvc.go b/vendor/kmodules.xyz/client-go/core/v1/pvc.go new file mode 100644 index 000000000..8506715db --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/pvc.go @@ -0,0 +1,101 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +func CreateOrPatchPVC(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.PersistentVolumeClaim) *core.PersistentVolumeClaim, opts metav1.PatchOptions) (*core.PersistentVolumeClaim, kutil.VerbType, error) { + cur, err := c.CoreV1().PersistentVolumeClaims(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating PersistentVolumeClaim %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().PersistentVolumeClaims(meta.Namespace).Create(ctx, transform(&core.PersistentVolumeClaim{ + TypeMeta: metav1.TypeMeta{ + Kind: "PersistentVolumeClaim", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + }), metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchPVC(ctx, c, cur, transform, opts) +} + +func PatchPVC(ctx context.Context, c kubernetes.Interface, cur *core.PersistentVolumeClaim, transform func(*core.PersistentVolumeClaim) *core.PersistentVolumeClaim, opts metav1.PatchOptions) (*core.PersistentVolumeClaim, kutil.VerbType, error) { + return PatchPVCObject(ctx, c, cur, transform(cur.DeepCopy()), opts) +} + +func PatchPVCObject(ctx context.Context, c kubernetes.Interface, cur, mod *core.PersistentVolumeClaim, opts metav1.PatchOptions) (*core.PersistentVolumeClaim, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(mod) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.PersistentVolumeClaim{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + klog.V(3).Infof("Patching PersistentVolumeClaim %s/%s with %s.", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().PersistentVolumeClaims(cur.Namespace).Patch(ctx, cur.Name, types.StrategicMergePatchType, patch, opts) + return out, kutil.VerbPatched, err +} + +func TryUpdatePVC(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.PersistentVolumeClaim) *core.PersistentVolumeClaim, opts metav1.UpdateOptions) (result *core.PersistentVolumeClaim, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().PersistentVolumeClaims(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().PersistentVolumeClaims(cur.Namespace).Update(ctx, transform(cur.DeepCopy()), opts) + return e2 == nil, nil + } + klog.Errorf("Attempt %d failed to update PersistentVolumeClaim %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = errors.Errorf("failed to update PersistentVolumeClaim %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/rc.go b/vendor/kmodules.xyz/client-go/core/v1/rc.go new file mode 100644 index 000000000..e9aa32ed9 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/rc.go @@ -0,0 +1,112 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + + "github.com/pkg/errors" + "gomodules.xyz/pointer" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +func CreateOrPatchRC(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ReplicationController) *core.ReplicationController, opts metav1.PatchOptions) (*core.ReplicationController, kutil.VerbType, error) { + cur, err := c.CoreV1().ReplicationControllers(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating ReplicationController %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().ReplicationControllers(meta.Namespace).Create(ctx, transform(&core.ReplicationController{ + TypeMeta: metav1.TypeMeta{ + Kind: "ReplicationController", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + }), metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchRC(ctx, c, cur, transform, opts) +} + +func PatchRC(ctx context.Context, c kubernetes.Interface, cur *core.ReplicationController, transform func(*core.ReplicationController) *core.ReplicationController, opts metav1.PatchOptions) (*core.ReplicationController, kutil.VerbType, error) { + return PatchRCObject(ctx, c, cur, transform(cur.DeepCopy()), opts) +} + +func PatchRCObject(ctx context.Context, c kubernetes.Interface, cur, mod *core.ReplicationController, opts metav1.PatchOptions) (*core.ReplicationController, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(mod) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.ReplicationController{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + klog.V(3).Infof("Patching ReplicationController %s/%s with %s.", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().ReplicationControllers(cur.Namespace).Patch(ctx, cur.Name, types.StrategicMergePatchType, patch, opts) + return out, kutil.VerbPatched, err +} + +func TryUpdateRC(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ReplicationController) *core.ReplicationController, opts metav1.UpdateOptions) (result *core.ReplicationController, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().ReplicationControllers(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().ReplicationControllers(cur.Namespace).Update(ctx, transform(cur.DeepCopy()), opts) + return e2 == nil, nil + } + klog.Errorf("Attempt %d failed to update ReplicationController %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = errors.Errorf("failed to update ReplicationController %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} + +func WaitUntilRCReady(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta) error { + return wait.PollImmediate(kutil.RetryInterval, kutil.ReadinessTimeout, func() (bool, error) { + if obj, err := c.CoreV1().ReplicationControllers(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}); err == nil { + return pointer.Int32(obj.Spec.Replicas) == obj.Status.ReadyReplicas, nil + } + + return false, nil + }) +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/sa-token.go b/vendor/kmodules.xyz/client-go/core/v1/sa-token.go new file mode 100644 index 000000000..bb1f1ca4f --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/sa-token.go @@ -0,0 +1,111 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + "time" + + meta_util "kmodules.xyz/client-go/meta" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + utilrand "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +// https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#token-controller +func getServiceAccountTokenSecret(kc kubernetes.Interface, sa types.NamespacedName) (*core.Secret, error) { + list, err := kc.CoreV1().Secrets(sa.Namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + if len(list.Items) == 0 { + return nil, errors.New("token secret still haven't created yet") + } + for _, s := range list.Items { + if s.Type == core.SecretTypeServiceAccountToken && + s.Annotations[core.ServiceAccountNameKey] == sa.Name { + + _, caFound := s.Data["ca.crt"] + _, tokenFound := s.Data["token"] + if caFound && tokenFound { + return &s, nil + } + } + } + return nil, errors.New("token secret is not ready yet") +} + +const ( + retryTimeout = 10 * time.Second +) + +func tryGetServiceAccountTokenSecret(kc kubernetes.Interface, sa types.NamespacedName) (secret *core.Secret, err error) { + err = wait.PollImmediate(kutil.RetryInterval, retryTimeout, func() (bool, error) { + var e2 error + secret, e2 = getServiceAccountTokenSecret(kc, sa) + if e2 == nil { + return true, nil + } + klog.V(5).Infof("trying to get token secret for service account %s", sa) + return false, nil + }) + return +} + +func GetServiceAccountTokenSecret(kc kubernetes.Interface, sa types.NamespacedName) (*core.Secret, error) { + secret, err := tryGetServiceAccountTokenSecret(kc, sa) + if err == nil { + klog.V(5).Infof("secret found for ServiceAccount %s", sa) + return secret, nil + } + + saObj, err := kc.CoreV1().ServiceAccounts(sa.Namespace).Get(context.TODO(), sa.Name, metav1.GetOptions{}) + if err != nil { + return nil, errors.Wrapf(err, "failed to get ServiceAccount %s", sa) + } + + secretName := sa.Name + "-token-" + utilrand.String(6) + secret = &core.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: sa.Namespace, + }, + } + secret, vt, err := CreateOrPatchSecret(context.TODO(), kc, secret.ObjectMeta, func(in *core.Secret) *core.Secret { + in.Type = core.SecretTypeServiceAccountToken + ref := metav1.NewControllerRef(saObj, core.SchemeGroupVersion.WithKind("ServiceAccount")) + EnsureOwnerReference(in, ref) + in.Annotations = meta_util.OverwriteKeys(in.Annotations, map[string]string{ + core.ServiceAccountNameKey: sa.Name, + }) + + return in + }, metav1.PatchOptions{}) + if err != nil { + return nil, err + } + klog.Infof("%s Secret %s/%s", vt, secret.Namespace, secret.Name) + + return tryGetServiceAccountTokenSecret(kc, sa) +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/secret.go b/vendor/kmodules.xyz/client-go/core/v1/secret.go new file mode 100644 index 000000000..c5e96663e --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/secret.go @@ -0,0 +1,134 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +func CreateOrPatchSecret(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Secret) *core.Secret, opts metav1.PatchOptions, forceSyncType ...bool) (*core.Secret, kutil.VerbType, error) { + syncType := len(forceSyncType) == 1 && forceSyncType[0] + + cur, err := c.CoreV1().Secrets(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating Secret %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().Secrets(meta.Namespace).Create(ctx, transform(&core.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + }), metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + + mod := transform(cur.DeepCopy()) + if mod.Type != cur.Type && syncType && len(opts.DryRun) == 0 { + // secret type can't be modified once created, so we have to delete first, then recreate with correct type + klog.Warningf("Secret %s/%s type is modified, deleting first.", meta.Namespace, meta.Name) + foregroundDeletion := metav1.DeletePropagationForeground + err = c.CoreV1().Secrets(meta.Namespace).Delete(ctx, meta.Name, metav1.DeleteOptions{ + TypeMeta: metav1.TypeMeta{}, + PropagationPolicy: &foregroundDeletion, + DryRun: opts.DryRun, + }) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + klog.V(3).Infof("Creating Secret %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().Secrets(meta.Namespace).Create(ctx, mod, metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } + return PatchSecretObject(ctx, c, cur, mod, opts) +} + +func PatchSecret(ctx context.Context, c kubernetes.Interface, cur *core.Secret, transform func(*core.Secret) *core.Secret, opts metav1.PatchOptions) (*core.Secret, kutil.VerbType, error) { + return PatchSecretObject(ctx, c, cur, transform(cur.DeepCopy()), opts) +} + +func PatchSecretObject(ctx context.Context, c kubernetes.Interface, cur, mod *core.Secret, opts metav1.PatchOptions) (*core.Secret, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(mod) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.Secret{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + klog.V(3).Infof("Patching Secret %s/%s", cur.Namespace, cur.Name) + out, err := c.CoreV1().Secrets(cur.Namespace).Patch(ctx, cur.Name, types.StrategicMergePatchType, patch, opts) + return out, kutil.VerbPatched, err +} + +func TryUpdateSecret(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Secret) *core.Secret, opts metav1.UpdateOptions) (result *core.Secret, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().Secrets(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().Secrets(cur.Namespace).Update(ctx, transform(cur.DeepCopy()), opts) + return e2 == nil, nil + } + klog.Errorf("Attempt %d failed to update Secret %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = errors.Errorf("failed to update Secret %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} + +func ObfuscateSecret(in core.Secret) *core.Secret { + data := make(map[string][]byte) + for k := range in.Data { + data[k] = []byte("-") + } + in.Data = data + return &in +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/service.go b/vendor/kmodules.xyz/client-go/core/v1/service.go new file mode 100644 index 000000000..db3eb295a --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/service.go @@ -0,0 +1,153 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +func CreateOrPatchService(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Service) *core.Service, opts metav1.PatchOptions) (*core.Service, kutil.VerbType, error) { + cur, err := c.CoreV1().Services(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating Service %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().Services(meta.Namespace).Create(ctx, transform(&core.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + }), metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchService(ctx, c, cur, transform, opts) +} + +func PatchService(ctx context.Context, c kubernetes.Interface, cur *core.Service, transform func(*core.Service) *core.Service, opts metav1.PatchOptions) (*core.Service, kutil.VerbType, error) { + return PatchServiceObject(ctx, c, cur, transform(cur.DeepCopy()), opts) +} + +func PatchServiceObject(ctx context.Context, c kubernetes.Interface, cur, mod *core.Service, opts metav1.PatchOptions) (*core.Service, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(mod) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.Service{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + klog.V(3).Infof("Patching Service %s/%s with %s.", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().Services(cur.Namespace).Patch(ctx, cur.Name, types.StrategicMergePatchType, patch, opts) + return out, kutil.VerbPatched, err +} + +func TryUpdateService(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Service) *core.Service, opts metav1.UpdateOptions) (result *core.Service, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().Services(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().Services(cur.Namespace).Update(ctx, transform(cur.DeepCopy()), opts) + return e2 == nil, nil + } + klog.Errorf("Attempt %d failed to update Service %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = errors.Errorf("failed to update Service %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} + +func MergeServicePorts(cur, desired []core.ServicePort) []core.ServicePort { + if len(cur) == 0 { + return desired + } + + // ports + curPorts := make(map[string]core.ServicePort) + for _, p := range cur { + curPorts[p.Name] = p + } + for i, dp := range desired { + cp, ok := curPorts[dp.Name] + + // svc port not found + if !ok { + continue + } + + if dp.Port == 0 { + dp.Port = cp.Port + } + if dp.NodePort == 0 { + dp.NodePort = cp.NodePort // avoid reassigning port + } + if dp.Protocol == "" { + dp.Protocol = cp.Protocol + } + if dp.AppProtocol == nil { + dp.AppProtocol = cp.AppProtocol + } + desired[i] = dp + } + return desired +} + +func WaitUntilServiceDeletedBySelector(ctx context.Context, c kubernetes.Interface, namespace string, selector *metav1.LabelSelector) error { + sel, err := metav1.LabelSelectorAsSelector(selector) + if err != nil { + return err + } + + return wait.PollImmediate(kutil.RetryInterval, kutil.ReadinessTimeout, func() (bool, error) { + svcList, err := c.CoreV1().Services(namespace).List(ctx, metav1.ListOptions{ + LabelSelector: sel.String(), + }) + if err != nil { + return false, nil + } + return len(svcList.Items) == 0, nil + }) +} diff --git a/vendor/kmodules.xyz/client-go/core/v1/serviceaccount.go b/vendor/kmodules.xyz/client-go/core/v1/serviceaccount.go new file mode 100644 index 000000000..fdb00c9e2 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/core/v1/serviceaccount.go @@ -0,0 +1,131 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1 + +import ( + "context" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" +) + +func CreateOrPatchServiceAccount(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ServiceAccount) *core.ServiceAccount, opts metav1.PatchOptions) (*core.ServiceAccount, kutil.VerbType, error) { + cur, err := c.CoreV1().ServiceAccounts(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating ServiceAccount %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().ServiceAccounts(meta.Namespace).Create(ctx, transform(&core.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceAccount", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + }), metav1.CreateOptions{ + DryRun: opts.DryRun, + FieldManager: opts.FieldManager, + }) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchServiceAccount(ctx, c, cur, transform, opts) +} + +func PatchServiceAccount(ctx context.Context, c kubernetes.Interface, cur *core.ServiceAccount, transform func(*core.ServiceAccount) *core.ServiceAccount, opts metav1.PatchOptions) (*core.ServiceAccount, kutil.VerbType, error) { + return PatchServiceAccountObject(ctx, c, cur, transform(cur.DeepCopy()), opts) +} + +func PatchServiceAccountObject(ctx context.Context, c kubernetes.Interface, cur, mod *core.ServiceAccount, opts metav1.PatchOptions) (*core.ServiceAccount, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(mod) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.ServiceAccount{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + klog.V(3).Infof("Patching ServiceAccount %s/%s with %s", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().ServiceAccounts(cur.Namespace).Patch(ctx, cur.Name, types.StrategicMergePatchType, patch, opts) + return out, kutil.VerbPatched, err +} + +func TryUpdateServiceAccount(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ServiceAccount) *core.ServiceAccount, opts metav1.UpdateOptions) (result *core.ServiceAccount, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().ServiceAccounts(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().ServiceAccounts(cur.Namespace).Update(ctx, transform(cur.DeepCopy()), opts) + return e2 == nil, nil + } + klog.Errorf("Attempt %d failed to update ServiceAccount %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = errors.Errorf("failed to update ServiceAccount %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} + +func WaitUntillServiceAccountDeleted(ctx context.Context, c kubernetes.Interface, meta metav1.ObjectMeta) error { + return wait.PollImmediate(kutil.RetryInterval, kutil.GCTimeout, func() (bool, error) { + _, err := c.CoreV1().ServiceAccounts(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{}) + if err != nil && kerr.IsNotFound(err) { + return true, nil + } + return false, nil + }) +} + +// IsServiceAccountToken returns true if the secret is a valid api token for the service account +func IsServiceAccountToken(secret *core.Secret, sa *core.ServiceAccount) bool { + if secret.Type != core.SecretTypeServiceAccountToken { + return false + } + + name := secret.Annotations[core.ServiceAccountNameKey] + uid := secret.Annotations[core.ServiceAccountUIDKey] + if name != sa.Name { + // Name must match + return false + } + if len(uid) > 0 && uid != string(sa.UID) { + // If UID is specified, it must match + return false + } + + return true +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 859e50526..bcda4f2f7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1764,7 +1764,7 @@ kmodules.xyz/authorizer/apiserver kmodules.xyz/authorizer/rbac kmodules.xyz/authorizer/rbac/helpers kmodules.xyz/authorizer/rbac/validation -# kmodules.xyz/client-go v0.25.35-0.20230925195212-e59241c267b5 +# kmodules.xyz/client-go v0.25.37 ## explicit; go 1.18 kmodules.xyz/client-go kmodules.xyz/client-go/api/v1 @@ -1774,6 +1774,7 @@ kmodules.xyz/client-go/client kmodules.xyz/client-go/client/apiutil kmodules.xyz/client-go/client/duck kmodules.xyz/client-go/cluster +kmodules.xyz/client-go/core/v1 kmodules.xyz/client-go/discovery kmodules.xyz/client-go/meta kmodules.xyz/client-go/openapi