From ea8f4aa0d137b347c83f9c9d380c36399bb91e8a Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Mon, 14 Apr 2025 17:42:03 +0800 Subject: [PATCH 1/6] add related api resources --- .../apis/cloud/v1alpha1/apikey_types.go | 135 +++++++++ .../apis/cloud/v1alpha1/conditions.go | 39 +++ .../cloud/v1alpha1/serviceaccount_types.go | 58 ++++ .../v1alpha1/serviceaccountbinding_types.go | 54 ++++ .../v1alpha1/zz_generated.api.register.go | 3 + .../cloud/v1alpha1/zz_generated.deepcopy.go | 285 +++++++++++++++++- .../clientset/typed/cloud/v1alpha1/apikey.go | 192 ++++++++++++ .../typed/cloud/v1alpha1/cloud_client.go | 15 + .../typed/cloud/v1alpha1/fake/fake_apikey.go | 138 +++++++++ .../cloud/v1alpha1/fake/fake_cloud_client.go | 12 + .../v1alpha1/fake/fake_serviceaccount.go | 138 +++++++++ .../fake/fake_serviceaccountbinding.go | 138 +++++++++ .../cloud/v1alpha1/generated_expansion.go | 6 + .../typed/cloud/v1alpha1/serviceaccount.go | 192 ++++++++++++ .../cloud/v1alpha1/serviceaccountbinding.go | 192 ++++++++++++ .../externalversions/cloud/v1alpha1/apikey.go | 87 ++++++ .../cloud/v1alpha1/interface.go | 21 ++ .../cloud/v1alpha1/serviceaccount.go | 87 ++++++ .../cloud/v1alpha1/serviceaccountbinding.go | 87 ++++++ .../externalversions/generic.go | 6 + .../cloud/v1alpha1/apikey.go | 96 ++++++ .../cloud/v1alpha1/expansion_generated.go | 24 ++ .../cloud/v1alpha1/serviceaccount.go | 96 ++++++ .../cloud/v1alpha1/serviceaccountbinding.go | 96 ++++++ 24 files changed, 2195 insertions(+), 2 deletions(-) create mode 100644 pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go create mode 100644 pkg/streamnativecloud/apis/cloud/v1alpha1/conditions.go create mode 100644 pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go create mode 100644 pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go create mode 100644 pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/apikey.go create mode 100644 pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_apikey.go create mode 100644 pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_serviceaccount.go create mode 100644 pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_serviceaccountbinding.go create mode 100644 pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/serviceaccount.go create mode 100644 pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/serviceaccountbinding.go create mode 100644 pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/apikey.go create mode 100644 pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/serviceaccount.go create mode 100644 pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/serviceaccountbinding.go create mode 100644 pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/apikey.go create mode 100644 pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/serviceaccount.go create mode 100644 pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/serviceaccountbinding.go diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go new file mode 100644 index 00000000..3f22b44b --- /dev/null +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go @@ -0,0 +1,135 @@ +// Copyright 2025 StreamNative +// +// 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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// APIKey +// +k8s:openapi-gen=true +// +resource:path=apikeys,strategy=APIKeyStrategy +// +kubebuilder:categories=all +type APIKey struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + Spec APIKeySpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` + Status APIKeyStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` +} + +// APIKeySpec defines the desired state of APIKey +type APIKeySpec struct { + // InstanceName is the name of the instance this API key is for + InstanceName string `json:"instanceName,omitempty" protobuf:"bytes,1,opt,name=instanceName"` + + // ServiceAccountName is the name of the service account this API key is for + ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"bytes,2,opt,name=serviceAccountName"` + + // Description is a user defined description of the key + // +optional + Description string `json:"description,omitempty" protobuf:"bytes,3,opt,name=description"` + + // Expiration is a duration (as a golang duration string) that defines how long this API key is valid for. + // This can only be set on initial creation and not updated later + // +optional + ExpirationTime *metav1.Time `json:"expirationTime,omitempty" protobuf:"bytes,4,opt,name=expiration"` + + // Revoke is a boolean that defines if the token of this API key should be revoked. + // +kubebuilder:default=false + // +optional + Revoke bool `json:"revoke,omitempty" protobuf:"bytes,5,opt,name=revoke"` + + // EncryptionKey is a public key to encrypt the API Key token. + // Please provide an RSA key with modulus length of at least 2048 bits. + // See: https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey#rsa_key_pair_generation + // See: https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/exportKey#subjectpublickeyinfo_export + // +optional + EncryptionKey *EncryptionKey `json:"encryptionKey,omitempty" protobuf:"bytes,6,opt,name=encryptionKey"` +} + +// EncryptionKey specifies a public key for encryption purposes. +// Either a PEM or JWK must be specified. +type EncryptionKey struct { + // PEM is a PEM-encoded public key in PKIX, ASN.1 DER form ("spki" format). + // +optional + PEM string `json:"pem,omitempty" protobuf:"bytes,1,opt,name=pem"` + + // JWK is a JWK-encoded public key. + // +optional + JWK string `json:"jwk,omitempty" protobuf:"bytes,2,opt,name=jwk"` +} + +// APIKeyStatus defines the observed state of ServiceAccount +type APIKeyStatus struct { + // ObservedGeneration is the most recent generation observed by the controller. + //ObservedGeneration int64 `json:"observedGeneration" protobuf:"varint,1,opt,name=observedGeneration"` + + // KeyId is a generated field that is a uid for the token + // +optional + KeyId *string `json:"keyId,omitempty" protobuf:"bytes,1,opt,name=keyId"` + + // IssuedAt is a timestamp of when the key was issued, stored as an epoch in seconds + // +optional + IssuedAt *metav1.Time `json:"issuedAt,omitempty" protobuf:"bytes,2,opt,name=issuedAt"` + + // ExpiresAt is a timestamp of when the key expires + // +optional + ExpiresAt *metav1.Time `json:"expiresAt,omitempty" protobuf:"bytes,3,opt,name=expiresAt"` + + // Token is the plaintext security token issued for the key. + // +optional + Token *string `json:"token,omitempty" protobuf:"bytes,4,opt,name=keySecret"` + + // Token is the encrypted security token issued for the key. + // +optional + EncryptedToken *EncryptedToken `json:"encryptedToken,omitempty" protobuf:"bytes,7,opt,name=encryptedToken"` + + // Conditions is an array of current observed service account conditions. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,5,rep,name=conditions"` + + // ExpiresAt is a timestamp of when the key was revoked, it triggers revocation action + // +optional + RevokedAt *metav1.Time `json:"revokedAt,omitempty" protobuf:"bytes,6,opt,name=revokedAt"` +} + +// EncryptedToken is token that is encrypted using an encryption key. +// +structType=atomic +type EncryptedToken struct { + // JWE is the token as a JSON Web Encryption (JWE) message. + // For RSA public keys, the key encryption algorithm is RSA-OAEP, and the content encryption algorithm is AES GCM. + // +optional + JWE *string `json:"jwe,omitempty" protobuf:"bytes,7,opt,name=jwe"` +} + +type APIKeyConditionType string + +// These are valid conditions of the API Key. +const ( + // APIKeyIssued means the api key is ready for use. + APIKeyIssued APIKeyConditionType = "Issued" + // APIKeyExpired means the key has expired. + APIKeyExpired APIKeyConditionType = "Expired" + // APIKeyRevoked means the api keys has manually been revoked. + APIKeyRevoked APIKeyConditionType = "Revoked" +) diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/conditions.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/conditions.go new file mode 100644 index 00000000..d7e1fda4 --- /dev/null +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/conditions.go @@ -0,0 +1,39 @@ +// Copyright 2025 StreamNative +// +// 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 v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +type ConditionType string + +type ConditionReason string + +const ( + ConditionTypeReady ConditionType = "Ready" + + ConditionReasonReady ConditionReason = "Ready" + ConditionReasonNotReady ConditionReason = "NotReady" +) + +type Condition struct { + Type ConditionType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=ConditionType"` + Status metav1.ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status,casttype=k8s.io/apimachinery/pkg/apis/meta/v1.ConditionStatus"` + Reason ConditionReason `json:"reason,omitempty" protobuf:"bytes,3,opt,name=reason,casttype=ConditionReason"` + Message string `json:"message,omitempty" protobuf:"bytes,4,opt,name=message"` + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,5,opt,name=lastTransitionTime"` + // +optional + // +kubebuilder:validation:Minimum=0 + ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,6,opt,name=observedGeneration"` +} diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go new file mode 100644 index 00000000..9b5e5bed --- /dev/null +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go @@ -0,0 +1,58 @@ +// Copyright 2025 StreamNative +// +// 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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServiceAccount +// +k8s:openapi-gen=true +// +resource:path=serviceaccounts,strategy=ServiceAccountStrategy +// +kubebuilder:categories=all +type ServiceAccount struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + Spec ServiceAccountSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` + Status ServiceAccountStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` +} + +// ServiceAccountSpec defines the desired state of ServiceAccount +type ServiceAccountSpec struct { +} + +// ServiceAccountStatus defines the observed state of ServiceAccount +type ServiceAccountStatus struct { + + // PrivateKeyType indicates the type of private key information + // +optional + PrivateKeyType string `json:"privateKeyType,omitempty" protobuf:"bytes,1,opt,name=privateKeyType"` + + // PrivateKeyData provides the private key data (in base-64 format) for authentication purposes + // +optional + PrivateKeyData string `json:"privateKeyData,omitempty" protobuf:"bytes,2,opt,name=privateKeyData"` + + // Conditions is an array of current observed service account conditions. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,3,rep,name=conditions"` +} diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go new file mode 100644 index 00000000..40aec567 --- /dev/null +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go @@ -0,0 +1,54 @@ +// Copyright 2025 StreamNative +// +// 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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServiceAccountBinding +// +k8s:openapi-gen=true +// +resource:path=serviceaccountbindings,strategy=ServiceAccountBindingStrategy +// +kubebuilder:categories=all +type ServiceAccountBinding struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + Spec ServiceAccountBindingSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` + Status ServiceAccountBindingStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` +} + +// ServiceAccountBindingSpec defines the desired state of ServiceAccountBinding +type ServiceAccountBindingSpec struct { + // refers to the ServiceAccount under the same namespace as this binding object + ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"bytes,4,name=serviceAccountName"` + + // refers to a PoolMember in the current namespace or other namespaces + PoolMemberRef PoolMemberReference `json:"poolMemberRef,omitempty" protobuf:"bytes,5,name=poolMemberRef"` +} + +// ServiceAccountBindingStatus defines the observed state of ServiceAccountBinding +type ServiceAccountBindingStatus struct { + // Conditions is an array of current observed service account binding conditions. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` +} diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.api.register.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.api.register.go index 75976e46..7abd1a85 100644 --- a/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.api.register.go +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.api.register.go @@ -58,8 +58,11 @@ func init() { // Adds the list of known types to Scheme. func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, + &APIKey{}, &Secret{}, &SecretList{}, + &ServiceAccount{}, + &ServiceAccountBinding{}, ) // AddToGroupVersion allows the serialization of client types like ListOptions. v1.AddToGroupVersion(scheme, SchemeGroupVersion) diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.deepcopy.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.deepcopy.go index 1e5d3c56..563bcbb4 100644 --- a/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.deepcopy.go @@ -19,10 +19,111 @@ package v1alpha1 import ( - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIKey) DeepCopyInto(out *APIKey) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIKey. +func (in *APIKey) DeepCopy() *APIKey { + if in == nil { + return nil + } + out := new(APIKey) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *APIKey) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIKeySpec) DeepCopyInto(out *APIKeySpec) { + *out = *in + if in.ExpirationTime != nil { + in, out := &in.ExpirationTime, &out.ExpirationTime + *out = (*in).DeepCopy() + } + if in.EncryptionKey != nil { + in, out := &in.EncryptionKey, &out.EncryptionKey + *out = new(EncryptionKey) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIKeySpec. +func (in *APIKeySpec) DeepCopy() *APIKeySpec { + if in == nil { + return nil + } + out := new(APIKeySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIKeyStatus) DeepCopyInto(out *APIKeyStatus) { + *out = *in + if in.KeyId != nil { + in, out := &in.KeyId, &out.KeyId + *out = new(string) + **out = **in + } + if in.IssuedAt != nil { + in, out := &in.IssuedAt, &out.IssuedAt + *out = (*in).DeepCopy() + } + if in.ExpiresAt != nil { + in, out := &in.ExpiresAt, &out.ExpiresAt + *out = (*in).DeepCopy() + } + if in.Token != nil { + in, out := &in.Token, &out.Token + *out = new(string) + **out = **in + } + if in.EncryptedToken != nil { + in, out := &in.EncryptedToken, &out.EncryptedToken + *out = new(EncryptedToken) + (*in).DeepCopyInto(*out) + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RevokedAt != nil { + in, out := &in.RevokedAt, &out.RevokedAt + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIKeyStatus. +func (in *APIKeyStatus) DeepCopy() *APIKeyStatus { + if in == nil { + return nil + } + out := new(APIKeyStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AWSCloudConnection) DeepCopyInto(out *AWSCloudConnection) { *out = *in @@ -103,6 +204,22 @@ func (in *BookKeeperSetReference) DeepCopy() *BookKeeperSetReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Config) DeepCopyInto(out *Config) { *out = *in @@ -190,6 +307,41 @@ func (in *DomainTLS) DeepCopy() *DomainTLS { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EncryptedToken) DeepCopyInto(out *EncryptedToken) { + *out = *in + if in.JWE != nil { + in, out := &in.JWE, &out.JWE + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EncryptedToken. +func (in *EncryptedToken) DeepCopy() *EncryptedToken { + if in == nil { + return nil + } + out := new(EncryptedToken) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EncryptionKey) DeepCopyInto(out *EncryptionKey) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EncryptionKey. +func (in *EncryptionKey) DeepCopy() *EncryptionKey { + if in == nil { + return nil + } + out := new(EncryptionKey) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GCPCloudConnection) DeepCopyInto(out *GCPCloudConnection) { *out = *in @@ -361,7 +513,7 @@ func (in *Secret) DeepCopyInto(out *Secret) { } if in.Type != nil { in, out := &in.Type, &out.Type - *out = new(v1.SecretType) + *out = new(corev1.SecretType) **out = **in } } @@ -461,6 +613,135 @@ func (in *SecretStatus) DeepCopy() *SecretStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccount) DeepCopyInto(out *ServiceAccount) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccount. +func (in *ServiceAccount) DeepCopy() *ServiceAccount { + if in == nil { + return nil + } + out := new(ServiceAccount) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceAccount) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountBinding) DeepCopyInto(out *ServiceAccountBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountBinding. +func (in *ServiceAccountBinding) DeepCopy() *ServiceAccountBinding { + if in == nil { + return nil + } + out := new(ServiceAccountBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceAccountBinding) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountBindingSpec) DeepCopyInto(out *ServiceAccountBindingSpec) { + *out = *in + out.PoolMemberRef = in.PoolMemberRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountBindingSpec. +func (in *ServiceAccountBindingSpec) DeepCopy() *ServiceAccountBindingSpec { + if in == nil { + return nil + } + out := new(ServiceAccountBindingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountBindingStatus) DeepCopyInto(out *ServiceAccountBindingStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountBindingStatus. +func (in *ServiceAccountBindingStatus) DeepCopy() *ServiceAccountBindingStatus { + if in == nil { + return nil + } + out := new(ServiceAccountBindingStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountSpec) DeepCopyInto(out *ServiceAccountSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountSpec. +func (in *ServiceAccountSpec) DeepCopy() *ServiceAccountSpec { + if in == nil { + return nil + } + out := new(ServiceAccountSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountStatus) DeepCopyInto(out *ServiceAccountStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountStatus. +func (in *ServiceAccountStatus) DeepCopy() *ServiceAccountStatus { + if in == nil { + return nil + } + out := new(ServiceAccountStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SharingConfig) DeepCopyInto(out *SharingConfig) { *out = *in diff --git a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/apikey.go b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/apikey.go new file mode 100644 index 00000000..a88f4544 --- /dev/null +++ b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/apikey.go @@ -0,0 +1,192 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + scheme "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/clientset_generated/clientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// APIKeysGetter has a method to return a APIKeyInterface. +// A group's client should implement this interface. +type APIKeysGetter interface { + APIKeys(namespace string) APIKeyInterface +} + +// APIKeyInterface has methods to work with APIKey resources. +type APIKeyInterface interface { + Create(ctx context.Context, aPIKey *v1alpha1.APIKey, opts v1.CreateOptions) (*v1alpha1.APIKey, error) + Update(ctx context.Context, aPIKey *v1alpha1.APIKey, opts v1.UpdateOptions) (*v1alpha1.APIKey, error) + UpdateStatus(ctx context.Context, aPIKey *v1alpha1.APIKey, opts v1.UpdateOptions) (*v1alpha1.APIKey, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.APIKey, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.APIKeyList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.APIKey, err error) + APIKeyExpansion +} + +// aPIKeys implements APIKeyInterface +type aPIKeys struct { + client rest.Interface + ns string +} + +// newAPIKeys returns a APIKeys +func newAPIKeys(c *CloudV1alpha1Client, namespace string) *aPIKeys { + return &aPIKeys{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the aPIKey, and returns the corresponding aPIKey object, and an error if there is any. +func (c *aPIKeys) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.APIKey, err error) { + result = &v1alpha1.APIKey{} + err = c.client.Get(). + Namespace(c.ns). + Resource("apikeys"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of APIKeys that match those selectors. +func (c *aPIKeys) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.APIKeyList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.APIKeyList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("apikeys"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested aPIKeys. +func (c *aPIKeys) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("apikeys"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a aPIKey and creates it. Returns the server's representation of the aPIKey, and an error, if there is any. +func (c *aPIKeys) Create(ctx context.Context, aPIKey *v1alpha1.APIKey, opts v1.CreateOptions) (result *v1alpha1.APIKey, err error) { + result = &v1alpha1.APIKey{} + err = c.client.Post(). + Namespace(c.ns). + Resource("apikeys"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(aPIKey). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a aPIKey and updates it. Returns the server's representation of the aPIKey, and an error, if there is any. +func (c *aPIKeys) Update(ctx context.Context, aPIKey *v1alpha1.APIKey, opts v1.UpdateOptions) (result *v1alpha1.APIKey, err error) { + result = &v1alpha1.APIKey{} + err = c.client.Put(). + Namespace(c.ns). + Resource("apikeys"). + Name(aPIKey.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(aPIKey). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *aPIKeys) UpdateStatus(ctx context.Context, aPIKey *v1alpha1.APIKey, opts v1.UpdateOptions) (result *v1alpha1.APIKey, err error) { + result = &v1alpha1.APIKey{} + err = c.client.Put(). + Namespace(c.ns). + Resource("apikeys"). + Name(aPIKey.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(aPIKey). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the aPIKey and deletes it. Returns an error if one occurs. +func (c *aPIKeys) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("apikeys"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *aPIKeys) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("apikeys"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched aPIKey. +func (c *aPIKeys) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.APIKey, err error) { + result = &v1alpha1.APIKey{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("apikeys"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/cloud_client.go b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/cloud_client.go index f953b15b..45f785c8 100644 --- a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/cloud_client.go +++ b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/cloud_client.go @@ -25,7 +25,10 @@ import ( type CloudV1alpha1Interface interface { RESTClient() rest.Interface + APIKeysGetter SecretsGetter + ServiceAccountsGetter + ServiceAccountBindingsGetter } // CloudV1alpha1Client is used to interact with features provided by the cloud.streamnative.io group. @@ -33,10 +36,22 @@ type CloudV1alpha1Client struct { restClient rest.Interface } +func (c *CloudV1alpha1Client) APIKeys(namespace string) APIKeyInterface { + return newAPIKeys(c, namespace) +} + func (c *CloudV1alpha1Client) Secrets(namespace string) SecretInterface { return newSecrets(c, namespace) } +func (c *CloudV1alpha1Client) ServiceAccounts(namespace string) ServiceAccountInterface { + return newServiceAccounts(c, namespace) +} + +func (c *CloudV1alpha1Client) ServiceAccountBindings(namespace string) ServiceAccountBindingInterface { + return newServiceAccountBindings(c, namespace) +} + // NewForConfig creates a new CloudV1alpha1Client for the given config. // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), // where httpClient was generated with rest.HTTPClientFor(c). diff --git a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_apikey.go b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_apikey.go new file mode 100644 index 00000000..8cdc603f --- /dev/null +++ b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_apikey.go @@ -0,0 +1,138 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeAPIKeys implements APIKeyInterface +type FakeAPIKeys struct { + Fake *FakeCloudV1alpha1 + ns string +} + +var apikeysResource = v1alpha1.SchemeGroupVersion.WithResource("apikeys") + +var apikeysKind = v1alpha1.SchemeGroupVersion.WithKind("APIKey") + +// Get takes name of the aPIKey, and returns the corresponding aPIKey object, and an error if there is any. +func (c *FakeAPIKeys) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.APIKey, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(apikeysResource, c.ns, name), &v1alpha1.APIKey{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.APIKey), err +} + +// List takes label and field selectors, and returns the list of APIKeys that match those selectors. +func (c *FakeAPIKeys) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.APIKeyList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(apikeysResource, apikeysKind, c.ns, opts), &v1alpha1.APIKeyList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.APIKeyList{ListMeta: obj.(*v1alpha1.APIKeyList).ListMeta} + for _, item := range obj.(*v1alpha1.APIKeyList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested aPIKeys. +func (c *FakeAPIKeys) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(apikeysResource, c.ns, opts)) + +} + +// Create takes the representation of a aPIKey and creates it. Returns the server's representation of the aPIKey, and an error, if there is any. +func (c *FakeAPIKeys) Create(ctx context.Context, aPIKey *v1alpha1.APIKey, opts v1.CreateOptions) (result *v1alpha1.APIKey, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(apikeysResource, c.ns, aPIKey), &v1alpha1.APIKey{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.APIKey), err +} + +// Update takes the representation of a aPIKey and updates it. Returns the server's representation of the aPIKey, and an error, if there is any. +func (c *FakeAPIKeys) Update(ctx context.Context, aPIKey *v1alpha1.APIKey, opts v1.UpdateOptions) (result *v1alpha1.APIKey, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(apikeysResource, c.ns, aPIKey), &v1alpha1.APIKey{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.APIKey), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeAPIKeys) UpdateStatus(ctx context.Context, aPIKey *v1alpha1.APIKey, opts v1.UpdateOptions) (*v1alpha1.APIKey, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(apikeysResource, "status", c.ns, aPIKey), &v1alpha1.APIKey{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.APIKey), err +} + +// Delete takes name of the aPIKey and deletes it. Returns an error if one occurs. +func (c *FakeAPIKeys) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(apikeysResource, c.ns, name, opts), &v1alpha1.APIKey{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeAPIKeys) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(apikeysResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.APIKeyList{}) + return err +} + +// Patch applies the patch and returns the patched aPIKey. +func (c *FakeAPIKeys) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.APIKey, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(apikeysResource, c.ns, name, pt, data, subresources...), &v1alpha1.APIKey{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.APIKey), err +} diff --git a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_cloud_client.go b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_cloud_client.go index f2d6d939..3a026341 100644 --- a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_cloud_client.go +++ b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_cloud_client.go @@ -25,10 +25,22 @@ type FakeCloudV1alpha1 struct { *testing.Fake } +func (c *FakeCloudV1alpha1) APIKeys(namespace string) v1alpha1.APIKeyInterface { + return &FakeAPIKeys{c, namespace} +} + func (c *FakeCloudV1alpha1) Secrets(namespace string) v1alpha1.SecretInterface { return &FakeSecrets{c, namespace} } +func (c *FakeCloudV1alpha1) ServiceAccounts(namespace string) v1alpha1.ServiceAccountInterface { + return &FakeServiceAccounts{c, namespace} +} + +func (c *FakeCloudV1alpha1) ServiceAccountBindings(namespace string) v1alpha1.ServiceAccountBindingInterface { + return &FakeServiceAccountBindings{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeCloudV1alpha1) RESTClient() rest.Interface { diff --git a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_serviceaccount.go b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_serviceaccount.go new file mode 100644 index 00000000..ec2000f8 --- /dev/null +++ b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_serviceaccount.go @@ -0,0 +1,138 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeServiceAccounts implements ServiceAccountInterface +type FakeServiceAccounts struct { + Fake *FakeCloudV1alpha1 + ns string +} + +var serviceaccountsResource = v1alpha1.SchemeGroupVersion.WithResource("serviceaccounts") + +var serviceaccountsKind = v1alpha1.SchemeGroupVersion.WithKind("ServiceAccount") + +// Get takes name of the serviceAccount, and returns the corresponding serviceAccount object, and an error if there is any. +func (c *FakeServiceAccounts) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServiceAccount, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(serviceaccountsResource, c.ns, name), &v1alpha1.ServiceAccount{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceAccount), err +} + +// List takes label and field selectors, and returns the list of ServiceAccounts that match those selectors. +func (c *FakeServiceAccounts) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServiceAccountList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(serviceaccountsResource, serviceaccountsKind, c.ns, opts), &v1alpha1.ServiceAccountList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ServiceAccountList{ListMeta: obj.(*v1alpha1.ServiceAccountList).ListMeta} + for _, item := range obj.(*v1alpha1.ServiceAccountList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested serviceAccounts. +func (c *FakeServiceAccounts) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(serviceaccountsResource, c.ns, opts)) + +} + +// Create takes the representation of a serviceAccount and creates it. Returns the server's representation of the serviceAccount, and an error, if there is any. +func (c *FakeServiceAccounts) Create(ctx context.Context, serviceAccount *v1alpha1.ServiceAccount, opts v1.CreateOptions) (result *v1alpha1.ServiceAccount, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(serviceaccountsResource, c.ns, serviceAccount), &v1alpha1.ServiceAccount{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceAccount), err +} + +// Update takes the representation of a serviceAccount and updates it. Returns the server's representation of the serviceAccount, and an error, if there is any. +func (c *FakeServiceAccounts) Update(ctx context.Context, serviceAccount *v1alpha1.ServiceAccount, opts v1.UpdateOptions) (result *v1alpha1.ServiceAccount, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(serviceaccountsResource, c.ns, serviceAccount), &v1alpha1.ServiceAccount{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceAccount), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeServiceAccounts) UpdateStatus(ctx context.Context, serviceAccount *v1alpha1.ServiceAccount, opts v1.UpdateOptions) (*v1alpha1.ServiceAccount, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(serviceaccountsResource, "status", c.ns, serviceAccount), &v1alpha1.ServiceAccount{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceAccount), err +} + +// Delete takes name of the serviceAccount and deletes it. Returns an error if one occurs. +func (c *FakeServiceAccounts) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(serviceaccountsResource, c.ns, name, opts), &v1alpha1.ServiceAccount{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeServiceAccounts) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(serviceaccountsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.ServiceAccountList{}) + return err +} + +// Patch applies the patch and returns the patched serviceAccount. +func (c *FakeServiceAccounts) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServiceAccount, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(serviceaccountsResource, c.ns, name, pt, data, subresources...), &v1alpha1.ServiceAccount{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceAccount), err +} diff --git a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_serviceaccountbinding.go b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_serviceaccountbinding.go new file mode 100644 index 00000000..9ed89d93 --- /dev/null +++ b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/fake/fake_serviceaccountbinding.go @@ -0,0 +1,138 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeServiceAccountBindings implements ServiceAccountBindingInterface +type FakeServiceAccountBindings struct { + Fake *FakeCloudV1alpha1 + ns string +} + +var serviceaccountbindingsResource = v1alpha1.SchemeGroupVersion.WithResource("serviceaccountbindings") + +var serviceaccountbindingsKind = v1alpha1.SchemeGroupVersion.WithKind("ServiceAccountBinding") + +// Get takes name of the serviceAccountBinding, and returns the corresponding serviceAccountBinding object, and an error if there is any. +func (c *FakeServiceAccountBindings) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServiceAccountBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(serviceaccountbindingsResource, c.ns, name), &v1alpha1.ServiceAccountBinding{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceAccountBinding), err +} + +// List takes label and field selectors, and returns the list of ServiceAccountBindings that match those selectors. +func (c *FakeServiceAccountBindings) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServiceAccountBindingList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(serviceaccountbindingsResource, serviceaccountbindingsKind, c.ns, opts), &v1alpha1.ServiceAccountBindingList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ServiceAccountBindingList{ListMeta: obj.(*v1alpha1.ServiceAccountBindingList).ListMeta} + for _, item := range obj.(*v1alpha1.ServiceAccountBindingList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested serviceAccountBindings. +func (c *FakeServiceAccountBindings) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(serviceaccountbindingsResource, c.ns, opts)) + +} + +// Create takes the representation of a serviceAccountBinding and creates it. Returns the server's representation of the serviceAccountBinding, and an error, if there is any. +func (c *FakeServiceAccountBindings) Create(ctx context.Context, serviceAccountBinding *v1alpha1.ServiceAccountBinding, opts v1.CreateOptions) (result *v1alpha1.ServiceAccountBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(serviceaccountbindingsResource, c.ns, serviceAccountBinding), &v1alpha1.ServiceAccountBinding{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceAccountBinding), err +} + +// Update takes the representation of a serviceAccountBinding and updates it. Returns the server's representation of the serviceAccountBinding, and an error, if there is any. +func (c *FakeServiceAccountBindings) Update(ctx context.Context, serviceAccountBinding *v1alpha1.ServiceAccountBinding, opts v1.UpdateOptions) (result *v1alpha1.ServiceAccountBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(serviceaccountbindingsResource, c.ns, serviceAccountBinding), &v1alpha1.ServiceAccountBinding{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceAccountBinding), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeServiceAccountBindings) UpdateStatus(ctx context.Context, serviceAccountBinding *v1alpha1.ServiceAccountBinding, opts v1.UpdateOptions) (*v1alpha1.ServiceAccountBinding, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(serviceaccountbindingsResource, "status", c.ns, serviceAccountBinding), &v1alpha1.ServiceAccountBinding{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceAccountBinding), err +} + +// Delete takes name of the serviceAccountBinding and deletes it. Returns an error if one occurs. +func (c *FakeServiceAccountBindings) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(serviceaccountbindingsResource, c.ns, name, opts), &v1alpha1.ServiceAccountBinding{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeServiceAccountBindings) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(serviceaccountbindingsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.ServiceAccountBindingList{}) + return err +} + +// Patch applies the patch and returns the patched serviceAccountBinding. +func (c *FakeServiceAccountBindings) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServiceAccountBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(serviceaccountbindingsResource, c.ns, name, pt, data, subresources...), &v1alpha1.ServiceAccountBinding{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceAccountBinding), err +} diff --git a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/generated_expansion.go b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/generated_expansion.go index 68f6a03a..acaf4095 100644 --- a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/generated_expansion.go +++ b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/generated_expansion.go @@ -15,4 +15,10 @@ package v1alpha1 +type APIKeyExpansion interface{} + type SecretExpansion interface{} + +type ServiceAccountExpansion interface{} + +type ServiceAccountBindingExpansion interface{} diff --git a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/serviceaccount.go b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/serviceaccount.go new file mode 100644 index 00000000..6358b684 --- /dev/null +++ b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/serviceaccount.go @@ -0,0 +1,192 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + scheme "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/clientset_generated/clientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ServiceAccountsGetter has a method to return a ServiceAccountInterface. +// A group's client should implement this interface. +type ServiceAccountsGetter interface { + ServiceAccounts(namespace string) ServiceAccountInterface +} + +// ServiceAccountInterface has methods to work with ServiceAccount resources. +type ServiceAccountInterface interface { + Create(ctx context.Context, serviceAccount *v1alpha1.ServiceAccount, opts v1.CreateOptions) (*v1alpha1.ServiceAccount, error) + Update(ctx context.Context, serviceAccount *v1alpha1.ServiceAccount, opts v1.UpdateOptions) (*v1alpha1.ServiceAccount, error) + UpdateStatus(ctx context.Context, serviceAccount *v1alpha1.ServiceAccount, opts v1.UpdateOptions) (*v1alpha1.ServiceAccount, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ServiceAccount, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ServiceAccountList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServiceAccount, err error) + ServiceAccountExpansion +} + +// serviceAccounts implements ServiceAccountInterface +type serviceAccounts struct { + client rest.Interface + ns string +} + +// newServiceAccounts returns a ServiceAccounts +func newServiceAccounts(c *CloudV1alpha1Client, namespace string) *serviceAccounts { + return &serviceAccounts{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the serviceAccount, and returns the corresponding serviceAccount object, and an error if there is any. +func (c *serviceAccounts) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServiceAccount, err error) { + result = &v1alpha1.ServiceAccount{} + err = c.client.Get(). + Namespace(c.ns). + Resource("serviceaccounts"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ServiceAccounts that match those selectors. +func (c *serviceAccounts) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServiceAccountList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ServiceAccountList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("serviceaccounts"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested serviceAccounts. +func (c *serviceAccounts) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("serviceaccounts"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a serviceAccount and creates it. Returns the server's representation of the serviceAccount, and an error, if there is any. +func (c *serviceAccounts) Create(ctx context.Context, serviceAccount *v1alpha1.ServiceAccount, opts v1.CreateOptions) (result *v1alpha1.ServiceAccount, err error) { + result = &v1alpha1.ServiceAccount{} + err = c.client.Post(). + Namespace(c.ns). + Resource("serviceaccounts"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(serviceAccount). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a serviceAccount and updates it. Returns the server's representation of the serviceAccount, and an error, if there is any. +func (c *serviceAccounts) Update(ctx context.Context, serviceAccount *v1alpha1.ServiceAccount, opts v1.UpdateOptions) (result *v1alpha1.ServiceAccount, err error) { + result = &v1alpha1.ServiceAccount{} + err = c.client.Put(). + Namespace(c.ns). + Resource("serviceaccounts"). + Name(serviceAccount.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(serviceAccount). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *serviceAccounts) UpdateStatus(ctx context.Context, serviceAccount *v1alpha1.ServiceAccount, opts v1.UpdateOptions) (result *v1alpha1.ServiceAccount, err error) { + result = &v1alpha1.ServiceAccount{} + err = c.client.Put(). + Namespace(c.ns). + Resource("serviceaccounts"). + Name(serviceAccount.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(serviceAccount). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the serviceAccount and deletes it. Returns an error if one occurs. +func (c *serviceAccounts) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("serviceaccounts"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *serviceAccounts) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("serviceaccounts"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched serviceAccount. +func (c *serviceAccounts) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServiceAccount, err error) { + result = &v1alpha1.ServiceAccount{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("serviceaccounts"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/serviceaccountbinding.go b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/serviceaccountbinding.go new file mode 100644 index 00000000..632d30cd --- /dev/null +++ b/pkg/streamnativecloud/client/clientset_generated/clientset/typed/cloud/v1alpha1/serviceaccountbinding.go @@ -0,0 +1,192 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + scheme "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/clientset_generated/clientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ServiceAccountBindingsGetter has a method to return a ServiceAccountBindingInterface. +// A group's client should implement this interface. +type ServiceAccountBindingsGetter interface { + ServiceAccountBindings(namespace string) ServiceAccountBindingInterface +} + +// ServiceAccountBindingInterface has methods to work with ServiceAccountBinding resources. +type ServiceAccountBindingInterface interface { + Create(ctx context.Context, serviceAccountBinding *v1alpha1.ServiceAccountBinding, opts v1.CreateOptions) (*v1alpha1.ServiceAccountBinding, error) + Update(ctx context.Context, serviceAccountBinding *v1alpha1.ServiceAccountBinding, opts v1.UpdateOptions) (*v1alpha1.ServiceAccountBinding, error) + UpdateStatus(ctx context.Context, serviceAccountBinding *v1alpha1.ServiceAccountBinding, opts v1.UpdateOptions) (*v1alpha1.ServiceAccountBinding, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ServiceAccountBinding, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ServiceAccountBindingList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServiceAccountBinding, err error) + ServiceAccountBindingExpansion +} + +// serviceAccountBindings implements ServiceAccountBindingInterface +type serviceAccountBindings struct { + client rest.Interface + ns string +} + +// newServiceAccountBindings returns a ServiceAccountBindings +func newServiceAccountBindings(c *CloudV1alpha1Client, namespace string) *serviceAccountBindings { + return &serviceAccountBindings{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the serviceAccountBinding, and returns the corresponding serviceAccountBinding object, and an error if there is any. +func (c *serviceAccountBindings) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServiceAccountBinding, err error) { + result = &v1alpha1.ServiceAccountBinding{} + err = c.client.Get(). + Namespace(c.ns). + Resource("serviceaccountbindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ServiceAccountBindings that match those selectors. +func (c *serviceAccountBindings) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServiceAccountBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ServiceAccountBindingList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("serviceaccountbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested serviceAccountBindings. +func (c *serviceAccountBindings) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("serviceaccountbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a serviceAccountBinding and creates it. Returns the server's representation of the serviceAccountBinding, and an error, if there is any. +func (c *serviceAccountBindings) Create(ctx context.Context, serviceAccountBinding *v1alpha1.ServiceAccountBinding, opts v1.CreateOptions) (result *v1alpha1.ServiceAccountBinding, err error) { + result = &v1alpha1.ServiceAccountBinding{} + err = c.client.Post(). + Namespace(c.ns). + Resource("serviceaccountbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(serviceAccountBinding). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a serviceAccountBinding and updates it. Returns the server's representation of the serviceAccountBinding, and an error, if there is any. +func (c *serviceAccountBindings) Update(ctx context.Context, serviceAccountBinding *v1alpha1.ServiceAccountBinding, opts v1.UpdateOptions) (result *v1alpha1.ServiceAccountBinding, err error) { + result = &v1alpha1.ServiceAccountBinding{} + err = c.client.Put(). + Namespace(c.ns). + Resource("serviceaccountbindings"). + Name(serviceAccountBinding.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(serviceAccountBinding). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *serviceAccountBindings) UpdateStatus(ctx context.Context, serviceAccountBinding *v1alpha1.ServiceAccountBinding, opts v1.UpdateOptions) (result *v1alpha1.ServiceAccountBinding, err error) { + result = &v1alpha1.ServiceAccountBinding{} + err = c.client.Put(). + Namespace(c.ns). + Resource("serviceaccountbindings"). + Name(serviceAccountBinding.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(serviceAccountBinding). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the serviceAccountBinding and deletes it. Returns an error if one occurs. +func (c *serviceAccountBindings) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("serviceaccountbindings"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *serviceAccountBindings) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("serviceaccountbindings"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched serviceAccountBinding. +func (c *serviceAccountBindings) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServiceAccountBinding, err error) { + result = &v1alpha1.ServiceAccountBinding{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("serviceaccountbindings"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/apikey.go b/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/apikey.go new file mode 100644 index 00000000..c196bc53 --- /dev/null +++ b/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/apikey.go @@ -0,0 +1,87 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + cloudv1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + clientset "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/clientset_generated/clientset" + internalinterfaces "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/informers_generated/externalversions/internalinterfaces" + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// APIKeyInformer provides access to a shared informer and lister for +// APIKeys. +type APIKeyInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.APIKeyLister +} + +type aPIKeyInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewAPIKeyInformer constructs a new informer for APIKey type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewAPIKeyInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredAPIKeyInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredAPIKeyInformer constructs a new informer for APIKey type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredAPIKeyInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.CloudV1alpha1().APIKeys(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.CloudV1alpha1().APIKeys(namespace).Watch(context.TODO(), options) + }, + }, + &cloudv1alpha1.APIKey{}, + resyncPeriod, + indexers, + ) +} + +func (f *aPIKeyInformer) defaultInformer(client clientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredAPIKeyInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *aPIKeyInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&cloudv1alpha1.APIKey{}, f.defaultInformer) +} + +func (f *aPIKeyInformer) Lister() v1alpha1.APIKeyLister { + return v1alpha1.NewAPIKeyLister(f.Informer().GetIndexer()) +} diff --git a/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/interface.go b/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/interface.go index 2ac99eb9..e574d9e6 100644 --- a/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/interface.go +++ b/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/interface.go @@ -21,8 +21,14 @@ import ( // Interface provides access to all the informers in this group version. type Interface interface { + // APIKeys returns a APIKeyInformer. + APIKeys() APIKeyInformer // Secrets returns a SecretInformer. Secrets() SecretInformer + // ServiceAccounts returns a ServiceAccountInformer. + ServiceAccounts() ServiceAccountInformer + // ServiceAccountBindings returns a ServiceAccountBindingInformer. + ServiceAccountBindings() ServiceAccountBindingInformer } type version struct { @@ -36,7 +42,22 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} } +// APIKeys returns a APIKeyInformer. +func (v *version) APIKeys() APIKeyInformer { + return &aPIKeyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // Secrets returns a SecretInformer. func (v *version) Secrets() SecretInformer { return &secretInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// ServiceAccounts returns a ServiceAccountInformer. +func (v *version) ServiceAccounts() ServiceAccountInformer { + return &serviceAccountInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// ServiceAccountBindings returns a ServiceAccountBindingInformer. +func (v *version) ServiceAccountBindings() ServiceAccountBindingInformer { + return &serviceAccountBindingInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/serviceaccount.go b/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/serviceaccount.go new file mode 100644 index 00000000..d0138e38 --- /dev/null +++ b/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/serviceaccount.go @@ -0,0 +1,87 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + cloudv1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + clientset "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/clientset_generated/clientset" + internalinterfaces "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/informers_generated/externalversions/internalinterfaces" + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ServiceAccountInformer provides access to a shared informer and lister for +// ServiceAccounts. +type ServiceAccountInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ServiceAccountLister +} + +type serviceAccountInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewServiceAccountInformer constructs a new informer for ServiceAccount type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewServiceAccountInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredServiceAccountInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredServiceAccountInformer constructs a new informer for ServiceAccount type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredServiceAccountInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.CloudV1alpha1().ServiceAccounts(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.CloudV1alpha1().ServiceAccounts(namespace).Watch(context.TODO(), options) + }, + }, + &cloudv1alpha1.ServiceAccount{}, + resyncPeriod, + indexers, + ) +} + +func (f *serviceAccountInformer) defaultInformer(client clientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredServiceAccountInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *serviceAccountInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&cloudv1alpha1.ServiceAccount{}, f.defaultInformer) +} + +func (f *serviceAccountInformer) Lister() v1alpha1.ServiceAccountLister { + return v1alpha1.NewServiceAccountLister(f.Informer().GetIndexer()) +} diff --git a/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/serviceaccountbinding.go b/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/serviceaccountbinding.go new file mode 100644 index 00000000..a2d005c0 --- /dev/null +++ b/pkg/streamnativecloud/client/informers_generated/externalversions/cloud/v1alpha1/serviceaccountbinding.go @@ -0,0 +1,87 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + cloudv1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + clientset "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/clientset_generated/clientset" + internalinterfaces "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/informers_generated/externalversions/internalinterfaces" + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ServiceAccountBindingInformer provides access to a shared informer and lister for +// ServiceAccountBindings. +type ServiceAccountBindingInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ServiceAccountBindingLister +} + +type serviceAccountBindingInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewServiceAccountBindingInformer constructs a new informer for ServiceAccountBinding type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewServiceAccountBindingInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredServiceAccountBindingInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredServiceAccountBindingInformer constructs a new informer for ServiceAccountBinding type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredServiceAccountBindingInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.CloudV1alpha1().ServiceAccountBindings(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.CloudV1alpha1().ServiceAccountBindings(namespace).Watch(context.TODO(), options) + }, + }, + &cloudv1alpha1.ServiceAccountBinding{}, + resyncPeriod, + indexers, + ) +} + +func (f *serviceAccountBindingInformer) defaultInformer(client clientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredServiceAccountBindingInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *serviceAccountBindingInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&cloudv1alpha1.ServiceAccountBinding{}, f.defaultInformer) +} + +func (f *serviceAccountBindingInformer) Lister() v1alpha1.ServiceAccountBindingLister { + return v1alpha1.NewServiceAccountBindingLister(f.Informer().GetIndexer()) +} diff --git a/pkg/streamnativecloud/client/informers_generated/externalversions/generic.go b/pkg/streamnativecloud/client/informers_generated/externalversions/generic.go index 524edf2f..2f2d23ee 100644 --- a/pkg/streamnativecloud/client/informers_generated/externalversions/generic.go +++ b/pkg/streamnativecloud/client/informers_generated/externalversions/generic.go @@ -51,8 +51,14 @@ func (f *genericInformer) Lister() cache.GenericLister { func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { // Group=cloud.streamnative.io, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("apikeys"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Cloud().V1alpha1().APIKeys().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("secrets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Cloud().V1alpha1().Secrets().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("serviceaccounts"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Cloud().V1alpha1().ServiceAccounts().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("serviceaccountbindings"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Cloud().V1alpha1().ServiceAccountBindings().Informer()}, nil // Group=compute.streamnative.io, Version=v1alpha1 case computev1alpha1.SchemeGroupVersion.WithResource("flinkdeployments"): diff --git a/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/apikey.go b/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/apikey.go new file mode 100644 index 00000000..19c5ab9f --- /dev/null +++ b/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/apikey.go @@ -0,0 +1,96 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// APIKeyLister helps list APIKeys. +// All objects returned here must be treated as read-only. +type APIKeyLister interface { + // List lists all APIKeys in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.APIKey, err error) + // APIKeys returns an object that can list and get APIKeys. + APIKeys(namespace string) APIKeyNamespaceLister + APIKeyListerExpansion +} + +// aPIKeyLister implements the APIKeyLister interface. +type aPIKeyLister struct { + indexer cache.Indexer +} + +// NewAPIKeyLister returns a new APIKeyLister. +func NewAPIKeyLister(indexer cache.Indexer) APIKeyLister { + return &aPIKeyLister{indexer: indexer} +} + +// List lists all APIKeys in the indexer. +func (s *aPIKeyLister) List(selector labels.Selector) (ret []*v1alpha1.APIKey, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.APIKey)) + }) + return ret, err +} + +// APIKeys returns an object that can list and get APIKeys. +func (s *aPIKeyLister) APIKeys(namespace string) APIKeyNamespaceLister { + return aPIKeyNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// APIKeyNamespaceLister helps list and get APIKeys. +// All objects returned here must be treated as read-only. +type APIKeyNamespaceLister interface { + // List lists all APIKeys in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.APIKey, err error) + // Get retrieves the APIKey from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.APIKey, error) + APIKeyNamespaceListerExpansion +} + +// aPIKeyNamespaceLister implements the APIKeyNamespaceLister +// interface. +type aPIKeyNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all APIKeys in the indexer for a given namespace. +func (s aPIKeyNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.APIKey, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.APIKey)) + }) + return ret, err +} + +// Get retrieves the APIKey from the indexer for a given namespace and name. +func (s aPIKeyNamespaceLister) Get(name string) (*v1alpha1.APIKey, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("apikey"), name) + } + return obj.(*v1alpha1.APIKey), nil +} diff --git a/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/expansion_generated.go b/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/expansion_generated.go index 4aa27ae8..27d2fded 100644 --- a/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/expansion_generated.go +++ b/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/expansion_generated.go @@ -15,6 +15,14 @@ package v1alpha1 +// APIKeyListerExpansion allows custom methods to be added to +// APIKeyLister. +type APIKeyListerExpansion interface{} + +// APIKeyNamespaceListerExpansion allows custom methods to be added to +// APIKeyNamespaceLister. +type APIKeyNamespaceListerExpansion interface{} + // SecretListerExpansion allows custom methods to be added to // SecretLister. type SecretListerExpansion interface{} @@ -22,3 +30,19 @@ type SecretListerExpansion interface{} // SecretNamespaceListerExpansion allows custom methods to be added to // SecretNamespaceLister. type SecretNamespaceListerExpansion interface{} + +// ServiceAccountListerExpansion allows custom methods to be added to +// ServiceAccountLister. +type ServiceAccountListerExpansion interface{} + +// ServiceAccountNamespaceListerExpansion allows custom methods to be added to +// ServiceAccountNamespaceLister. +type ServiceAccountNamespaceListerExpansion interface{} + +// ServiceAccountBindingListerExpansion allows custom methods to be added to +// ServiceAccountBindingLister. +type ServiceAccountBindingListerExpansion interface{} + +// ServiceAccountBindingNamespaceListerExpansion allows custom methods to be added to +// ServiceAccountBindingNamespaceLister. +type ServiceAccountBindingNamespaceListerExpansion interface{} diff --git a/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/serviceaccount.go b/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/serviceaccount.go new file mode 100644 index 00000000..0e177e0f --- /dev/null +++ b/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/serviceaccount.go @@ -0,0 +1,96 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ServiceAccountLister helps list ServiceAccounts. +// All objects returned here must be treated as read-only. +type ServiceAccountLister interface { + // List lists all ServiceAccounts in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ServiceAccount, err error) + // ServiceAccounts returns an object that can list and get ServiceAccounts. + ServiceAccounts(namespace string) ServiceAccountNamespaceLister + ServiceAccountListerExpansion +} + +// serviceAccountLister implements the ServiceAccountLister interface. +type serviceAccountLister struct { + indexer cache.Indexer +} + +// NewServiceAccountLister returns a new ServiceAccountLister. +func NewServiceAccountLister(indexer cache.Indexer) ServiceAccountLister { + return &serviceAccountLister{indexer: indexer} +} + +// List lists all ServiceAccounts in the indexer. +func (s *serviceAccountLister) List(selector labels.Selector) (ret []*v1alpha1.ServiceAccount, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ServiceAccount)) + }) + return ret, err +} + +// ServiceAccounts returns an object that can list and get ServiceAccounts. +func (s *serviceAccountLister) ServiceAccounts(namespace string) ServiceAccountNamespaceLister { + return serviceAccountNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ServiceAccountNamespaceLister helps list and get ServiceAccounts. +// All objects returned here must be treated as read-only. +type ServiceAccountNamespaceLister interface { + // List lists all ServiceAccounts in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ServiceAccount, err error) + // Get retrieves the ServiceAccount from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.ServiceAccount, error) + ServiceAccountNamespaceListerExpansion +} + +// serviceAccountNamespaceLister implements the ServiceAccountNamespaceLister +// interface. +type serviceAccountNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all ServiceAccounts in the indexer for a given namespace. +func (s serviceAccountNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ServiceAccount, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ServiceAccount)) + }) + return ret, err +} + +// Get retrieves the ServiceAccount from the indexer for a given namespace and name. +func (s serviceAccountNamespaceLister) Get(name string) (*v1alpha1.ServiceAccount, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("serviceaccount"), name) + } + return obj.(*v1alpha1.ServiceAccount), nil +} diff --git a/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/serviceaccountbinding.go b/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/serviceaccountbinding.go new file mode 100644 index 00000000..9af76224 --- /dev/null +++ b/pkg/streamnativecloud/client/listers_generated/cloud/v1alpha1/serviceaccountbinding.go @@ -0,0 +1,96 @@ +// Copyright 2025 StreamNative +// +// 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. +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ServiceAccountBindingLister helps list ServiceAccountBindings. +// All objects returned here must be treated as read-only. +type ServiceAccountBindingLister interface { + // List lists all ServiceAccountBindings in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ServiceAccountBinding, err error) + // ServiceAccountBindings returns an object that can list and get ServiceAccountBindings. + ServiceAccountBindings(namespace string) ServiceAccountBindingNamespaceLister + ServiceAccountBindingListerExpansion +} + +// serviceAccountBindingLister implements the ServiceAccountBindingLister interface. +type serviceAccountBindingLister struct { + indexer cache.Indexer +} + +// NewServiceAccountBindingLister returns a new ServiceAccountBindingLister. +func NewServiceAccountBindingLister(indexer cache.Indexer) ServiceAccountBindingLister { + return &serviceAccountBindingLister{indexer: indexer} +} + +// List lists all ServiceAccountBindings in the indexer. +func (s *serviceAccountBindingLister) List(selector labels.Selector) (ret []*v1alpha1.ServiceAccountBinding, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ServiceAccountBinding)) + }) + return ret, err +} + +// ServiceAccountBindings returns an object that can list and get ServiceAccountBindings. +func (s *serviceAccountBindingLister) ServiceAccountBindings(namespace string) ServiceAccountBindingNamespaceLister { + return serviceAccountBindingNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ServiceAccountBindingNamespaceLister helps list and get ServiceAccountBindings. +// All objects returned here must be treated as read-only. +type ServiceAccountBindingNamespaceLister interface { + // List lists all ServiceAccountBindings in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ServiceAccountBinding, err error) + // Get retrieves the ServiceAccountBinding from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.ServiceAccountBinding, error) + ServiceAccountBindingNamespaceListerExpansion +} + +// serviceAccountBindingNamespaceLister implements the ServiceAccountBindingNamespaceLister +// interface. +type serviceAccountBindingNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all ServiceAccountBindings in the indexer for a given namespace. +func (s serviceAccountBindingNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ServiceAccountBinding, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ServiceAccountBinding)) + }) + return ret, err +} + +// Get retrieves the ServiceAccountBinding from the indexer for a given namespace and name. +func (s serviceAccountBindingNamespaceLister) Get(name string) (*v1alpha1.ServiceAccountBinding, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("serviceaccountbinding"), name) + } + return obj.(*v1alpha1.ServiceAccountBinding), nil +} From 090230d205fd001564f5e19b21488d1e9b851226 Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Mon, 14 Apr 2025 18:40:43 +0800 Subject: [PATCH 2/6] feat: add apikeys, sa, sab --- api/v1alpha1/apikey_types.go | 140 ++++++ api/v1alpha1/serviceaccount_types.go | 84 ++++ api/v1alpha1/serviceaccountbinding_types.go | 71 +++ api/v1alpha1/zz_generated.deepcopy.go | 434 ++++++++++++++++-- .../resource.streamnative.io_apikeys.yaml | 234 ++++++++++ ...treamnative.io_serviceaccountbindings.yaml | 171 +++++++ ...ource.streamnative.io_serviceaccounts.yaml | 183 ++++++++ controllers/apikey_controller.go | 367 +++++++++++++++ controllers/serviceaccount_controller.go | 350 ++++++++++++++ .../serviceaccountbinding_controller.go | 346 ++++++++++++++ pkg/streamnativecloud/apikey_client.go | 142 ++++++ .../apis/cloud/v1alpha1/apikey_types.go | 23 +- .../cloud/v1alpha1/serviceaccount_types.go | 9 + .../v1alpha1/serviceaccountbinding_types.go | 9 + .../v1alpha1/zz_generated.api.register.go | 3 + .../cloud/v1alpha1/zz_generated.deepcopy.go | 100 +++- .../serviceaccount_client.go | 128 ++++++ .../serviceaccountbinding_client.go | 130 ++++++ 18 files changed, 2873 insertions(+), 51 deletions(-) create mode 100644 api/v1alpha1/apikey_types.go create mode 100644 api/v1alpha1/serviceaccount_types.go create mode 100644 api/v1alpha1/serviceaccountbinding_types.go create mode 100644 config/crd/bases/resource.streamnative.io_apikeys.yaml create mode 100644 config/crd/bases/resource.streamnative.io_serviceaccountbindings.yaml create mode 100644 config/crd/bases/resource.streamnative.io_serviceaccounts.yaml create mode 100644 controllers/apikey_controller.go create mode 100644 controllers/serviceaccount_controller.go create mode 100644 controllers/serviceaccountbinding_controller.go create mode 100644 pkg/streamnativecloud/apikey_client.go create mode 100644 pkg/streamnativecloud/serviceaccount_client.go create mode 100644 pkg/streamnativecloud/serviceaccountbinding_client.go diff --git a/api/v1alpha1/apikey_types.go b/api/v1alpha1/apikey_types.go new file mode 100644 index 00000000..03f3adee --- /dev/null +++ b/api/v1alpha1/apikey_types.go @@ -0,0 +1,140 @@ +// Copyright 2025 StreamNative +// +// 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 v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// APIKeySpec defines the desired state of APIKey +type APIKeySpec struct { + // APIServerRef is the reference to the StreamNativeCloudConnection + // +required + APIServerRef corev1.LocalObjectReference `json:"apiServerRef"` + + // InstanceName is the name of the instance this API key is for + // +optional + InstanceName string `json:"instanceName,omitempty"` + + // ServiceAccountName is the name of the service account this API key is for + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` + + // Description is a user defined description of the key + // +optional + Description string `json:"description,omitempty"` + + // ExpirationTime is a timestamp that defines when this API key will expire + // This can only be set on initial creation and not updated later + // +optional + ExpirationTime *metav1.Time `json:"expirationTime,omitempty"` + + // Revoke is a boolean that defines if the token of this API key should be revoked + // +kubebuilder:default=false + // +optional + Revoke bool `json:"revoke,omitempty"` + + // EncryptionKey is a public key to encrypt the API Key token + // Please provide an RSA key with modulus length of at least 2048 bits + // +optional + EncryptionKey *EncryptionKey `json:"encryptionKey,omitempty"` +} + +// EncryptionKey specifies a public key for encryption purposes +// Either a PEM or JWK must be specified +type EncryptionKey struct { + // PEM is a PEM-encoded public key in PKIX, ASN.1 DER form ("spki" format) + // +optional + PEM string `json:"pem,omitempty"` + + // JWK is a JWK-encoded public key + // +optional + JWK string `json:"jwk,omitempty"` +} + +// APIKeyStatus defines the observed state of APIKey +type APIKeyStatus struct { + // Conditions represent the latest available observations of an object's state + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // ObservedGeneration is the last observed generation + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // KeyID is a generated field that is a uid for the token + // +optional + KeyID *string `json:"keyId,omitempty"` + + // IssuedAt is a timestamp of when the key was issued + // +optional + IssuedAt *metav1.Time `json:"issuedAt,omitempty"` + + // ExpiresAt is a timestamp of when the key expires + // +optional + ExpiresAt *metav1.Time `json:"expiresAt,omitempty"` + + // Token is the plaintext security token issued for the key + // +optional + Token *string `json:"token,omitempty"` + + // EncryptedToken is the encrypted security token issued for the key + // +optional + EncryptedToken *EncryptedToken `json:"encryptedToken,omitempty"` + + // RevokedAt is a timestamp of when the key was revoked, it triggers revocation action + // +optional + RevokedAt *metav1.Time `json:"revokedAt,omitempty"` +} + +// EncryptedToken is token that is encrypted using an encryption key +// +structType=atomic +type EncryptedToken struct { + // JWE is the token as a JSON Web Encryption (JWE) message + // For RSA public keys, the key encryption algorithm is RSA-OAEP, and the content encryption algorithm is AES GCM + // +optional + JWE *string `json:"jwe,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:scope=Namespaced,categories={streamnative,all} +//+kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +//+kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status" +//+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// APIKey is the Schema for the APIKeys API +type APIKey struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec APIKeySpec `json:"spec,omitempty"` + Status APIKeyStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true +//+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// APIKeyList contains a list of APIKey +type APIKeyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []APIKey `json:"items"` +} + +func init() { + SchemeBuilder.Register(&APIKey{}, &APIKeyList{}) +} diff --git a/api/v1alpha1/serviceaccount_types.go b/api/v1alpha1/serviceaccount_types.go new file mode 100644 index 00000000..f4838f29 --- /dev/null +++ b/api/v1alpha1/serviceaccount_types.go @@ -0,0 +1,84 @@ +// Copyright 2025 StreamNative +// +// 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 v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ServiceAccountSpec defines the desired state of ServiceAccount +type ServiceAccountSpec struct { + // APIServerRef is the reference to the StreamNativeCloudConnection + // +required + APIServerRef corev1.LocalObjectReference `json:"apiServerRef"` + + // Description is a user defined description of the service account + // +optional + Description string `json:"description,omitempty"` + + // InstanceName is the name of the instance this service account is for + // +optional + InstanceName string `json:"instanceName,omitempty"` +} + +// ServiceAccountStatus defines the observed state of ServiceAccount +type ServiceAccountStatus struct { + // Conditions represent the latest available observations of an object's state + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // ObservedGeneration is the last observed generation + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // PrivateKeyType indicates the type of private key information + // +optional + PrivateKeyType string `json:"privateKeyType,omitempty"` + + // PrivateKeyData provides the private key data (in base-64 format) for authentication purposes + // +optional + PrivateKeyData string `json:"privateKeyData,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:scope=Namespaced,categories={streamnative,all} +//+kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +//+kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status" +//+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServiceAccount is the Schema for the ServiceAccounts API +type ServiceAccount struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ServiceAccountSpec `json:"spec,omitempty"` + Status ServiceAccountStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true +//+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServiceAccountList contains a list of ServiceAccount +type ServiceAccountList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ServiceAccount `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ServiceAccount{}, &ServiceAccountList{}) +} diff --git a/api/v1alpha1/serviceaccountbinding_types.go b/api/v1alpha1/serviceaccountbinding_types.go new file mode 100644 index 00000000..3eca30ee --- /dev/null +++ b/api/v1alpha1/serviceaccountbinding_types.go @@ -0,0 +1,71 @@ +// Copyright 2025 StreamNative +// +// 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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ServiceAccountBindingSpec defines the desired state of ServiceAccountBinding +type ServiceAccountBindingSpec struct { + // ServiceAccountName refers to the ServiceAccount under the same namespace as this binding object + // +required + ServiceAccountName string `json:"serviceAccountName"` + + // PoolMemberRef refers to a PoolMember in the current namespace or other namespaces + // +required + PoolMemberRef PoolMemberReference `json:"poolMemberRef"` +} + +// ServiceAccountBindingStatus defines the observed state of ServiceAccountBinding +type ServiceAccountBindingStatus struct { + // Conditions represent the latest available observations of an object's state + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // ObservedGeneration is the last observed generation + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:scope=Namespaced,categories={streamnative,all} +//+kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +//+kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status" +//+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServiceAccountBinding is the Schema for the ServiceAccountBindings API +type ServiceAccountBinding struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ServiceAccountBindingSpec `json:"spec,omitempty"` + Status ServiceAccountBindingStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true +//+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServiceAccountBindingList contains a list of ServiceAccountBinding +type ServiceAccountBindingList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ServiceAccountBinding `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ServiceAccountBinding{}, &ServiceAccountBindingList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 55f4d4c3..c47961af 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -20,12 +20,145 @@ package v1alpha1 import ( "github.com/streamnative/pulsar-resources-operator/pkg/utils" - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIKey) DeepCopyInto(out *APIKey) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIKey. +func (in *APIKey) DeepCopy() *APIKey { + if in == nil { + return nil + } + out := new(APIKey) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *APIKey) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIKeyList) DeepCopyInto(out *APIKeyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]APIKey, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIKeyList. +func (in *APIKeyList) DeepCopy() *APIKeyList { + if in == nil { + return nil + } + out := new(APIKeyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *APIKeyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIKeySpec) DeepCopyInto(out *APIKeySpec) { + *out = *in + out.APIServerRef = in.APIServerRef + if in.ExpirationTime != nil { + in, out := &in.ExpirationTime, &out.ExpirationTime + *out = (*in).DeepCopy() + } + if in.EncryptionKey != nil { + in, out := &in.EncryptionKey, &out.EncryptionKey + *out = new(EncryptionKey) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIKeySpec. +func (in *APIKeySpec) DeepCopy() *APIKeySpec { + if in == nil { + return nil + } + out := new(APIKeySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIKeyStatus) DeepCopyInto(out *APIKeyStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.KeyID != nil { + in, out := &in.KeyID, &out.KeyID + *out = new(string) + **out = **in + } + if in.IssuedAt != nil { + in, out := &in.IssuedAt, &out.IssuedAt + *out = (*in).DeepCopy() + } + if in.ExpiresAt != nil { + in, out := &in.ExpiresAt, &out.ExpiresAt + *out = (*in).DeepCopy() + } + if in.Token != nil { + in, out := &in.Token, &out.Token + *out = new(string) + **out = **in + } + if in.EncryptedToken != nil { + in, out := &in.EncryptedToken, &out.EncryptedToken + *out = new(EncryptedToken) + (*in).DeepCopyInto(*out) + } + if in.RevokedAt != nil { + in, out := &in.RevokedAt, &out.RevokedAt + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIKeyStatus. +func (in *APIKeyStatus) DeepCopy() *APIKeyStatus { + if in == nil { + return nil + } + out := new(APIKeyStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Artifact) DeepCopyInto(out *Artifact) { *out = *in @@ -300,7 +433,7 @@ func (in *ComputeFlinkDeploymentSpec) DeepCopyInto(out *ComputeFlinkDeploymentSp } if in.ImagePullSecrets != nil { in, out := &in.ImagePullSecrets, &out.ImagePullSecrets - *out = make([]v1.LocalObjectReference, len(*in)) + *out = make([]corev1.LocalObjectReference, len(*in)) copy(*out, *in) } } @@ -320,7 +453,7 @@ func (in *ComputeFlinkDeploymentStatus) DeepCopyInto(out *ComputeFlinkDeployment *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -442,7 +575,7 @@ func (in *ComputeWorkspaceStatus) DeepCopyInto(out *ComputeWorkspaceStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -535,14 +668,14 @@ func (in *Container) DeepCopyInto(out *Container) { } if in.Env != nil { in, out := &in.Env, &out.Env - *out = make([]v1.EnvVar, len(*in)) + *out = make([]corev1.EnvVar, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.EnvFrom != nil { in, out := &in.EnvFrom, &out.EnvFrom - *out = make([]v1.EnvFromSource, len(*in)) + *out = make([]corev1.EnvFromSource, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -550,14 +683,14 @@ func (in *Container) DeepCopyInto(out *Container) { in.Resources.DeepCopyInto(&out.Resources) if in.VolumeMounts != nil { in, out := &in.VolumeMounts, &out.VolumeMounts - *out = make([]v1.VolumeMount, len(*in)) + *out = make([]corev1.VolumeMount, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.SecurityContext != nil { in, out := &in.SecurityContext, &out.SecurityContext - *out = new(v1.SecurityContext) + *out = new(corev1.SecurityContext) (*in).DeepCopyInto(*out) } } @@ -599,6 +732,41 @@ func (in *CryptoConfig) DeepCopy() *CryptoConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EncryptedToken) DeepCopyInto(out *EncryptedToken) { + *out = *in + if in.JWE != nil { + in, out := &in.JWE, &out.JWE + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EncryptedToken. +func (in *EncryptedToken) DeepCopy() *EncryptedToken { + if in == nil { + return nil + } + out := new(EncryptedToken) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EncryptionKey) DeepCopyInto(out *EncryptionKey) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EncryptionKey. +func (in *EncryptionKey) DeepCopy() *EncryptionKey { + if in == nil { + return nil + } + out := new(EncryptionKey) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EnvVar) DeepCopyInto(out *EnvVar) { *out = *in @@ -822,22 +990,22 @@ func (in *PodTemplateSpec) DeepCopyInto(out *PodTemplateSpec) { } if in.SecurityContext != nil { in, out := &in.SecurityContext, &out.SecurityContext - *out = new(v1.PodSecurityContext) + *out = new(corev1.PodSecurityContext) (*in).DeepCopyInto(*out) } if in.ImagePullSecrets != nil { in, out := &in.ImagePullSecrets, &out.ImagePullSecrets - *out = make([]v1.LocalObjectReference, len(*in)) + *out = make([]corev1.LocalObjectReference, len(*in)) copy(*out, *in) } if in.Affinity != nil { in, out := &in.Affinity, &out.Affinity - *out = new(v1.Affinity) + *out = new(corev1.Affinity) (*in).DeepCopyInto(*out) } if in.Tolerations != nil { in, out := &in.Tolerations, &out.Tolerations - *out = make([]v1.Toleration, len(*in)) + *out = make([]corev1.Toleration, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1033,7 +1201,7 @@ func (in *PulsarConnectionStatus) DeepCopyInto(out *PulsarConnectionStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1230,7 +1398,7 @@ func (in *PulsarFunctionStatus) DeepCopyInto(out *PulsarFunctionStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1328,7 +1496,7 @@ func (in *PulsarGeoReplicationStatus) DeepCopyInto(out *PulsarGeoReplicationStat *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1447,7 +1615,7 @@ func (in *PulsarNSIsolationPolicyStatus) DeepCopyInto(out *PulsarNSIsolationPoli *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1594,11 +1762,11 @@ func (in *PulsarNamespaceSpec) DeepCopyInto(out *PulsarNamespaceSpec) { } if in.GeoReplicationRefs != nil { in, out := &in.GeoReplicationRefs, &out.GeoReplicationRefs - *out = make([]*v1.LocalObjectReference, len(*in)) + *out = make([]*corev1.LocalObjectReference, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1.LocalObjectReference) + *out = new(corev1.LocalObjectReference) **out = **in } } @@ -1635,7 +1803,7 @@ func (in *PulsarNamespaceStatus) DeepCopyInto(out *PulsarNamespaceStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1739,7 +1907,7 @@ func (in *PulsarPackageStatus) DeepCopyInto(out *PulsarPackageStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1846,7 +2014,7 @@ func (in *PulsarPermissionStatus) DeepCopyInto(out *PulsarPermissionStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -2013,7 +2181,7 @@ func (in *PulsarSinkStatus) DeepCopyInto(out *PulsarSinkStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -2147,7 +2315,7 @@ func (in *PulsarSourceStatus) DeepCopyInto(out *PulsarSourceStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -2239,11 +2407,11 @@ func (in *PulsarTenantSpec) DeepCopyInto(out *PulsarTenantSpec) { } if in.GeoReplicationRefs != nil { in, out := &in.GeoReplicationRefs, &out.GeoReplicationRefs - *out = make([]*v1.LocalObjectReference, len(*in)) + *out = make([]*corev1.LocalObjectReference, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1.LocalObjectReference) + *out = new(corev1.LocalObjectReference) **out = **in } } @@ -2265,7 +2433,7 @@ func (in *PulsarTenantStatus) DeepCopyInto(out *PulsarTenantStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -2412,11 +2580,11 @@ func (in *PulsarTopicSpec) DeepCopyInto(out *PulsarTopicSpec) { } if in.GeoReplicationRefs != nil { in, out := &in.GeoReplicationRefs, &out.GeoReplicationRefs - *out = make([]*v1.LocalObjectReference, len(*in)) + *out = make([]*corev1.LocalObjectReference, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(v1.LocalObjectReference) + *out = new(corev1.LocalObjectReference) **out = **in } } @@ -2448,7 +2616,7 @@ func (in *PulsarTopicStatus) DeepCopyInto(out *PulsarTopicStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -2596,7 +2764,7 @@ func (in *SecretReference) DeepCopyInto(out *SecretReference) { *out = *in if in.ValueFrom != nil { in, out := &in.ValueFrom, &out.ValueFrom - *out = new(v1.SecretKeySelector) + *out = new(corev1.SecretKeySelector) (*in).DeepCopyInto(*out) } } @@ -2639,7 +2807,7 @@ func (in *SecretSpec) DeepCopyInto(out *SecretSpec) { } if in.Type != nil { in, out := &in.Type, &out.Type - *out = new(v1.SecretType) + *out = new(corev1.SecretType) **out = **in } } @@ -2659,7 +2827,7 @@ func (in *SecretStatus) DeepCopyInto(out *SecretStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -2676,6 +2844,130 @@ func (in *SecretStatus) DeepCopy() *SecretStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccount) DeepCopyInto(out *ServiceAccount) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccount. +func (in *ServiceAccount) DeepCopy() *ServiceAccount { + if in == nil { + return nil + } + out := new(ServiceAccount) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceAccount) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountBinding) DeepCopyInto(out *ServiceAccountBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountBinding. +func (in *ServiceAccountBinding) DeepCopy() *ServiceAccountBinding { + if in == nil { + return nil + } + out := new(ServiceAccountBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceAccountBinding) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountBindingList) DeepCopyInto(out *ServiceAccountBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ServiceAccountBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountBindingList. +func (in *ServiceAccountBindingList) DeepCopy() *ServiceAccountBindingList { + if in == nil { + return nil + } + out := new(ServiceAccountBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceAccountBindingList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountBindingSpec) DeepCopyInto(out *ServiceAccountBindingSpec) { + *out = *in + out.PoolMemberRef = in.PoolMemberRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountBindingSpec. +func (in *ServiceAccountBindingSpec) DeepCopy() *ServiceAccountBindingSpec { + if in == nil { + return nil + } + out := new(ServiceAccountBindingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountBindingStatus) DeepCopyInto(out *ServiceAccountBindingStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountBindingStatus. +func (in *ServiceAccountBindingStatus) DeepCopy() *ServiceAccountBindingStatus { + if in == nil { + return nil + } + out := new(ServiceAccountBindingStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceAccountCredentials) DeepCopyInto(out *ServiceAccountCredentials) { *out = *in @@ -2691,6 +2983,76 @@ func (in *ServiceAccountCredentials) DeepCopy() *ServiceAccountCredentials { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountList) DeepCopyInto(out *ServiceAccountList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ServiceAccount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountList. +func (in *ServiceAccountList) DeepCopy() *ServiceAccountList { + if in == nil { + return nil + } + out := new(ServiceAccountList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceAccountList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountSpec) DeepCopyInto(out *ServiceAccountSpec) { + *out = *in + out.APIServerRef = in.APIServerRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountSpec. +func (in *ServiceAccountSpec) DeepCopy() *ServiceAccountSpec { + if in == nil { + return nil + } + out := new(ServiceAccountSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountStatus) DeepCopyInto(out *ServiceAccountStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountStatus. +func (in *ServiceAccountStatus) DeepCopy() *ServiceAccountStatus { + if in == nil { + return nil + } + out := new(ServiceAccountStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StreamNativeCloudConnection) DeepCopyInto(out *StreamNativeCloudConnection) { *out = *in @@ -2772,7 +3134,7 @@ func (in *StreamNativeCloudConnectionStatus) DeepCopyInto(out *StreamNativeCloud *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -2883,12 +3245,12 @@ func (in *VolumeSource) DeepCopyInto(out *VolumeSource) { *out = *in if in.ConfigMap != nil { in, out := &in.ConfigMap, &out.ConfigMap - *out = new(v1.ConfigMapVolumeSource) + *out = new(corev1.ConfigMapVolumeSource) (*in).DeepCopyInto(*out) } if in.Secret != nil { in, out := &in.Secret, &out.Secret - *out = new(v1.SecretVolumeSource) + *out = new(corev1.SecretVolumeSource) (*in).DeepCopyInto(*out) } } diff --git a/config/crd/bases/resource.streamnative.io_apikeys.yaml b/config/crd/bases/resource.streamnative.io_apikeys.yaml new file mode 100644 index 00000000..67e740bb --- /dev/null +++ b/config/crd/bases/resource.streamnative.io_apikeys.yaml @@ -0,0 +1,234 @@ +# Copyright 2025 StreamNative +# +# 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. + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: apikeys.resource.streamnative.io +spec: + group: resource.streamnative.io + names: + categories: + - streamnative + - all + kind: APIKey + listKind: APIKeyList + plural: apikeys + singular: apikey + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: READY + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: APIKey is the Schema for the APIKeys API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: APIKeySpec defines the desired state of APIKey + properties: + apiServerRef: + description: APIServerRef is the reference to the StreamNativeCloudConnection + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + type: object + x-kubernetes-map-type: atomic + description: + description: Description is a user defined description of the key + type: string + encryptionKey: + description: |- + EncryptionKey is a public key to encrypt the API Key token + Please provide an RSA key with modulus length of at least 2048 bits + properties: + jwk: + description: JWK is a JWK-encoded public key + type: string + pem: + description: PEM is a PEM-encoded public key in PKIX, ASN.1 DER + form ("spki" format) + type: string + type: object + expirationTime: + description: |- + ExpirationTime is a timestamp that defines when this API key will expire + This can only be set on initial creation and not updated later + format: date-time + type: string + instanceName: + description: InstanceName is the name of the instance this API key + is for + type: string + revoke: + default: false + description: Revoke is a boolean that defines if the token of this + API key should be revoked + type: boolean + serviceAccountName: + description: ServiceAccountName is the name of the service account + this API key is for + type: string + required: + - apiServerRef + type: object + status: + description: APIKeyStatus defines the observed state of APIKey + properties: + conditions: + description: Conditions represent the latest available observations + of an object's state + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + encryptedToken: + description: EncryptedToken is the encrypted security token issued + for the key + properties: + jwe: + description: |- + JWE is the token as a JSON Web Encryption (JWE) message + For RSA public keys, the key encryption algorithm is RSA-OAEP, and the content encryption algorithm is AES GCM + type: string + type: object + x-kubernetes-map-type: atomic + expiresAt: + description: ExpiresAt is a timestamp of when the key expires + format: date-time + type: string + issuedAt: + description: IssuedAt is a timestamp of when the key was issued + format: date-time + type: string + keyId: + description: KeyID is a generated field that is a uid for the token + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation + format: int64 + type: integer + revokedAt: + description: RevokedAt is a timestamp of when the key was revoked, + it triggers revocation action + format: date-time + type: string + token: + description: Token is the plaintext security token issued for the + key + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/resource.streamnative.io_serviceaccountbindings.yaml b/config/crd/bases/resource.streamnative.io_serviceaccountbindings.yaml new file mode 100644 index 00000000..4582ee47 --- /dev/null +++ b/config/crd/bases/resource.streamnative.io_serviceaccountbindings.yaml @@ -0,0 +1,171 @@ +# Copyright 2025 StreamNative +# +# 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. + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: serviceaccountbindings.resource.streamnative.io +spec: + group: resource.streamnative.io + names: + categories: + - streamnative + - all + kind: ServiceAccountBinding + listKind: ServiceAccountBindingList + plural: serviceaccountbindings + singular: serviceaccountbinding + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: READY + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceAccountBinding is the Schema for the ServiceAccountBindings + API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ServiceAccountBindingSpec defines the desired state of ServiceAccountBinding + properties: + poolMemberRef: + description: PoolMemberRef refers to a PoolMember in the current namespace + or other namespaces + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + serviceAccountName: + description: ServiceAccountName refers to the ServiceAccount under + the same namespace as this binding object + type: string + required: + - poolMemberRef + - serviceAccountName + type: object + status: + description: ServiceAccountBindingStatus defines the observed state of + ServiceAccountBinding + properties: + conditions: + description: Conditions represent the latest available observations + of an object's state + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + observedGeneration: + description: ObservedGeneration is the last observed generation + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/resource.streamnative.io_serviceaccounts.yaml b/config/crd/bases/resource.streamnative.io_serviceaccounts.yaml new file mode 100644 index 00000000..a58232e7 --- /dev/null +++ b/config/crd/bases/resource.streamnative.io_serviceaccounts.yaml @@ -0,0 +1,183 @@ +# Copyright 2025 StreamNative +# +# 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. + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: serviceaccounts.resource.streamnative.io +spec: + group: resource.streamnative.io + names: + categories: + - streamnative + - all + kind: ServiceAccount + listKind: ServiceAccountList + plural: serviceaccounts + singular: serviceaccount + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: READY + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceAccount is the Schema for the ServiceAccounts API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ServiceAccountSpec defines the desired state of ServiceAccount + properties: + apiServerRef: + description: APIServerRef is the reference to the StreamNativeCloudConnection + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + type: object + x-kubernetes-map-type: atomic + description: + description: Description is a user defined description of the service + account + type: string + instanceName: + description: InstanceName is the name of the instance this service + account is for + type: string + required: + - apiServerRef + type: object + status: + description: ServiceAccountStatus defines the observed state of ServiceAccount + properties: + conditions: + description: Conditions represent the latest available observations + of an object's state + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + observedGeneration: + description: ObservedGeneration is the last observed generation + format: int64 + type: integer + privateKeyData: + description: PrivateKeyData provides the private key data (in base-64 + format) for authentication purposes + type: string + privateKeyType: + description: PrivateKeyType indicates the type of private key information + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/controllers/apikey_controller.go b/controllers/apikey_controller.go new file mode 100644 index 00000000..6eb593f8 --- /dev/null +++ b/controllers/apikey_controller.go @@ -0,0 +1,367 @@ +// Copyright 2025 StreamNative +// +// 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 controllers + +import ( + "context" + "fmt" + "sync" + "time" + + resourcev1alpha1 "github.com/streamnative/pulsar-resources-operator/api/v1alpha1" + controllers2 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + cloudapi "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" +) + +// APIKeyReconciler reconciles a StreamNative Cloud APIKey object +type APIKeyReconciler struct { + client.Client + Scheme *runtime.Scheme + ConnectionManager *ConnectionManager + // watcherMap stores active watchers for APIKeys + watcherMap map[types.NamespacedName]watch.Interface + // watcherMutex protects watcherMap + watcherMutex sync.RWMutex +} + +const APIKeyFinalizer = "apikey.resource.streamnative.io/finalizer" + +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=apikeys,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=apikeys/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=apikeys/finalizers,verbs=update +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=streamnativecloudconnections,verbs=get;list;watch + +// handleWatchEvents processes events from the watch interface +func (r *APIKeyReconciler) handleWatchEvents(ctx context.Context, namespacedName types.NamespacedName, watcher watch.Interface) { + logger := log.FromContext(ctx) + defer watcher.Stop() + + for { + select { + case <-ctx.Done(): + return + case event, ok := <-watcher.ResultChan(): + if !ok { + logger.Info("Watch channel closed", "namespace", namespacedName.Namespace, "name", namespacedName.Name) + // Remove the watcher from the map + r.watcherMutex.Lock() + delete(r.watcherMap, namespacedName) + r.watcherMutex.Unlock() + return + } + + if event.Type == watch.Modified { + // Check if the object is an APIKey + _, ok := event.Object.(*cloudapi.APIKey) + if !ok { + logger.Error(fmt.Errorf("unexpected object type"), "Failed to convert object to APIKey") + continue + } + + // Get the local APIKey + localAPIKey := &resourcev1alpha1.APIKey{} + if err := r.Get(ctx, namespacedName, localAPIKey); err != nil { + logger.Error(err, "Failed to get local APIKey") + continue + } + + // Update status + r.updateAPIKeyStatus(ctx, localAPIKey, nil, "Ready", "APIKey synced successfully") + } + } + } +} + +// setupWatch creates a new watcher for an APIKey +func (r *APIKeyReconciler) setupWatch(ctx context.Context, apiKey *resourcev1alpha1.APIKey, apiKeyClient *controllers2.APIKeyClient) error { + namespacedName := types.NamespacedName{ + Namespace: apiKey.Namespace, + Name: apiKey.Name, + } + + // Check if we already have a watcher + r.watcherMutex.RLock() + _, exists := r.watcherMap[namespacedName] + r.watcherMutex.RUnlock() + if exists { + return nil + } + + // Create new watcher + watcher, err := apiKeyClient.WatchAPIKey(ctx, apiKey.Name) + if err != nil { + return fmt.Errorf("failed to create watcher: %w", err) + } + + // Store watcher in map + r.watcherMutex.Lock() + r.watcherMap[namespacedName] = watcher + r.watcherMutex.Unlock() + + // Start watching in a new goroutine + go r.handleWatchEvents(ctx, namespacedName, watcher) + return nil +} + +// Reconcile handles the reconciliation of APIKey objects +func (r *APIKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconciling APIKey", "namespace", req.Namespace, "name", req.Name) + + // Add requeue interval for status sync + requeueInterval := time.Minute + + // Get the APIKey resource + apiKey := &resourcev1alpha1.APIKey{} + if err := r.Get(ctx, req.NamespacedName, apiKey); err != nil { + if apierrors.IsNotFound(err) { + // Stop and remove watcher if it exists + r.watcherMutex.Lock() + if watcher, exists := r.watcherMap[req.NamespacedName]; exists { + watcher.Stop() + delete(r.watcherMap, req.NamespacedName) + } + r.watcherMutex.Unlock() + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + // Get the APIServerConnection + connection := &resourcev1alpha1.StreamNativeCloudConnection{} + if err := r.Get(ctx, types.NamespacedName{ + Namespace: req.Namespace, + Name: apiKey.Spec.APIServerRef.Name, + }, connection); err != nil { + r.updateAPIKeyStatus(ctx, apiKey, err, "ConnectionNotFound", + fmt.Sprintf("Failed to get APIServerConnection: %v", err)) + return ctrl.Result{}, err + } + + // Get API connection + apiConn, err := r.ConnectionManager.GetOrCreateConnection(connection, nil) + if err != nil { + // If connection is not initialized, requeue the request + if _, ok := err.(*NotInitializedError); ok { + logger.Info("Connection not initialized, requeueing", "error", err.Error()) + return ctrl.Result{Requeue: true}, nil + } + r.updateAPIKeyStatus(ctx, apiKey, err, "GetConnectionFailed", + fmt.Sprintf("Failed to get connection: %v", err)) + return ctrl.Result{}, err + } + + // Get organization from connection + organization := connection.Spec.Organization + if organization == "" { + err := fmt.Errorf("organization is required but not specified") + r.updateAPIKeyStatus(ctx, apiKey, err, "ValidationFailed", err.Error()) + return ctrl.Result{}, err + } + + // Create APIKey client + apiKeyClient, err := controllers2.NewAPIKeyClient(apiConn, organization) + if err != nil { + r.updateAPIKeyStatus(ctx, apiKey, err, "ClientCreationFailed", + fmt.Sprintf("Failed to create APIKey client: %v", err)) + return ctrl.Result{}, err + } + + // Handle deletion + if !apiKey.DeletionTimestamp.IsZero() { + if controllers2.ContainsString(apiKey.Finalizers, APIKeyFinalizer) { + // Try to delete remote APIKey + if err := apiKeyClient.DeleteAPIKey(ctx, apiKey); err != nil { + if !apierrors.IsNotFound(err) { + r.updateAPIKeyStatus(ctx, apiKey, err, "DeleteFailed", + fmt.Sprintf("Failed to delete external resources: %v", err)) + return ctrl.Result{}, err + } + // If the resource is already gone, that's fine + logger.Info("Remote APIKey already deleted or not found", + "apiKey", apiKey.Name) + } + + // Remove finalizer after successful deletion + apiKey.Finalizers = controllers2.RemoveString(apiKey.Finalizers, APIKeyFinalizer) + if err := r.Update(ctx, apiKey); err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil + } + + // Add finalizer if it doesn't exist + if !controllers2.ContainsString(apiKey.Finalizers, APIKeyFinalizer) { + apiKey.Finalizers = append(apiKey.Finalizers, APIKeyFinalizer) + if err := r.Update(ctx, apiKey); err != nil { + return ctrl.Result{}, err + } + } + + // Check if ApiKey exists + existingAPIKey, err := apiKeyClient.GetAPIKey(ctx, apiKey.Name) + if err != nil { + logger.Info("Failed to get APIKey", "error", err, "existingAPIKey", existingAPIKey) + if !apierrors.IsNotFound(err) { + r.updateAPIKeyStatus(ctx, apiKey, err, "GetAPIKeyFailed", + fmt.Sprintf("Failed to get APIKey: %v", err)) + return ctrl.Result{}, client.IgnoreNotFound(err) + } + existingAPIKey = nil + } + + if existingAPIKey == nil { + // Create APIKey + resultAPIKey, err := apiKeyClient.CreateAPIKey(ctx, apiKey) + if err != nil { + r.updateAPIKeyStatus(ctx, apiKey, err, "CreateAPIKeyFailed", + fmt.Sprintf("Failed to create APIKey: %v", err)) + return ctrl.Result{}, err + } + + // Update status with token information + if resultAPIKey.Status.Token != nil { + apiKey.Status.Token = resultAPIKey.Status.Token + } + if resultAPIKey.Status.KeyID != nil { + apiKey.Status.KeyID = resultAPIKey.Status.KeyID + } + if resultAPIKey.Status.IssuedAt != nil { + apiKey.Status.IssuedAt = resultAPIKey.Status.IssuedAt + } + if resultAPIKey.Status.ExpiresAt != nil { + apiKey.Status.ExpiresAt = resultAPIKey.Status.ExpiresAt + } + if resultAPIKey.Status.EncryptedToken != nil { + apiKey.Status.EncryptedToken = &resourcev1alpha1.EncryptedToken{ + JWE: resultAPIKey.Status.EncryptedToken.JWE, + } + } + + // Update status + r.updateAPIKeyStatus(ctx, apiKey, nil, "Ready", "APIKey created successfully") + } else { + // Update APIKey + resultAPIKey, err := apiKeyClient.UpdateAPIKey(ctx, apiKey) + if err != nil { + r.updateAPIKeyStatus(ctx, apiKey, err, "UpdateAPIKeyFailed", + fmt.Sprintf("Failed to update APIKey: %v", err)) + return ctrl.Result{}, err + } + + // Update status with token information + if resultAPIKey.Status.KeyID != nil { + apiKey.Status.KeyID = resultAPIKey.Status.KeyID + } + if resultAPIKey.Status.IssuedAt != nil { + apiKey.Status.IssuedAt = resultAPIKey.Status.IssuedAt + } + if resultAPIKey.Status.ExpiresAt != nil { + apiKey.Status.ExpiresAt = resultAPIKey.Status.ExpiresAt + } + if resultAPIKey.Status.RevokedAt != nil { + apiKey.Status.RevokedAt = resultAPIKey.Status.RevokedAt + } + + // Update status + r.updateAPIKeyStatus(ctx, apiKey, nil, "Ready", "APIKey updated successfully") + } + + // Setup watch after APIKey is created/updated + if err := r.setupWatch(ctx, apiKey, apiKeyClient); err != nil { + logger.Error(err, "Failed to setup watch") + // Don't return error, just log it + } + + return ctrl.Result{RequeueAfter: requeueInterval}, nil +} + +func (r *APIKeyReconciler) updateAPIKeyStatus( + ctx context.Context, + apiKey *resourcev1alpha1.APIKey, + err error, + reason string, + message string, +) { + // Create new condition + condition := metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionTrue, + Reason: reason, + Message: message, + ObservedGeneration: apiKey.Generation, + LastTransitionTime: metav1.Now(), + } + + if err != nil { + condition.Status = metav1.ConditionFalse + } + + // Create new conditions slice for comparison + newConditions := make([]metav1.Condition, 0) + for _, c := range apiKey.Status.Conditions { + if c.Type != condition.Type { + newConditions = append(newConditions, c) + } + } + newConditions = append(newConditions, condition) + + // Check if status has actually changed + if !controllers2.StatusHasChanged(apiKey.Status.Conditions, newConditions) { + return + } + + // Update conditions + apiKey.Status.Conditions = newConditions + + // Update observed generation + apiKey.Status.ObservedGeneration = apiKey.Generation + + // Update status + if err := r.Status().Update(ctx, apiKey); err != nil { + log.FromContext(ctx).Error(err, "Failed to update APIKey status") + } +} + +// SetupWithManager sets up the controller with the Manager. +func (r *APIKeyReconciler) SetupWithManager(mgr ctrl.Manager) error { + // Initialize the watcher map + r.watcherMap = make(map[types.NamespacedName]watch.Interface) + + return ctrl.NewControllerManagedBy(mgr). + For(&resourcev1alpha1.APIKey{}). + WithEventFilter(predicate.Or( + // Trigger on spec changes + predicate.GenerationChangedPredicate{}, + // Trigger periodically to sync status + predicate.NewPredicateFuncs(func(object client.Object) bool { + // Trigger every minute to sync status + return true + }), + )). + Complete(r) +} diff --git a/controllers/serviceaccount_controller.go b/controllers/serviceaccount_controller.go new file mode 100644 index 00000000..6ab994ee --- /dev/null +++ b/controllers/serviceaccount_controller.go @@ -0,0 +1,350 @@ +// Copyright 2025 StreamNative +// +// 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 controllers + +import ( + "context" + "fmt" + "sync" + "time" + + resourcev1alpha1 "github.com/streamnative/pulsar-resources-operator/api/v1alpha1" + controllers2 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + cloudapi "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" +) + +// ServiceAccountReconciler reconciles a StreamNative Cloud ServiceAccount object +type ServiceAccountReconciler struct { + client.Client + Scheme *runtime.Scheme + ConnectionManager *ConnectionManager + // watcherMap stores active watchers for ServiceAccounts + watcherMap map[types.NamespacedName]watch.Interface + // watcherMutex protects watcherMap + watcherMutex sync.RWMutex +} + +const ServiceAccountFinalizer = "serviceaccount.resource.streamnative.io/finalizer" + +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=serviceaccounts/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=serviceaccounts/finalizers,verbs=update +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=streamnativecloudconnections,verbs=get;list;watch + +// handleWatchEvents processes events from the watch interface +func (r *ServiceAccountReconciler) handleWatchEvents(ctx context.Context, namespacedName types.NamespacedName, watcher watch.Interface) { + logger := log.FromContext(ctx) + defer watcher.Stop() + + for { + select { + case <-ctx.Done(): + return + case event, ok := <-watcher.ResultChan(): + if !ok { + logger.Info("Watch channel closed", "namespace", namespacedName.Namespace, "name", namespacedName.Name) + // Remove the watcher from the map + r.watcherMutex.Lock() + delete(r.watcherMap, namespacedName) + r.watcherMutex.Unlock() + return + } + + if event.Type == watch.Modified { + // Check if the object is a ServiceAccount + _, ok := event.Object.(*cloudapi.ServiceAccount) + if !ok { + logger.Error(fmt.Errorf("unexpected object type"), "Failed to convert object to ServiceAccount") + continue + } + + // Get the local ServiceAccount + localServiceAccount := &resourcev1alpha1.ServiceAccount{} + if err := r.Get(ctx, namespacedName, localServiceAccount); err != nil { + logger.Error(err, "Failed to get local ServiceAccount") + continue + } + + // Update status + r.updateServiceAccountStatus(ctx, localServiceAccount, nil, "Ready", "ServiceAccount synced successfully") + } + } + } +} + +// setupWatch creates a new watcher for a ServiceAccount +func (r *ServiceAccountReconciler) setupWatch(ctx context.Context, serviceAccount *resourcev1alpha1.ServiceAccount, saClient *controllers2.ServiceAccountClient) error { + namespacedName := types.NamespacedName{ + Namespace: serviceAccount.Namespace, + Name: serviceAccount.Name, + } + + // Check if we already have a watcher + r.watcherMutex.RLock() + _, exists := r.watcherMap[namespacedName] + r.watcherMutex.RUnlock() + if exists { + return nil + } + + // Create new watcher + watcher, err := saClient.WatchServiceAccount(ctx, serviceAccount.Name) + if err != nil { + return fmt.Errorf("failed to create watcher: %w", err) + } + + // Store watcher in map + r.watcherMutex.Lock() + r.watcherMap[namespacedName] = watcher + r.watcherMutex.Unlock() + + // Start watching in a new goroutine + go r.handleWatchEvents(ctx, namespacedName, watcher) + return nil +} + +// Reconcile handles the reconciliation of ServiceAccount objects +func (r *ServiceAccountReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconciling ServiceAccount", "namespace", req.Namespace, "name", req.Name) + + // Add requeue interval for status sync + requeueInterval := time.Minute + + // Get the ServiceAccount resource + serviceAccount := &resourcev1alpha1.ServiceAccount{} + if err := r.Get(ctx, req.NamespacedName, serviceAccount); err != nil { + if apierrors.IsNotFound(err) { + // Stop and remove watcher if it exists + r.watcherMutex.Lock() + if watcher, exists := r.watcherMap[req.NamespacedName]; exists { + watcher.Stop() + delete(r.watcherMap, req.NamespacedName) + } + r.watcherMutex.Unlock() + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + // Get the APIServerConnection + connection := &resourcev1alpha1.StreamNativeCloudConnection{} + if err := r.Get(ctx, types.NamespacedName{ + Namespace: req.Namespace, + Name: serviceAccount.Spec.APIServerRef.Name, + }, connection); err != nil { + r.updateServiceAccountStatus(ctx, serviceAccount, err, "ConnectionNotFound", + fmt.Sprintf("Failed to get APIServerConnection: %v", err)) + return ctrl.Result{}, err + } + + // Get API connection + apiConn, err := r.ConnectionManager.GetOrCreateConnection(connection, nil) + if err != nil { + // If connection is not initialized, requeue the request + if _, ok := err.(*NotInitializedError); ok { + logger.Info("Connection not initialized, requeueing", "error", err.Error()) + return ctrl.Result{Requeue: true}, nil + } + r.updateServiceAccountStatus(ctx, serviceAccount, err, "GetConnectionFailed", + fmt.Sprintf("Failed to get connection: %v", err)) + return ctrl.Result{}, err + } + + // Get organization from connection + organization := connection.Spec.Organization + if organization == "" { + err := fmt.Errorf("organization is required but not specified") + r.updateServiceAccountStatus(ctx, serviceAccount, err, "ValidationFailed", err.Error()) + return ctrl.Result{}, err + } + + // Create ServiceAccount client + saClient, err := controllers2.NewServiceAccountClient(apiConn, organization) + if err != nil { + r.updateServiceAccountStatus(ctx, serviceAccount, err, "ClientCreationFailed", + fmt.Sprintf("Failed to create ServiceAccount client: %v", err)) + return ctrl.Result{}, err + } + + // Handle deletion + if !serviceAccount.DeletionTimestamp.IsZero() { + if controllers2.ContainsString(serviceAccount.Finalizers, ServiceAccountFinalizer) { + // Try to delete remote ServiceAccount + if err := saClient.DeleteServiceAccount(ctx, serviceAccount); err != nil { + if !apierrors.IsNotFound(err) { + r.updateServiceAccountStatus(ctx, serviceAccount, err, "DeleteFailed", + fmt.Sprintf("Failed to delete external resources: %v", err)) + return ctrl.Result{}, err + } + // If the resource is already gone, that's fine + logger.Info("Remote ServiceAccount already deleted or not found", + "serviceAccount", serviceAccount.Name) + } + + // Remove finalizer after successful deletion + serviceAccount.Finalizers = controllers2.RemoveString(serviceAccount.Finalizers, ServiceAccountFinalizer) + if err := r.Update(ctx, serviceAccount); err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil + } + + // Add finalizer if it doesn't exist + if !controllers2.ContainsString(serviceAccount.Finalizers, ServiceAccountFinalizer) { + serviceAccount.Finalizers = append(serviceAccount.Finalizers, ServiceAccountFinalizer) + if err := r.Update(ctx, serviceAccount); err != nil { + return ctrl.Result{}, err + } + } + + // Check if ServiceAccount exists + existingSA, err := saClient.GetServiceAccount(ctx, serviceAccount.Name) + if err != nil { + logger.Info("Failed to get ServiceAccount", "error", err, "existingSA", existingSA) + if !apierrors.IsNotFound(err) { + r.updateServiceAccountStatus(ctx, serviceAccount, err, "GetServiceAccountFailed", + fmt.Sprintf("Failed to get ServiceAccount: %v", err)) + return ctrl.Result{}, client.IgnoreNotFound(err) + } + existingSA = nil + } + + if existingSA == nil { + // Create ServiceAccount + resultSA, err := saClient.CreateServiceAccount(ctx, serviceAccount) + if err != nil { + r.updateServiceAccountStatus(ctx, serviceAccount, err, "CreateServiceAccountFailed", + fmt.Sprintf("Failed to create ServiceAccount: %v", err)) + return ctrl.Result{}, err + } + + // Update status with private key information + if resultSA.Status.PrivateKeyType != "" { + serviceAccount.Status.PrivateKeyType = resultSA.Status.PrivateKeyType + } + if resultSA.Status.PrivateKeyData != "" { + serviceAccount.Status.PrivateKeyData = resultSA.Status.PrivateKeyData + } + + // Update status + r.updateServiceAccountStatus(ctx, serviceAccount, nil, "Ready", "ServiceAccount created successfully") + } else { + // Update ServiceAccount + resultSA, err := saClient.UpdateServiceAccount(ctx, serviceAccount) + if err != nil { + r.updateServiceAccountStatus(ctx, serviceAccount, err, "UpdateServiceAccountFailed", + fmt.Sprintf("Failed to update ServiceAccount: %v", err)) + return ctrl.Result{}, err + } + + // Update status with private key information if available + if resultSA.Status.PrivateKeyType != "" { + serviceAccount.Status.PrivateKeyType = resultSA.Status.PrivateKeyType + } + if resultSA.Status.PrivateKeyData != "" { + serviceAccount.Status.PrivateKeyData = resultSA.Status.PrivateKeyData + } + + // Update status + r.updateServiceAccountStatus(ctx, serviceAccount, nil, "Ready", "ServiceAccount updated successfully") + } + + // Setup watch after ServiceAccount is created/updated + if err := r.setupWatch(ctx, serviceAccount, saClient); err != nil { + logger.Error(err, "Failed to setup watch") + // Don't return error, just log it + } + + return ctrl.Result{RequeueAfter: requeueInterval}, nil +} + +func (r *ServiceAccountReconciler) updateServiceAccountStatus( + ctx context.Context, + serviceAccount *resourcev1alpha1.ServiceAccount, + err error, + reason string, + message string, +) { + // Create new condition + condition := metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionTrue, + Reason: reason, + Message: message, + ObservedGeneration: serviceAccount.Generation, + LastTransitionTime: metav1.Now(), + } + + if err != nil { + condition.Status = metav1.ConditionFalse + } + + // Create new conditions slice for comparison + newConditions := make([]metav1.Condition, 0) + for _, c := range serviceAccount.Status.Conditions { + if c.Type != condition.Type { + newConditions = append(newConditions, c) + } + } + newConditions = append(newConditions, condition) + + // Check if status has actually changed + if !controllers2.StatusHasChanged(serviceAccount.Status.Conditions, newConditions) { + return + } + + // Update conditions + serviceAccount.Status.Conditions = newConditions + + // Update observed generation + serviceAccount.Status.ObservedGeneration = serviceAccount.Generation + + // Update status + if err := r.Status().Update(ctx, serviceAccount); err != nil { + log.FromContext(ctx).Error(err, "Failed to update ServiceAccount status") + } +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ServiceAccountReconciler) SetupWithManager(mgr ctrl.Manager) error { + // Initialize the watcher map + r.watcherMap = make(map[types.NamespacedName]watch.Interface) + + return ctrl.NewControllerManagedBy(mgr). + For(&resourcev1alpha1.ServiceAccount{}). + WithEventFilter(predicate.Or( + // Trigger on spec changes + predicate.GenerationChangedPredicate{}, + // Trigger periodically to sync status + predicate.NewPredicateFuncs(func(object client.Object) bool { + // Trigger every minute to sync status + return true + }), + )). + Complete(r) +} diff --git a/controllers/serviceaccountbinding_controller.go b/controllers/serviceaccountbinding_controller.go new file mode 100644 index 00000000..4bdce6de --- /dev/null +++ b/controllers/serviceaccountbinding_controller.go @@ -0,0 +1,346 @@ +// Copyright 2025 StreamNative +// +// 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 controllers + +import ( + "context" + "fmt" + "sync" + "time" + + resourcev1alpha1 "github.com/streamnative/pulsar-resources-operator/api/v1alpha1" + controllers2 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + cloudapi "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" +) + +// ServiceAccountBindingReconciler reconciles a StreamNative Cloud ServiceAccountBinding object +type ServiceAccountBindingReconciler struct { + client.Client + Scheme *runtime.Scheme + ConnectionManager *ConnectionManager + // watcherMap stores active watchers for ServiceAccountBindings + watcherMap map[types.NamespacedName]watch.Interface + // watcherMutex protects watcherMap + watcherMutex sync.RWMutex +} + +const ServiceAccountBindingFinalizer = "serviceaccountbinding.resource.streamnative.io/finalizer" + +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=serviceaccountbindings,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=serviceaccountbindings/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=serviceaccountbindings/finalizers,verbs=update +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=serviceaccounts,verbs=get;list;watch +//+kubebuilder:rbac:groups=resource.streamnative.io,resources=streamnativecloudconnections,verbs=get;list;watch + +// handleWatchEvents processes events from the watch interface +func (r *ServiceAccountBindingReconciler) handleWatchEvents(ctx context.Context, namespacedName types.NamespacedName, watcher watch.Interface) { + logger := log.FromContext(ctx) + defer watcher.Stop() + + for { + select { + case <-ctx.Done(): + return + case event, ok := <-watcher.ResultChan(): + if !ok { + logger.Info("Watch channel closed", "namespace", namespacedName.Namespace, "name", namespacedName.Name) + // Remove the watcher from the map + r.watcherMutex.Lock() + delete(r.watcherMap, namespacedName) + r.watcherMutex.Unlock() + return + } + + if event.Type == watch.Modified { + // Check if the object is a ServiceAccountBinding + _, ok := event.Object.(*cloudapi.ServiceAccountBinding) + if !ok { + logger.Error(fmt.Errorf("unexpected object type"), "Failed to convert object to ServiceAccountBinding") + continue + } + + // Get the local ServiceAccountBinding + localBinding := &resourcev1alpha1.ServiceAccountBinding{} + if err := r.Get(ctx, namespacedName, localBinding); err != nil { + logger.Error(err, "Failed to get local ServiceAccountBinding") + continue + } + + // Update status + r.updateServiceAccountBindingStatus(ctx, localBinding, nil, "Ready", "ServiceAccountBinding synced successfully") + } + } + } +} + +// setupWatch creates a new watcher for a ServiceAccountBinding +func (r *ServiceAccountBindingReconciler) setupWatch(ctx context.Context, binding *resourcev1alpha1.ServiceAccountBinding, bindingClient *controllers2.ServiceAccountBindingClient) error { + namespacedName := types.NamespacedName{ + Namespace: binding.Namespace, + Name: binding.Name, + } + + // Check if we already have a watcher + r.watcherMutex.RLock() + _, exists := r.watcherMap[namespacedName] + r.watcherMutex.RUnlock() + if exists { + return nil + } + + // Create new watcher + watcher, err := bindingClient.WatchServiceAccountBinding(ctx, binding.Name) + if err != nil { + return fmt.Errorf("failed to create watcher: %w", err) + } + + // Store watcher in map + r.watcherMutex.Lock() + r.watcherMap[namespacedName] = watcher + r.watcherMutex.Unlock() + + // Start watching in a new goroutine + go r.handleWatchEvents(ctx, namespacedName, watcher) + return nil +} + +// Reconcile handles the reconciliation of ServiceAccountBinding objects +func (r *ServiceAccountBindingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconciling ServiceAccountBinding", "namespace", req.Namespace, "name", req.Name) + + // Add requeue interval for status sync + requeueInterval := time.Minute + + // Get the ServiceAccountBinding resource + binding := &resourcev1alpha1.ServiceAccountBinding{} + if err := r.Get(ctx, req.NamespacedName, binding); err != nil { + if apierrors.IsNotFound(err) { + // Stop and remove watcher if it exists + r.watcherMutex.Lock() + if watcher, exists := r.watcherMap[req.NamespacedName]; exists { + watcher.Stop() + delete(r.watcherMap, req.NamespacedName) + } + r.watcherMutex.Unlock() + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + // Get the ServiceAccount + serviceAccount := &resourcev1alpha1.ServiceAccount{} + if err := r.Get(ctx, types.NamespacedName{ + Namespace: req.Namespace, + Name: binding.Spec.ServiceAccountName, + }, serviceAccount); err != nil { + r.updateServiceAccountBindingStatus(ctx, binding, err, "ServiceAccountNotFound", + fmt.Sprintf("Failed to get ServiceAccount: %v", err)) + return ctrl.Result{}, err + } + + // Get the APIServerConnection from the ServiceAccount + connection := &resourcev1alpha1.StreamNativeCloudConnection{} + if err := r.Get(ctx, types.NamespacedName{ + Namespace: req.Namespace, + Name: serviceAccount.Spec.APIServerRef.Name, + }, connection); err != nil { + r.updateServiceAccountBindingStatus(ctx, binding, err, "ConnectionNotFound", + fmt.Sprintf("Failed to get APIServerConnection: %v", err)) + return ctrl.Result{}, err + } + + // Get API connection + apiConn, err := r.ConnectionManager.GetOrCreateConnection(connection, nil) + if err != nil { + // If connection is not initialized, requeue the request + if _, ok := err.(*NotInitializedError); ok { + logger.Info("Connection not initialized, requeueing", "error", err.Error()) + return ctrl.Result{Requeue: true}, nil + } + r.updateServiceAccountBindingStatus(ctx, binding, err, "GetConnectionFailed", + fmt.Sprintf("Failed to get connection: %v", err)) + return ctrl.Result{}, err + } + + // Get organization from connection + organization := connection.Spec.Organization + if organization == "" { + err := fmt.Errorf("organization is required but not specified") + r.updateServiceAccountBindingStatus(ctx, binding, err, "ValidationFailed", err.Error()) + return ctrl.Result{}, err + } + + // Create ServiceAccountBinding client + bindingClient, err := controllers2.NewServiceAccountBindingClient(apiConn, organization) + if err != nil { + r.updateServiceAccountBindingStatus(ctx, binding, err, "ClientCreationFailed", + fmt.Sprintf("Failed to create ServiceAccountBinding client: %v", err)) + return ctrl.Result{}, err + } + + // Handle deletion + if !binding.DeletionTimestamp.IsZero() { + if controllers2.ContainsString(binding.Finalizers, ServiceAccountBindingFinalizer) { + // Try to delete remote ServiceAccountBinding + if err := bindingClient.DeleteServiceAccountBinding(ctx, binding); err != nil { + if !apierrors.IsNotFound(err) { + r.updateServiceAccountBindingStatus(ctx, binding, err, "DeleteFailed", + fmt.Sprintf("Failed to delete external resources: %v", err)) + return ctrl.Result{}, err + } + // If the resource is already gone, that's fine + logger.Info("Remote ServiceAccountBinding already deleted or not found", + "binding", binding.Name) + } + + // Remove finalizer after successful deletion + binding.Finalizers = controllers2.RemoveString(binding.Finalizers, ServiceAccountBindingFinalizer) + if err := r.Update(ctx, binding); err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil + } + + // Add finalizer if it doesn't exist + if !controllers2.ContainsString(binding.Finalizers, ServiceAccountBindingFinalizer) { + binding.Finalizers = append(binding.Finalizers, ServiceAccountBindingFinalizer) + if err := r.Update(ctx, binding); err != nil { + return ctrl.Result{}, err + } + } + + // Check if ServiceAccountBinding exists + existingBinding, err := bindingClient.GetServiceAccountBinding(ctx, binding.Name) + if err != nil { + logger.Info("Failed to get ServiceAccountBinding", "error", err, "existingBinding", existingBinding) + if !apierrors.IsNotFound(err) { + r.updateServiceAccountBindingStatus(ctx, binding, err, "GetServiceAccountBindingFailed", + fmt.Sprintf("Failed to get ServiceAccountBinding: %v", err)) + return ctrl.Result{}, client.IgnoreNotFound(err) + } + existingBinding = nil + } + + if existingBinding == nil { + // Create ServiceAccountBinding + _, err := bindingClient.CreateServiceAccountBinding(ctx, binding) + if err != nil { + r.updateServiceAccountBindingStatus(ctx, binding, err, "CreateServiceAccountBindingFailed", + fmt.Sprintf("Failed to create ServiceAccountBinding: %v", err)) + return ctrl.Result{}, err + } + + // Update status + r.updateServiceAccountBindingStatus(ctx, binding, nil, "Ready", "ServiceAccountBinding created successfully") + } else { + // Update ServiceAccountBinding + _, err := bindingClient.UpdateServiceAccountBinding(ctx, binding) + if err != nil { + r.updateServiceAccountBindingStatus(ctx, binding, err, "UpdateServiceAccountBindingFailed", + fmt.Sprintf("Failed to update ServiceAccountBinding: %v", err)) + return ctrl.Result{}, err + } + + // Update status + r.updateServiceAccountBindingStatus(ctx, binding, nil, "Ready", "ServiceAccountBinding updated successfully") + } + + // Setup watch after ServiceAccountBinding is created/updated + if err := r.setupWatch(ctx, binding, bindingClient); err != nil { + logger.Error(err, "Failed to setup watch") + // Don't return error, just log it + } + + return ctrl.Result{RequeueAfter: requeueInterval}, nil +} + +func (r *ServiceAccountBindingReconciler) updateServiceAccountBindingStatus( + ctx context.Context, + binding *resourcev1alpha1.ServiceAccountBinding, + err error, + reason string, + message string, +) { + // Create new condition + condition := metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionTrue, + Reason: reason, + Message: message, + ObservedGeneration: binding.Generation, + LastTransitionTime: metav1.Now(), + } + + if err != nil { + condition.Status = metav1.ConditionFalse + } + + // Create new conditions slice for comparison + newConditions := make([]metav1.Condition, 0) + for _, c := range binding.Status.Conditions { + if c.Type != condition.Type { + newConditions = append(newConditions, c) + } + } + newConditions = append(newConditions, condition) + + // Check if status has actually changed + if !controllers2.StatusHasChanged(binding.Status.Conditions, newConditions) { + return + } + + // Update conditions + binding.Status.Conditions = newConditions + + // Update observed generation + binding.Status.ObservedGeneration = binding.Generation + + // Update status + if err := r.Status().Update(ctx, binding); err != nil { + log.FromContext(ctx).Error(err, "Failed to update ServiceAccountBinding status") + } +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ServiceAccountBindingReconciler) SetupWithManager(mgr ctrl.Manager) error { + // Initialize the watcher map + r.watcherMap = make(map[types.NamespacedName]watch.Interface) + + return ctrl.NewControllerManagedBy(mgr). + For(&resourcev1alpha1.ServiceAccountBinding{}). + WithEventFilter(predicate.Or( + // Trigger on spec changes + predicate.GenerationChangedPredicate{}, + // Trigger periodically to sync status + predicate.NewPredicateFuncs(func(object client.Object) bool { + // Trigger every minute to sync status + return true + }), + )). + Complete(r) +} diff --git a/pkg/streamnativecloud/apikey_client.go b/pkg/streamnativecloud/apikey_client.go new file mode 100644 index 00000000..1c40e2c0 --- /dev/null +++ b/pkg/streamnativecloud/apikey_client.go @@ -0,0 +1,142 @@ +// Copyright 2025 StreamNative +// +// 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 streamnativecloud + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/rest" + + resourcev1alpha1 "github.com/streamnative/pulsar-resources-operator/api/v1alpha1" + cloudapi "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + cloudclient "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/clientset_generated/clientset" +) + +// APIKeyClient handles APIKey operations with the API server +type APIKeyClient struct { + client cloudclient.Interface + // organization is the organization to use for API calls + organization string +} + +// NewAPIKeyClient creates a new APIKey client +func NewAPIKeyClient(apiConn *APIConnection, organization string) (*APIKeyClient, error) { + if apiConn == nil || apiConn.config == nil || apiConn.client == nil { + return nil, fmt.Errorf("api connection is nil") + } + if !apiConn.IsInitialized() { + return nil, fmt.Errorf("api connection is not initialized") + } + // Create REST config + config := &rest.Config{ + Host: apiConn.config.Spec.Server, + Transport: apiConn.client.Transport, + } + + // Create client + client, err := cloudclient.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create client: %w", err) + } + + return &APIKeyClient{ + client: client, + organization: organization, + }, nil +} + +// WatchAPIKey watches an APIKey by name +func (c *APIKeyClient) WatchAPIKey(ctx context.Context, name string) (watch.Interface, error) { + // Use ListOptions to watch a single object + opts := metav1.ListOptions{ + FieldSelector: fmt.Sprintf("metadata.name=%s", name), + Watch: true, + } + return c.client.CloudV1alpha1().APIKeys(c.organization).Watch(ctx, opts) +} + +// GetAPIKey gets an APIKey by name +func (c *APIKeyClient) GetAPIKey(ctx context.Context, name string) (*cloudapi.APIKey, error) { + return c.client.CloudV1alpha1().APIKeys(c.organization).Get(ctx, name, metav1.GetOptions{}) +} + +// convertToCloudAPIKey converts a local APIKey to a cloud APIKey +func convertToCloudAPIKey(apiKey *resourcev1alpha1.APIKey) *cloudapi.APIKey { + // Convert to cloud API type + cloudAPIKey := &cloudapi.APIKey{ + ObjectMeta: metav1.ObjectMeta{ + Name: apiKey.Name, + }, + Spec: cloudapi.APIKeySpec{}, + } + + // Add fields from our APIKey CRD + if apiKey.Spec.InstanceName != "" { + cloudAPIKey.Spec.InstanceName = apiKey.Spec.InstanceName + } + if apiKey.Spec.ServiceAccountName != "" { + cloudAPIKey.Spec.ServiceAccountName = apiKey.Spec.ServiceAccountName + } + if apiKey.Spec.Description != "" { + cloudAPIKey.Spec.Description = apiKey.Spec.Description + } + if apiKey.Spec.ExpirationTime != nil { + cloudAPIKey.Spec.ExpirationTime = apiKey.Spec.ExpirationTime + } + cloudAPIKey.Spec.Revoke = apiKey.Spec.Revoke + + // Copy encryption key if provided + if apiKey.Spec.EncryptionKey != nil { + cloudAPIKey.Spec.EncryptionKey = &cloudapi.EncryptionKey{ + PEM: apiKey.Spec.EncryptionKey.PEM, + JWK: apiKey.Spec.EncryptionKey.JWK, + } + } + + return cloudAPIKey +} + +// CreateAPIKey creates a new APIKey +func (c *APIKeyClient) CreateAPIKey(ctx context.Context, apiKey *resourcev1alpha1.APIKey) (*cloudapi.APIKey, error) { + cloudAPIKey := convertToCloudAPIKey(apiKey) + + // Create APIKey + return c.client.CloudV1alpha1().APIKeys(c.organization).Create(ctx, cloudAPIKey, metav1.CreateOptions{}) +} + +// UpdateAPIKey updates an existing APIKey +func (c *APIKeyClient) UpdateAPIKey(ctx context.Context, apiKey *resourcev1alpha1.APIKey) (*cloudapi.APIKey, error) { + // Get existing APIKey + existing, err := c.GetAPIKey(ctx, apiKey.Name) + if err != nil { + return nil, err + } + + // Create updated version + updated := convertToCloudAPIKey(apiKey) + // Preserve ResourceVersion for optimistic concurrency + updated.ResourceVersion = existing.ResourceVersion + + // Update APIKey + return c.client.CloudV1alpha1().APIKeys(c.organization).Update(ctx, updated, metav1.UpdateOptions{}) +} + +// DeleteAPIKey deletes an APIKey by name +func (c *APIKeyClient) DeleteAPIKey(ctx context.Context, apiKey *resourcev1alpha1.APIKey) error { + return c.client.CloudV1alpha1().APIKeys(c.organization).Delete(ctx, apiKey.Name, metav1.DeleteOptions{}) +} diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go index 3f22b44b..6aba1cee 100644 --- a/pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go @@ -78,11 +78,11 @@ type EncryptionKey struct { // APIKeyStatus defines the observed state of ServiceAccount type APIKeyStatus struct { // ObservedGeneration is the most recent generation observed by the controller. - //ObservedGeneration int64 `json:"observedGeneration" protobuf:"varint,1,opt,name=observedGeneration"` + // ObservedGeneration int64 `json:"observedGeneration" protobuf:"varint,1,opt,name=observedGeneration"` - // KeyId is a generated field that is a uid for the token + // KeyID is a generated field that is a uid for the token // +optional - KeyId *string `json:"keyId,omitempty" protobuf:"bytes,1,opt,name=keyId"` + KeyID *string `json:"keyId,omitempty" protobuf:"bytes,1,opt,name=keyId"` // IssuedAt is a timestamp of when the key was issued, stored as an epoch in seconds // +optional @@ -122,14 +122,11 @@ type EncryptedToken struct { JWE *string `json:"jwe,omitempty" protobuf:"bytes,7,opt,name=jwe"` } -type APIKeyConditionType string +//+kubebuilder:object:root=true -// These are valid conditions of the API Key. -const ( - // APIKeyIssued means the api key is ready for use. - APIKeyIssued APIKeyConditionType = "Issued" - // APIKeyExpired means the key has expired. - APIKeyExpired APIKeyConditionType = "Expired" - // APIKeyRevoked means the api keys has manually been revoked. - APIKeyRevoked APIKeyConditionType = "Revoked" -) +// APIKeyList contains a list of APIKey +type APIKeyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []APIKey `json:"items"` +} diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go index 9b5e5bed..e56eb312 100644 --- a/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go @@ -56,3 +56,12 @@ type ServiceAccountStatus struct { // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,3,rep,name=conditions"` } + +//+kubebuilder:object:root=true + +// ServiceAccountList contains a list of ServiceAccount +type ServiceAccountList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ServiceAccount `json:"items"` +} diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go index 40aec567..401ed60b 100644 --- a/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go @@ -52,3 +52,12 @@ type ServiceAccountBindingStatus struct { // +listMapKey=type Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` } + +//+kubebuilder:object:root=true + +// ServiceAccountBindingList contains a list of ServiceAccountBinding +type ServiceAccountBindingList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ServiceAccountBinding `json:"items"` +} diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.api.register.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.api.register.go index 7abd1a85..f8b1ea16 100644 --- a/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.api.register.go +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.api.register.go @@ -59,10 +59,13 @@ func init() { func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &APIKey{}, + &APIKeyList{}, &Secret{}, &SecretList{}, &ServiceAccount{}, &ServiceAccountBinding{}, + &ServiceAccountBindingList{}, + &ServiceAccountList{}, ) // AddToGroupVersion allows the serialization of client types like ListOptions. v1.AddToGroupVersion(scheme, SchemeGroupVersion) diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.deepcopy.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.deepcopy.go index 563bcbb4..ce34d143 100644 --- a/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/zz_generated.deepcopy.go @@ -51,6 +51,38 @@ func (in *APIKey) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIKeyList) DeepCopyInto(out *APIKeyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]APIKey, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIKeyList. +func (in *APIKeyList) DeepCopy() *APIKeyList { + if in == nil { + return nil + } + out := new(APIKeyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *APIKeyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *APIKeySpec) DeepCopyInto(out *APIKeySpec) { *out = *in @@ -78,8 +110,8 @@ func (in *APIKeySpec) DeepCopy() *APIKeySpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *APIKeyStatus) DeepCopyInto(out *APIKeyStatus) { *out = *in - if in.KeyId != nil { - in, out := &in.KeyId, &out.KeyId + if in.KeyID != nil { + in, out := &in.KeyID, &out.KeyID *out = new(string) **out = **in } @@ -667,6 +699,38 @@ func (in *ServiceAccountBinding) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountBindingList) DeepCopyInto(out *ServiceAccountBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ServiceAccountBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountBindingList. +func (in *ServiceAccountBindingList) DeepCopy() *ServiceAccountBindingList { + if in == nil { + return nil + } + out := new(ServiceAccountBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceAccountBindingList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceAccountBindingSpec) DeepCopyInto(out *ServiceAccountBindingSpec) { *out = *in @@ -705,6 +769,38 @@ func (in *ServiceAccountBindingStatus) DeepCopy() *ServiceAccountBindingStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceAccountList) DeepCopyInto(out *ServiceAccountList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ServiceAccount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountList. +func (in *ServiceAccountList) DeepCopy() *ServiceAccountList { + if in == nil { + return nil + } + out := new(ServiceAccountList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceAccountList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceAccountSpec) DeepCopyInto(out *ServiceAccountSpec) { *out = *in diff --git a/pkg/streamnativecloud/serviceaccount_client.go b/pkg/streamnativecloud/serviceaccount_client.go new file mode 100644 index 00000000..39cfcdcc --- /dev/null +++ b/pkg/streamnativecloud/serviceaccount_client.go @@ -0,0 +1,128 @@ +// Copyright 2025 StreamNative +// +// 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 streamnativecloud + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/rest" + + resourcev1alpha1 "github.com/streamnative/pulsar-resources-operator/api/v1alpha1" + cloudapi "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + cloudclient "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/clientset_generated/clientset" +) + +// ServiceAccountClient handles ServiceAccount operations with the API server +type ServiceAccountClient struct { + client cloudclient.Interface + // organization is the organization to use for API calls + organization string +} + +// NewServiceAccountClient creates a new ServiceAccount client +func NewServiceAccountClient(apiConn *APIConnection, organization string) (*ServiceAccountClient, error) { + if apiConn == nil || apiConn.config == nil || apiConn.client == nil { + return nil, fmt.Errorf("api connection is nil") + } + if !apiConn.IsInitialized() { + return nil, fmt.Errorf("api connection is not initialized") + } + // Create REST config + config := &rest.Config{ + Host: apiConn.config.Spec.Server, + Transport: apiConn.client.Transport, + } + + // Create client + client, err := cloudclient.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create client: %w", err) + } + + return &ServiceAccountClient{ + client: client, + organization: organization, + }, nil +} + +// WatchServiceAccount watches a ServiceAccount by name +func (c *ServiceAccountClient) WatchServiceAccount(ctx context.Context, name string) (watch.Interface, error) { + // Use ListOptions to watch a single object + opts := metav1.ListOptions{ + FieldSelector: fmt.Sprintf("metadata.name=%s", name), + Watch: true, + } + return c.client.CloudV1alpha1().ServiceAccounts(c.organization).Watch(ctx, opts) +} + +// GetServiceAccount gets a ServiceAccount by name +func (c *ServiceAccountClient) GetServiceAccount(ctx context.Context, name string) (*cloudapi.ServiceAccount, error) { + return c.client.CloudV1alpha1().ServiceAccounts(c.organization).Get(ctx, name, metav1.GetOptions{}) +} + +// convertToCloudServiceAccount converts a local ServiceAccount to a cloud ServiceAccount +func convertToCloudServiceAccount(sa *resourcev1alpha1.ServiceAccount) *cloudapi.ServiceAccount { + // Convert to cloud API type + cloudSA := &cloudapi.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: sa.Name, + }, + Spec: cloudapi.ServiceAccountSpec{}, + } + + // Add fields from our ServiceAccount CRD + // The cloud ServiceAccount spec is currently empty, but we may need to add fields here + // if the cloud API requires specific fields in the future + + return cloudSA +} + +// CreateServiceAccount creates a new ServiceAccount +func (c *ServiceAccountClient) CreateServiceAccount(ctx context.Context, sa *resourcev1alpha1.ServiceAccount) (*cloudapi.ServiceAccount, error) { + cloudSA := convertToCloudServiceAccount(sa) + + // Create ServiceAccount + created, err := c.client.CloudV1alpha1().ServiceAccounts(c.organization).Create(ctx, cloudSA, metav1.CreateOptions{}) + if err != nil { + return nil, err + } + + return created, nil +} + +// UpdateServiceAccount updates an existing ServiceAccount +func (c *ServiceAccountClient) UpdateServiceAccount(ctx context.Context, sa *resourcev1alpha1.ServiceAccount) (*cloudapi.ServiceAccount, error) { + // Get existing ServiceAccount + existing, err := c.GetServiceAccount(ctx, sa.Name) + if err != nil { + return nil, err + } + + // Create updated version + updated := convertToCloudServiceAccount(sa) + // Preserve ResourceVersion for optimistic concurrency + updated.ResourceVersion = existing.ResourceVersion + + // Update ServiceAccount + return c.client.CloudV1alpha1().ServiceAccounts(c.organization).Update(ctx, updated, metav1.UpdateOptions{}) +} + +// DeleteServiceAccount deletes a ServiceAccount by name +func (c *ServiceAccountClient) DeleteServiceAccount(ctx context.Context, sa *resourcev1alpha1.ServiceAccount) error { + return c.client.CloudV1alpha1().ServiceAccounts(c.organization).Delete(ctx, sa.Name, metav1.DeleteOptions{}) +} diff --git a/pkg/streamnativecloud/serviceaccountbinding_client.go b/pkg/streamnativecloud/serviceaccountbinding_client.go new file mode 100644 index 00000000..dae641be --- /dev/null +++ b/pkg/streamnativecloud/serviceaccountbinding_client.go @@ -0,0 +1,130 @@ +// Copyright 2025 StreamNative +// +// 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 streamnativecloud + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/rest" + + resourcev1alpha1 "github.com/streamnative/pulsar-resources-operator/api/v1alpha1" + cloudapi "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/apis/cloud/v1alpha1" + cloudclient "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud/client/clientset_generated/clientset" +) + +// ServiceAccountBindingClient handles ServiceAccountBinding operations with the API server +type ServiceAccountBindingClient struct { + client cloudclient.Interface + // organization is the organization to use for API calls + organization string +} + +// NewServiceAccountBindingClient creates a new ServiceAccountBinding client +func NewServiceAccountBindingClient(apiConn *APIConnection, organization string) (*ServiceAccountBindingClient, error) { + if apiConn == nil || apiConn.config == nil || apiConn.client == nil { + return nil, fmt.Errorf("api connection is nil") + } + if !apiConn.IsInitialized() { + return nil, fmt.Errorf("api connection is not initialized") + } + // Create REST config + config := &rest.Config{ + Host: apiConn.config.Spec.Server, + Transport: apiConn.client.Transport, + } + + // Create client + client, err := cloudclient.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create client: %w", err) + } + + return &ServiceAccountBindingClient{ + client: client, + organization: organization, + }, nil +} + +// WatchServiceAccountBinding watches a ServiceAccountBinding by name +func (c *ServiceAccountBindingClient) WatchServiceAccountBinding(ctx context.Context, name string) (watch.Interface, error) { + // Use ListOptions to watch a single object + opts := metav1.ListOptions{ + FieldSelector: fmt.Sprintf("metadata.name=%s", name), + Watch: true, + } + return c.client.CloudV1alpha1().ServiceAccountBindings(c.organization).Watch(ctx, opts) +} + +// GetServiceAccountBinding gets a ServiceAccountBinding by name +func (c *ServiceAccountBindingClient) GetServiceAccountBinding(ctx context.Context, name string) (*cloudapi.ServiceAccountBinding, error) { + return c.client.CloudV1alpha1().ServiceAccountBindings(c.organization).Get(ctx, name, metav1.GetOptions{}) +} + +// convertToCloudServiceAccountBinding converts a local ServiceAccountBinding to a cloud ServiceAccountBinding +func convertToCloudServiceAccountBinding(binding *resourcev1alpha1.ServiceAccountBinding) *cloudapi.ServiceAccountBinding { + // Convert to cloud API type + cloudBinding := &cloudapi.ServiceAccountBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: binding.Name, + }, + Spec: cloudapi.ServiceAccountBindingSpec{}, + } + + // Add fields from our ServiceAccountBinding CRD + if binding.Spec.ServiceAccountName != "" { + cloudBinding.Spec.ServiceAccountName = binding.Spec.ServiceAccountName + } + + // Convert PoolMemberRef + cloudBinding.Spec.PoolMemberRef = cloudapi.PoolMemberReference{ + Namespace: binding.Spec.PoolMemberRef.Namespace, + Name: binding.Spec.PoolMemberRef.Name, + } + + return cloudBinding +} + +// CreateServiceAccountBinding creates a new ServiceAccountBinding +func (c *ServiceAccountBindingClient) CreateServiceAccountBinding(ctx context.Context, binding *resourcev1alpha1.ServiceAccountBinding) (*cloudapi.ServiceAccountBinding, error) { + cloudBinding := convertToCloudServiceAccountBinding(binding) + + // Create ServiceAccountBinding + return c.client.CloudV1alpha1().ServiceAccountBindings(c.organization).Create(ctx, cloudBinding, metav1.CreateOptions{}) +} + +// UpdateServiceAccountBinding updates an existing ServiceAccountBinding +func (c *ServiceAccountBindingClient) UpdateServiceAccountBinding(ctx context.Context, binding *resourcev1alpha1.ServiceAccountBinding) (*cloudapi.ServiceAccountBinding, error) { + // Get existing ServiceAccountBinding + existing, err := c.GetServiceAccountBinding(ctx, binding.Name) + if err != nil { + return nil, err + } + + // Create updated version + updated := convertToCloudServiceAccountBinding(binding) + // Preserve ResourceVersion for optimistic concurrency + updated.ResourceVersion = existing.ResourceVersion + + // Update ServiceAccountBinding + return c.client.CloudV1alpha1().ServiceAccountBindings(c.organization).Update(ctx, updated, metav1.UpdateOptions{}) +} + +// DeleteServiceAccountBinding deletes a ServiceAccountBinding by name +func (c *ServiceAccountBindingClient) DeleteServiceAccountBinding(ctx context.Context, binding *resourcev1alpha1.ServiceAccountBinding) error { + return c.client.CloudV1alpha1().ServiceAccountBindings(c.organization).Delete(ctx, binding.Name, metav1.DeleteOptions{}) +} From 1af15847dc1df0124a2b2f451df992cdb2c95087 Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Mon, 14 Apr 2025 18:41:29 +0800 Subject: [PATCH 3/6] add to manager --- api/v1alpha1/apikey_types.go | 16 +- api/v1alpha1/serviceaccount_types.go | 8 - config/rbac/apikey_editor_role.yaml | 38 + config/rbac/apikey_viewer_role.yaml | 34 + config/rbac/role.yaml | 78 ++ config/rbac/serviceaccount_editor_role.yaml | 38 + config/rbac/serviceaccount_viewer_role.yaml | 34 + .../serviceaccountbinding_editor_role.yaml | 38 + .../serviceaccountbinding_viewer_role.yaml | 34 + controllers/apikey_controller.go | 242 ++++-- controllers/serviceaccount_controller.go | 162 ++-- docs/apikey.md | 131 +++ docs/serviceaccount.md | 111 +++ docs/serviceaccountbinding.md | 73 ++ go.mod | 4 + go.sum | 8 + go.work.sum | 795 +----------------- main.go | 31 + pkg/crypto/jwe.go | 45 + pkg/crypto/rsa.go | 91 ++ pkg/streamnativecloud/apikey_client.go | 38 +- .../serviceaccountbinding_client.go | 10 +- pkg/utils/secrets.go | 162 ++++ 23 files changed, 1268 insertions(+), 953 deletions(-) create mode 100644 config/rbac/apikey_editor_role.yaml create mode 100644 config/rbac/apikey_viewer_role.yaml create mode 100644 config/rbac/serviceaccount_editor_role.yaml create mode 100644 config/rbac/serviceaccount_viewer_role.yaml create mode 100644 config/rbac/serviceaccountbinding_editor_role.yaml create mode 100644 config/rbac/serviceaccountbinding_viewer_role.yaml create mode 100644 docs/apikey.md create mode 100644 docs/serviceaccount.md create mode 100644 docs/serviceaccountbinding.md create mode 100644 pkg/crypto/jwe.go create mode 100644 pkg/crypto/rsa.go create mode 100644 pkg/utils/secrets.go diff --git a/api/v1alpha1/apikey_types.go b/api/v1alpha1/apikey_types.go index 03f3adee..3ac00928 100644 --- a/api/v1alpha1/apikey_types.go +++ b/api/v1alpha1/apikey_types.go @@ -42,27 +42,21 @@ type APIKeySpec struct { // +optional ExpirationTime *metav1.Time `json:"expirationTime,omitempty"` - // Revoke is a boolean that defines if the token of this API key should be revoked - // +kubebuilder:default=false + // Revoke indicates whether this API key should be revoked // +optional Revoke bool `json:"revoke,omitempty"` - // EncryptionKey is a public key to encrypt the API Key token - // Please provide an RSA key with modulus length of at least 2048 bits + // EncryptionKey contains the public key used to encrypt the token // +optional EncryptionKey *EncryptionKey `json:"encryptionKey,omitempty"` } -// EncryptionKey specifies a public key for encryption purposes -// Either a PEM or JWK must be specified +// EncryptionKey contains a public key used for encryption +// +structType=atomic type EncryptionKey struct { - // PEM is a PEM-encoded public key in PKIX, ASN.1 DER form ("spki" format) + // PEM is the public key in PEM format // +optional PEM string `json:"pem,omitempty"` - - // JWK is a JWK-encoded public key - // +optional - JWK string `json:"jwk,omitempty"` } // APIKeyStatus defines the observed state of APIKey diff --git a/api/v1alpha1/serviceaccount_types.go b/api/v1alpha1/serviceaccount_types.go index f4838f29..1a29e124 100644 --- a/api/v1alpha1/serviceaccount_types.go +++ b/api/v1alpha1/serviceaccount_types.go @@ -24,14 +24,6 @@ type ServiceAccountSpec struct { // APIServerRef is the reference to the StreamNativeCloudConnection // +required APIServerRef corev1.LocalObjectReference `json:"apiServerRef"` - - // Description is a user defined description of the service account - // +optional - Description string `json:"description,omitempty"` - - // InstanceName is the name of the instance this service account is for - // +optional - InstanceName string `json:"instanceName,omitempty"` } // ServiceAccountStatus defines the observed state of ServiceAccount diff --git a/config/rbac/apikey_editor_role.yaml b/config/rbac/apikey_editor_role.yaml new file mode 100644 index 00000000..db317fc5 --- /dev/null +++ b/config/rbac/apikey_editor_role.yaml @@ -0,0 +1,38 @@ +# Copyright 2025 StreamNative +# +# 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. + +# permissions for end users to edit apikeys. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: apikey-editor-role +rules: +- apiGroups: + - resource.streamnative.io + resources: + - apikeys + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - resource.streamnative.io + resources: + - apikeys/status + verbs: + - get \ No newline at end of file diff --git a/config/rbac/apikey_viewer_role.yaml b/config/rbac/apikey_viewer_role.yaml new file mode 100644 index 00000000..14611977 --- /dev/null +++ b/config/rbac/apikey_viewer_role.yaml @@ -0,0 +1,34 @@ +# Copyright 2025 StreamNative +# +# 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. + +# permissions for end users to view apikeys. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: apikey-viewer-role +rules: +- apiGroups: + - resource.streamnative.io + resources: + - apikeys + verbs: + - get + - list + - watch +- apiGroups: + - resource.streamnative.io + resources: + - apikeys/status + verbs: + - get \ No newline at end of file diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index b0a8e97b..3c72115e 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -428,3 +428,81 @@ rules: - get - patch - update +- apiGroups: + - resource.streamnative.io + resources: + - apikeys + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - resource.streamnative.io + resources: + - apikeys/finalizers + verbs: + - update +- apiGroups: + - resource.streamnative.io + resources: + - apikeys/status + verbs: + - get + - patch + - update +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccounts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccounts/finalizers + verbs: + - update +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccounts/status + verbs: + - get + - patch + - update +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccountbindings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccountbindings/finalizers + verbs: + - update +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccountbindings/status + verbs: + - get + - patch + - update diff --git a/config/rbac/serviceaccount_editor_role.yaml b/config/rbac/serviceaccount_editor_role.yaml new file mode 100644 index 00000000..72e14002 --- /dev/null +++ b/config/rbac/serviceaccount_editor_role.yaml @@ -0,0 +1,38 @@ +# Copyright 2025 StreamNative +# +# 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. + +# permissions for end users to edit serviceaccounts. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: serviceaccount-editor-role +rules: +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccounts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccounts/status + verbs: + - get \ No newline at end of file diff --git a/config/rbac/serviceaccount_viewer_role.yaml b/config/rbac/serviceaccount_viewer_role.yaml new file mode 100644 index 00000000..284fb1c5 --- /dev/null +++ b/config/rbac/serviceaccount_viewer_role.yaml @@ -0,0 +1,34 @@ +# Copyright 2025 StreamNative +# +# 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. + +# permissions for end users to view serviceaccounts. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: serviceaccount-viewer-role +rules: +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccounts + verbs: + - get + - list + - watch +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccounts/status + verbs: + - get \ No newline at end of file diff --git a/config/rbac/serviceaccountbinding_editor_role.yaml b/config/rbac/serviceaccountbinding_editor_role.yaml new file mode 100644 index 00000000..80185690 --- /dev/null +++ b/config/rbac/serviceaccountbinding_editor_role.yaml @@ -0,0 +1,38 @@ +# Copyright 2025 StreamNative +# +# 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. + +# permissions for end users to edit serviceaccountbindings. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: serviceaccountbinding-editor-role +rules: +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccountbindings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccountbindings/status + verbs: + - get \ No newline at end of file diff --git a/config/rbac/serviceaccountbinding_viewer_role.yaml b/config/rbac/serviceaccountbinding_viewer_role.yaml new file mode 100644 index 00000000..4ff56aaf --- /dev/null +++ b/config/rbac/serviceaccountbinding_viewer_role.yaml @@ -0,0 +1,34 @@ +# Copyright 2025 StreamNative +# +# 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. + +# permissions for end users to view serviceaccountbindings. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: serviceaccountbinding-viewer-role +rules: +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccountbindings + verbs: + - get + - list + - watch +- apiGroups: + - resource.streamnative.io + resources: + - serviceaccountbindings/status + verbs: + - get \ No newline at end of file diff --git a/controllers/apikey_controller.go b/controllers/apikey_controller.go index 6eb593f8..af803037 100644 --- a/controllers/apikey_controller.go +++ b/controllers/apikey_controller.go @@ -16,12 +16,18 @@ package controllers import ( "context" + "crypto/rsa" "fmt" "sync" "time" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwe" + "github.com/pkg/errors" resourcev1alpha1 "github.com/streamnative/pulsar-resources-operator/api/v1alpha1" + "github.com/streamnative/pulsar-resources-operator/pkg/crypto" controllers2 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud" + "github.com/streamnative/pulsar-resources-operator/pkg/utils" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -53,6 +59,7 @@ const APIKeyFinalizer = "apikey.resource.streamnative.io/finalizer" //+kubebuilder:rbac:groups=resource.streamnative.io,resources=apikeys/status,verbs=get;update;patch //+kubebuilder:rbac:groups=resource.streamnative.io,resources=apikeys/finalizers,verbs=update //+kubebuilder:rbac:groups=resource.streamnative.io,resources=streamnativecloudconnections,verbs=get;list;watch +//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete // handleWatchEvents processes events from the watch interface func (r *APIKeyReconciler) handleWatchEvents(ctx context.Context, namespacedName types.NamespacedName, watcher watch.Interface) { @@ -75,7 +82,7 @@ func (r *APIKeyReconciler) handleWatchEvents(ctx context.Context, namespacedName if event.Type == watch.Modified { // Check if the object is an APIKey - _, ok := event.Object.(*cloudapi.APIKey) + cloudAPIKey, ok := event.Object.(*cloudapi.APIKey) if !ok { logger.Error(fmt.Errorf("unexpected object type"), "Failed to convert object to APIKey") continue @@ -90,11 +97,62 @@ func (r *APIKeyReconciler) handleWatchEvents(ctx context.Context, namespacedName // Update status r.updateAPIKeyStatus(ctx, localAPIKey, nil, "Ready", "APIKey synced successfully") + + // Process encrypted token and update Secret if needed + if cloudAPIKey.Status.EncryptedToken != nil && cloudAPIKey.Status.EncryptedToken.JWE != nil { + r.processEncryptedToken(ctx, localAPIKey, cloudAPIKey) + } } } } } +// processEncryptedToken handles decrypting the token and storing it in a Secret +func (r *APIKeyReconciler) processEncryptedToken(ctx context.Context, localAPIKey *resourcev1alpha1.APIKey, cloudAPIKey *cloudapi.APIKey) { + logger := log.FromContext(ctx) + + // Skip if we don't have an encryption key in our local APIKey + if localAPIKey.Spec.EncryptionKey == nil || localAPIKey.Spec.EncryptionKey.PEM == "" { + logger.Info("No encryption key found in local APIKey, skipping token processing") + return + } + + // Get private key from secret + privateKeyStr, err := utils.GetPrivateKeyFromSecret(ctx, r.Client, localAPIKey.Namespace, localAPIKey.Name) + if err != nil { + logger.Error(err, "Failed to get private key from secret") + return + } + + // Import private key + privateKey, err := crypto.ImportPrivateKeyFromPEM(privateKeyStr) + if err != nil { + logger.Error(err, "Failed to import private key") + return + } + + // Decrypt token + jweToken := *cloudAPIKey.Status.EncryptedToken.JWE + token, err := crypto.DecryptJWEToken(jweToken, privateKey) + if err != nil { + logger.Error(err, "Failed to decrypt token") + return + } + + // Store token in secret + var keyID string + if cloudAPIKey.Status.KeyID != nil { + keyID = *cloudAPIKey.Status.KeyID + } + + if err := utils.CreateOrUpdateTokenSecret(ctx, r.Client, localAPIKey, localAPIKey.Namespace, localAPIKey.Name, token, keyID); err != nil { + logger.Error(err, "Failed to create or update token secret") + return + } + + logger.Info("Successfully processed encrypted token and stored in secret") +} + // setupWatch creates a new watcher for an APIKey func (r *APIKeyReconciler) setupWatch(ctx context.Context, apiKey *resourcev1alpha1.APIKey, apiKeyClient *controllers2.APIKeyClient) error { namespacedName := types.NamespacedName{ @@ -235,6 +293,41 @@ func (r *APIKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } if existingAPIKey == nil { + // Generate RSA key pair for token encryption + privateKey, err := crypto.GenerateRSAKeyPair() + if err != nil { + r.updateAPIKeyStatus(ctx, apiKey, err, "KeyGenerationFailed", + fmt.Sprintf("Failed to generate RSA key pair: %v", err)) + return ctrl.Result{}, err + } + + // Export public key in PEM format + publicKeyPEM, err := crypto.ExportPublicKeyAsPEM(privateKey) + if err != nil { + r.updateAPIKeyStatus(ctx, apiKey, err, "KeyExportFailed", + fmt.Sprintf("Failed to export public key: %v", err)) + return ctrl.Result{}, err + } + + // Store private key in Secret + privateKeyPEM := crypto.ExportPrivateKeyAsPEM(privateKey) + if err := utils.CreateOrUpdatePrivateKeySecret(ctx, r.Client, apiKey, apiKey.Namespace, apiKey.Name, privateKeyPEM); err != nil { + r.updateAPIKeyStatus(ctx, apiKey, err, "SecretCreationFailed", + fmt.Sprintf("Failed to create private key secret: %v", err)) + return ctrl.Result{}, err + } + + // Add encryption key to spec + if apiKey.Spec.EncryptionKey == nil { + apiKey.Spec.EncryptionKey = &resourcev1alpha1.EncryptionKey{} + } + apiKey.Spec.EncryptionKey.PEM = publicKeyPEM + + // Update APIKey with encryption key + if err := r.Update(ctx, apiKey); err != nil { + return ctrl.Result{}, err + } + // Create APIKey resultAPIKey, err := apiKeyClient.CreateAPIKey(ctx, apiKey) if err != nil { @@ -244,62 +337,54 @@ func (r *APIKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } // Update status with token information - if resultAPIKey.Status.Token != nil { - apiKey.Status.Token = resultAPIKey.Status.Token - } - if resultAPIKey.Status.KeyID != nil { - apiKey.Status.KeyID = resultAPIKey.Status.KeyID - } - if resultAPIKey.Status.IssuedAt != nil { - apiKey.Status.IssuedAt = resultAPIKey.Status.IssuedAt + if resultAPIKey.Status.EncryptedToken != nil && resultAPIKey.Status.EncryptedToken.JWE != nil { + // Process the encrypted token + r.processEncryptedToken(ctx, apiKey, resultAPIKey) } - if resultAPIKey.Status.ExpiresAt != nil { - apiKey.Status.ExpiresAt = resultAPIKey.Status.ExpiresAt - } - if resultAPIKey.Status.EncryptedToken != nil { - apiKey.Status.EncryptedToken = &resourcev1alpha1.EncryptedToken{ - JWE: resultAPIKey.Status.EncryptedToken.JWE, - } + + // Set up watch for APIKey + if err := r.setupWatch(ctx, apiKey, apiKeyClient); err != nil { + logger.Error(err, "Failed to set up watch", "apiKey", apiKey.Name) } // Update status r.updateAPIKeyStatus(ctx, apiKey, nil, "Ready", "APIKey created successfully") - } else { - // Update APIKey - resultAPIKey, err := apiKeyClient.UpdateAPIKey(ctx, apiKey) - if err != nil { - r.updateAPIKeyStatus(ctx, apiKey, err, "UpdateAPIKeyFailed", + return ctrl.Result{RequeueAfter: requeueInterval}, nil + } + + // Update revocation status if needed + if apiKey.Spec.Revoke != existingAPIKey.Spec.Revoke { + // Create a new local copy with updated values + updatedAPIKey := apiKey.DeepCopy() + if err := r.Update(ctx, updatedAPIKey); err != nil { + r.updateAPIKeyStatus(ctx, apiKey, err, "UpdateFailed", fmt.Sprintf("Failed to update APIKey: %v", err)) return ctrl.Result{}, err } + } - // Update status with token information - if resultAPIKey.Status.KeyID != nil { - apiKey.Status.KeyID = resultAPIKey.Status.KeyID - } - if resultAPIKey.Status.IssuedAt != nil { - apiKey.Status.IssuedAt = resultAPIKey.Status.IssuedAt - } - if resultAPIKey.Status.ExpiresAt != nil { - apiKey.Status.ExpiresAt = resultAPIKey.Status.ExpiresAt - } - if resultAPIKey.Status.RevokedAt != nil { - apiKey.Status.RevokedAt = resultAPIKey.Status.RevokedAt + // Update description if needed + if apiKey.Spec.Description != existingAPIKey.Spec.Description { + // Create a new local copy with updated values + updatedAPIKey := apiKey.DeepCopy() + if err := r.Update(ctx, updatedAPIKey); err != nil { + r.updateAPIKeyStatus(ctx, apiKey, err, "UpdateFailed", + fmt.Sprintf("Failed to update APIKey: %v", err)) + return ctrl.Result{}, err } - - // Update status - r.updateAPIKeyStatus(ctx, apiKey, nil, "Ready", "APIKey updated successfully") } - // Setup watch after APIKey is created/updated + // Set up watch for APIKey if err := r.setupWatch(ctx, apiKey, apiKeyClient); err != nil { - logger.Error(err, "Failed to setup watch") - // Don't return error, just log it + logger.Error(err, "Failed to set up watch", "apiKey", apiKey.Name) } + // Update status + r.updateAPIKeyStatus(ctx, apiKey, nil, "Ready", "APIKey synced successfully") return ctrl.Result{RequeueAfter: requeueInterval}, nil } +// updateAPIKeyStatus updates the status of the ApiKey resource func (r *APIKeyReconciler) updateAPIKeyStatus( ctx context.Context, apiKey *resourcev1alpha1.APIKey, @@ -307,61 +392,70 @@ func (r *APIKeyReconciler) updateAPIKeyStatus( reason string, message string, ) { - // Create new condition - condition := metav1.Condition{ + logger := log.FromContext(ctx) + apiKey.Status.ObservedGeneration = apiKey.Generation + + // Set ready condition based on error + meta := metav1.Now() + readyCondition := metav1.Condition{ Type: "Ready", Status: metav1.ConditionTrue, Reason: reason, Message: message, - ObservedGeneration: apiKey.Generation, - LastTransitionTime: metav1.Now(), + LastTransitionTime: meta, } if err != nil { - condition.Status = metav1.ConditionFalse + readyCondition.Status = metav1.ConditionFalse + logger.Error(err, "APIKey reconciliation failed", + "reason", reason, "message", message) } - // Create new conditions slice for comparison - newConditions := make([]metav1.Condition, 0) - for _, c := range apiKey.Status.Conditions { - if c.Type != condition.Type { - newConditions = append(newConditions, c) + // Update ready condition + meta = metav1.Now() + if len(apiKey.Status.Conditions) == 0 { + apiKey.Status.Conditions = []metav1.Condition{readyCondition} + } else { + for i, condition := range apiKey.Status.Conditions { + if condition.Type == "Ready" { + // Only update time if status changes + if condition.Status != readyCondition.Status { + readyCondition.LastTransitionTime = meta + } else { + readyCondition.LastTransitionTime = condition.LastTransitionTime + } + apiKey.Status.Conditions[i] = readyCondition + break + } } } - newConditions = append(newConditions, condition) - - // Check if status has actually changed - if !controllers2.StatusHasChanged(apiKey.Status.Conditions, newConditions) { - return - } - - // Update conditions - apiKey.Status.Conditions = newConditions - - // Update observed generation - apiKey.Status.ObservedGeneration = apiKey.Generation - // Update status + // Update APIKey status if err := r.Status().Update(ctx, apiKey); err != nil { - log.FromContext(ctx).Error(err, "Failed to update APIKey status") + logger.Error(err, "Failed to update APIKey status") } } // SetupWithManager sets up the controller with the Manager. func (r *APIKeyReconciler) SetupWithManager(mgr ctrl.Manager) error { - // Initialize the watcher map r.watcherMap = make(map[types.NamespacedName]watch.Interface) - return ctrl.NewControllerManagedBy(mgr). For(&resourcev1alpha1.APIKey{}). - WithEventFilter(predicate.Or( - // Trigger on spec changes - predicate.GenerationChangedPredicate{}, - // Trigger periodically to sync status - predicate.NewPredicateFuncs(func(object client.Object) bool { - // Trigger every minute to sync status - return true - }), - )). + WithEventFilter(predicate.GenerationChangedPredicate{}). Complete(r) } + +// DecryptToken decrypts an encrypted token using the provided private key +func DecryptToken(priv *rsa.PrivateKey, encryptedToken cloudapi.EncryptedToken) (string, error) { + if encryptedToken.JWE == nil || *encryptedToken.JWE == "" { + return "", errors.New("encrypted token is empty") + } + + // Decrypt the JWE token + token, err := jwe.Decrypt([]byte(*encryptedToken.JWE), jwe.WithKey(jwa.RSA_OAEP, priv)) + if err != nil { + return "", errors.Wrap(err, "failed to decrypt JWE token") + } + + return string(token), nil +} diff --git a/controllers/serviceaccount_controller.go b/controllers/serviceaccount_controller.go index 6ab994ee..83fe0ad7 100644 --- a/controllers/serviceaccount_controller.go +++ b/controllers/serviceaccount_controller.go @@ -16,12 +16,14 @@ package controllers import ( "context" + "encoding/base64" "fmt" "sync" "time" resourcev1alpha1 "github.com/streamnative/pulsar-resources-operator/api/v1alpha1" controllers2 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud" + "github.com/streamnative/pulsar-resources-operator/pkg/utils" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -53,6 +55,7 @@ const ServiceAccountFinalizer = "serviceaccount.resource.streamnative.io/finaliz //+kubebuilder:rbac:groups=resource.streamnative.io,resources=serviceaccounts/status,verbs=get;update;patch //+kubebuilder:rbac:groups=resource.streamnative.io,resources=serviceaccounts/finalizers,verbs=update //+kubebuilder:rbac:groups=resource.streamnative.io,resources=streamnativecloudconnections,verbs=get;list;watch +//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete // handleWatchEvents processes events from the watch interface func (r *ServiceAccountReconciler) handleWatchEvents(ctx context.Context, namespacedName types.NamespacedName, watcher watch.Interface) { @@ -75,26 +78,51 @@ func (r *ServiceAccountReconciler) handleWatchEvents(ctx context.Context, namesp if event.Type == watch.Modified { // Check if the object is a ServiceAccount - _, ok := event.Object.(*cloudapi.ServiceAccount) + cloudSA, ok := event.Object.(*cloudapi.ServiceAccount) if !ok { logger.Error(fmt.Errorf("unexpected object type"), "Failed to convert object to ServiceAccount") continue } // Get the local ServiceAccount - localServiceAccount := &resourcev1alpha1.ServiceAccount{} - if err := r.Get(ctx, namespacedName, localServiceAccount); err != nil { + localSA := &resourcev1alpha1.ServiceAccount{} + if err := r.Get(ctx, namespacedName, localSA); err != nil { logger.Error(err, "Failed to get local ServiceAccount") continue } // Update status - r.updateServiceAccountStatus(ctx, localServiceAccount, nil, "Ready", "ServiceAccount synced successfully") + r.updateServiceAccountStatus(ctx, localSA, nil, "Ready", "ServiceAccount synced successfully") + + // Process credentials and create Secret if needed + if cloudSA.Status.PrivateKeyType == utils.ServiceAccountCredentialsType && cloudSA.Status.PrivateKeyData != "" { + r.processServiceAccountCredentials(ctx, localSA, cloudSA) + } } } } } +// processServiceAccountCredentials handles credentials data and creates a Secret +func (r *ServiceAccountReconciler) processServiceAccountCredentials(ctx context.Context, localSA *resourcev1alpha1.ServiceAccount, cloudSA *cloudapi.ServiceAccount) { + logger := log.FromContext(ctx) + + // Base64 decode the private key data + credentialsData, err := base64.StdEncoding.DecodeString(cloudSA.Status.PrivateKeyData) + if err != nil { + logger.Error(err, "Failed to decode private key data") + return + } + + // Create or update Secret with credentials + if err := utils.CreateOrUpdateServiceAccountCredentialsSecret(ctx, r.Client, localSA, localSA.Namespace, localSA.Name, string(credentialsData)); err != nil { + logger.Error(err, "Failed to create or update service account credentials secret") + return + } + + logger.Info("Successfully created credentials secret for service account") +} + // setupWatch creates a new watcher for a ServiceAccount func (r *ServiceAccountReconciler) setupWatch(ctx context.Context, serviceAccount *resourcev1alpha1.ServiceAccount, saClient *controllers2.ServiceAccountClient) error { namespacedName := types.NamespacedName{ @@ -249,40 +277,65 @@ func (r *ServiceAccountReconciler) Reconcile(ctx context.Context, req ctrl.Reque } if resultSA.Status.PrivateKeyData != "" { serviceAccount.Status.PrivateKeyData = resultSA.Status.PrivateKeyData + + // If the credentials type is ServiceAccountCredentialsType, create a Secret + if resultSA.Status.PrivateKeyType == utils.ServiceAccountCredentialsType { + credentialsData, err := base64.StdEncoding.DecodeString(resultSA.Status.PrivateKeyData) + if err != nil { + logger.Error(err, "Failed to decode private key data") + } else { + if err := utils.CreateOrUpdateServiceAccountCredentialsSecret(ctx, r.Client, serviceAccount, serviceAccount.Namespace, serviceAccount.Name, string(credentialsData)); err != nil { + logger.Error(err, "Failed to create or update service account credentials secret") + } else { + logger.Info("Successfully created credentials secret for service account") + } + } + } + } + + // Set up watch for ServiceAccount + if err := r.setupWatch(ctx, serviceAccount, saClient); err != nil { + logger.Error(err, "Failed to set up watch", "serviceAccount", serviceAccount.Name) } // Update status r.updateServiceAccountStatus(ctx, serviceAccount, nil, "Ready", "ServiceAccount created successfully") } else { - // Update ServiceAccount - resultSA, err := saClient.UpdateServiceAccount(ctx, serviceAccount) - if err != nil { - r.updateServiceAccountStatus(ctx, serviceAccount, err, "UpdateServiceAccountFailed", - fmt.Sprintf("Failed to update ServiceAccount: %v", err)) - return ctrl.Result{}, err + // Update status with private key information from existing ServiceAccount + if existingSA.Status.PrivateKeyType != "" { + serviceAccount.Status.PrivateKeyType = existingSA.Status.PrivateKeyType } - - // Update status with private key information if available - if resultSA.Status.PrivateKeyType != "" { - serviceAccount.Status.PrivateKeyType = resultSA.Status.PrivateKeyType + if existingSA.Status.PrivateKeyData != "" { + serviceAccount.Status.PrivateKeyData = existingSA.Status.PrivateKeyData + + // If the credentials type is ServiceAccountCredentialsType, ensure Secret exists + if existingSA.Status.PrivateKeyType == utils.ServiceAccountCredentialsType { + credentialsData, err := base64.StdEncoding.DecodeString(existingSA.Status.PrivateKeyData) + if err != nil { + logger.Error(err, "Failed to decode private key data") + } else { + if err := utils.CreateOrUpdateServiceAccountCredentialsSecret(ctx, r.Client, serviceAccount, serviceAccount.Namespace, serviceAccount.Name, string(credentialsData)); err != nil { + logger.Error(err, "Failed to create or update service account credentials secret") + } else { + logger.Info("Successfully created credentials secret for service account") + } + } + } } - if resultSA.Status.PrivateKeyData != "" { - serviceAccount.Status.PrivateKeyData = resultSA.Status.PrivateKeyData + + // Set up watch for ServiceAccount + if err := r.setupWatch(ctx, serviceAccount, saClient); err != nil { + logger.Error(err, "Failed to set up watch", "serviceAccount", serviceAccount.Name) } // Update status - r.updateServiceAccountStatus(ctx, serviceAccount, nil, "Ready", "ServiceAccount updated successfully") - } - - // Setup watch after ServiceAccount is created/updated - if err := r.setupWatch(ctx, serviceAccount, saClient); err != nil { - logger.Error(err, "Failed to setup watch") - // Don't return error, just log it + r.updateServiceAccountStatus(ctx, serviceAccount, nil, "Ready", "ServiceAccount synced successfully") } return ctrl.Result{RequeueAfter: requeueInterval}, nil } +// updateServiceAccountStatus updates the status of the ServiceAccount resource func (r *ServiceAccountReconciler) updateServiceAccountStatus( ctx context.Context, serviceAccount *resourcev1alpha1.ServiceAccount, @@ -290,61 +343,54 @@ func (r *ServiceAccountReconciler) updateServiceAccountStatus( reason string, message string, ) { - // Create new condition - condition := metav1.Condition{ + logger := log.FromContext(ctx) + serviceAccount.Status.ObservedGeneration = serviceAccount.Generation + + // Set ready condition based on error + meta := metav1.Now() + readyCondition := metav1.Condition{ Type: "Ready", Status: metav1.ConditionTrue, Reason: reason, Message: message, - ObservedGeneration: serviceAccount.Generation, - LastTransitionTime: metav1.Now(), + LastTransitionTime: meta, } if err != nil { - condition.Status = metav1.ConditionFalse + readyCondition.Status = metav1.ConditionFalse + logger.Error(err, "ServiceAccount reconciliation failed", + "reason", reason, "message", message) } - // Create new conditions slice for comparison - newConditions := make([]metav1.Condition, 0) - for _, c := range serviceAccount.Status.Conditions { - if c.Type != condition.Type { - newConditions = append(newConditions, c) + // Update ready condition + if len(serviceAccount.Status.Conditions) == 0 { + serviceAccount.Status.Conditions = []metav1.Condition{readyCondition} + } else { + for i, condition := range serviceAccount.Status.Conditions { + if condition.Type == "Ready" { + // Only update time if status changes + if condition.Status != readyCondition.Status { + readyCondition.LastTransitionTime = meta + } else { + readyCondition.LastTransitionTime = condition.LastTransitionTime + } + serviceAccount.Status.Conditions[i] = readyCondition + break + } } } - newConditions = append(newConditions, condition) - - // Check if status has actually changed - if !controllers2.StatusHasChanged(serviceAccount.Status.Conditions, newConditions) { - return - } - - // Update conditions - serviceAccount.Status.Conditions = newConditions - // Update observed generation - serviceAccount.Status.ObservedGeneration = serviceAccount.Generation - - // Update status + // Update ServiceAccount status if err := r.Status().Update(ctx, serviceAccount); err != nil { - log.FromContext(ctx).Error(err, "Failed to update ServiceAccount status") + logger.Error(err, "Failed to update ServiceAccount status") } } // SetupWithManager sets up the controller with the Manager. func (r *ServiceAccountReconciler) SetupWithManager(mgr ctrl.Manager) error { - // Initialize the watcher map r.watcherMap = make(map[types.NamespacedName]watch.Interface) - return ctrl.NewControllerManagedBy(mgr). For(&resourcev1alpha1.ServiceAccount{}). - WithEventFilter(predicate.Or( - // Trigger on spec changes - predicate.GenerationChangedPredicate{}, - // Trigger periodically to sync status - predicate.NewPredicateFuncs(func(object client.Object) bool { - // Trigger every minute to sync status - return true - }), - )). + WithEventFilter(predicate.GenerationChangedPredicate{}). Complete(r) } diff --git a/docs/apikey.md b/docs/apikey.md new file mode 100644 index 00000000..99b20eb8 --- /dev/null +++ b/docs/apikey.md @@ -0,0 +1,131 @@ +# APIKey + +The `APIKey` resource allows you to create and manage API keys for authenticating with the StreamNative Cloud API. + +## Example + +```yaml +apiVersion: resource.streamnative.io/v1alpha1 +kind: APIKey +metadata: + name: my-apikey + namespace: default +spec: + apiServerRef: + name: my-connection + instanceName: my-pulsar-instance + serviceAccountName: my-service-account + description: "API Key for automation" + expirationTime: "2025-12-31T23:59:59Z" +``` + +## Specification + +| Field | Type | Description | Required | +| --- | --- | --- | --- | +| `spec.apiServerRef` | [LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#localobjectreference-v1-core) | Reference to a StreamNativeCloudConnection in the same namespace | Yes | +| `spec.instanceName` | string | Name of the instance this API key is for | No | +| `spec.serviceAccountName` | string | Name of the service account this API key is for | Yes | +| `spec.description` | string | User-defined description of the API key | No | +| `spec.expirationTime` | string | Timestamp defining when this API key will expire | No | +| `spec.revoke` | boolean | Indicates whether this API key should be revoked | No | +| `spec.encryptionKey` | object | Contains the public key used to encrypt the token | No | + +## Status + +| Field | Type | Description | +| --- | --- | --- | +| `status.conditions` | []Condition | Current state of the APIKey | +| `status.observedGeneration` | int64 | Last observed generation | +| `status.keyId` | string | Unique identifier for the API key | +| `status.issuedAt` | string | Timestamp when the key was issued | +| `status.expiresAt` | string | Timestamp when the key expires | +| `status.token` | string | The plaintext security token issued for the key (available only after creation) | +| `status.encryptedToken` | object | The encrypted security token if an encryption key was provided | +| `status.revokedAt` | string | Timestamp when the key was revoked, if applicable | + +## Secret Management + +For each APIKey resource, the operator creates and manages two types of Secrets: + +1. **Private Key Secret**: Contains the RSA private key used for decrypting tokens + - Name format: `-private-key` + - Contains key: `private-key` + +2. **Token Secret**: Contains the decrypted API token for authentication + - Name format: `-token` + - Contains key: `token` + - Includes labels: + - `resources.streamnative.io/apikey`: Name of the APIKey + - `resources.streamnative.io/key-id`: Unique identifier of the APIKey + +## Usage + +API keys provide authentication credentials for service accounts to access the StreamNative Cloud API. They can be used in automated workflows, CI/CD pipelines, or any system that needs to interact with StreamNative Cloud resources. + +### Creating an API Key + +To create an API key, you need: + +1. A StreamNativeCloudConnection resource configured with valid credentials +2. An existing service account for which the API key will be created + +```yaml +apiVersion: resource.streamnative.io/v1alpha1 +kind: APIKey +metadata: + name: my-automation-key + namespace: default +spec: + apiServerRef: + name: my-connection + instanceName: my-pulsar-instance + serviceAccountName: my-service-account + description: "API Key for CI/CD pipeline" + expirationTime: "2026-01-01T00:00:00Z" +``` + +### Using API Keys in Applications + +You can mount the token secret in your application pods: + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: my-app +spec: + containers: + - name: app + image: my-app-image + volumeMounts: + - name: apikey-volume + mountPath: /etc/apikey + readOnly: true + volumes: + - name: apikey-volume + secret: + secretName: my-automation-key-token +``` + +The application can then read the token from `/etc/apikey/token`. + +### Revoking an API Key + +To revoke an API key, update the `spec.revoke` field to `true`: + +```yaml +apiVersion: resource.streamnative.io/v1alpha1 +kind: APIKey +metadata: + name: my-automation-key + namespace: default +spec: + apiServerRef: + name: my-connection + instanceName: my-pulsar-instance + serviceAccountName: my-service-account + revoke: true +``` + +Once revoked, the API key can no longer be used for authentication. diff --git a/docs/serviceaccount.md b/docs/serviceaccount.md new file mode 100644 index 00000000..283d97ea --- /dev/null +++ b/docs/serviceaccount.md @@ -0,0 +1,111 @@ +# ServiceAccount + +The `ServiceAccount` resource allows you to create and manage service accounts in the StreamNative Cloud platform for programmatic access to resources. + +## Example + +```yaml +apiVersion: resource.streamnative.io/v1alpha1 +kind: ServiceAccount +metadata: + name: my-service-account + namespace: default +spec: + apiServerRef: + name: my-connection +``` + +## Specification + +| Field | Type | Description | Required | +| --- | --- | --- | --- | +| `spec.apiServerRef` | [LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#localobjectreference-v1-core) | Reference to a StreamNativeCloudConnection in the same namespace | Yes | + +## Status + +| Field | Type | Description | +| --- | --- | --- | +| `status.conditions` | []Condition | Current state of the ServiceAccount | +| `status.observedGeneration` | int64 | Last observed generation | +| `status.privateKeyType` | string | Type of the private key data | +| `status.privateKeyData` | string | Private key data in base64 format | + +## Secret Management + +When a `ServiceAccount` resource is successfully created and contains credentials, the operator automatically creates a Kubernetes Secret to store these credentials: + +- **Credentials Secret**: Contains the OAuth2 JSON credentials data + - Name format: `-credentials` + - Contains key: `credentials.json` + - The Secret is automatically created when: + - The ServiceAccount is in a Ready state + - The `privateKeyType` is set to `TYPE_SN_CREDENTIALS_FILE` + - The `privateKeyData` field contains valid base64-encoded credentials + +This Secret is owned by the ServiceAccount resource and will be automatically deleted when the ServiceAccount is deleted. + +## Usage + +Service accounts provide a way to manage access to StreamNative Cloud resources programmatically. They are typically used for automation, CI/CD pipelines, or any system that needs to interact with StreamNative Cloud resources without using personal user credentials. + +### Creating a Service Account + +To create a service account, you need: + +1. A StreamNativeCloudConnection resource configured with valid credentials +2. An instance in which to create the service account + +```yaml +apiVersion: resource.streamnative.io/v1alpha1 +kind: ServiceAccount +metadata: + name: automation-account + namespace: default +spec: + apiServerRef: + name: my-connection +``` + +### Using Service Account Credentials in Applications + +You can mount the credentials secret in your application pods: + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: my-app +spec: + containers: + - name: app + image: my-app-image + volumeMounts: + - name: credentials-volume + mountPath: /etc/credentials + readOnly: true + volumes: + - name: credentials-volume + secret: + secretName: automation-account-credentials +``` + +The application can then read the credentials from `/etc/credentials/credentials.json` and use them for OAuth2 authentication. + +## Creating API Keys + +After creating a service account, you can create API keys for it using the `APIKey` resource: + +```yaml +apiVersion: resource.streamnative.io/v1alpha1 +kind: APIKey +metadata: + name: my-api-key + namespace: default +spec: + apiServerRef: + name: my-connection + serviceAccountName: automation-account + description: "API Key for automation" + expirationTime: "2025-12-31T23:59:59Z" + instanceName: my-pulsar-instance +``` diff --git a/docs/serviceaccountbinding.md b/docs/serviceaccountbinding.md new file mode 100644 index 00000000..af24321f --- /dev/null +++ b/docs/serviceaccountbinding.md @@ -0,0 +1,73 @@ +# ServiceAccountBinding + +The `ServiceAccountBinding` resource allows you to bind a service account to a pool member in the StreamNative Cloud, granting the service account access to the resources managed by that pool member, such as Pulsar Functions, Pulsar Connectors, Kafka Connect Connectors, Flink Jobs, etc. + +## Example + +```yaml +apiVersion: resource.streamnative.io/v1alpha1 +kind: ServiceAccountBinding +metadata: + name: my-service-account-binding + namespace: default +spec: + serviceAccountName: my-service-account + poolMemberRef: + namespace: default + name: my-pool-member +``` + +## Specification + +| Field | Type | Description | Required | +| --- | --- | --- | --- | +| `spec.serviceAccountName` | string | Reference to a ServiceAccount in the same namespace as this binding object | Yes | +| `spec.poolMemberRef.name` | string | Name of the pool member this service account will be bound to | Yes | +| `spec.poolMemberRef.namespace` | string | Namespace of the pool member | Yes | + +## Status + +| Field | Type | Description | +| --- | --- | --- | +| `status.conditions` | []Condition | Current state of the ServiceAccountBinding | +| `status.observedGeneration` | int64 | Last observed generation | + +## Usage + +Service account bindings provide a way to grant service accounts access to specific pool members in StreamNative Cloud. This allows for fine-grained control over which service accounts can access which resources. + +### Creating a Service Account Binding + +To create a service account binding, you need: + +1. An existing ServiceAccount resource in the same namespace +2. A pool member to which the service account will be bound + +```yaml +apiVersion: resource.streamnative.io/v1alpha1 +kind: ServiceAccountBinding +metadata: + name: app-service-binding + namespace: default +spec: + serviceAccountName: app-service-account + poolMemberRef: + namespace: default + name: production-pool-member +``` + +### Checking Binding Status + +You can check the status of a service account binding using kubectl: + +```bash +kubectl get serviceaccountbinding app-service-binding -n default +``` + +The `READY` column will show `True` when the binding is successfully established. + +For more detailed status information: + +```bash +kubectl describe serviceaccountbinding app-service-binding -n default +``` diff --git a/go.mod b/go.mod index 49dfc426..7fa57b96 100644 --- a/go.mod +++ b/go.mod @@ -77,6 +77,7 @@ require ( github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/goccy/go-json v0.10.3 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.2 // indirect @@ -97,6 +98,8 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kylelemons/godebug v1.1.0 // indirect + github.com/lestrrat-go/jwx/v2 v2.1.4 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -112,6 +115,7 @@ require ( github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect + github.com/segmentio/asm v1.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.10.0 // indirect go.opencensus.io v0.24.0 // indirect diff --git a/go.sum b/go.sum index 77bf1f4b..b47f6f87 100644 --- a/go.sum +++ b/go.sum @@ -1532,6 +1532,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE= github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= @@ -1742,6 +1744,10 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lestrrat-go/jwx/v2 v2.1.4 h1:uBCMmJX8oRZStmKuMMOFb0Yh9xmEMgNJLgjuKKt4/qc= +github.com/lestrrat-go/jwx/v2 v2.1.4/go.mod h1:nWRbDFR1ALG2Z6GJbBXzfQaYyvn751KuuyySN2yR6is= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= @@ -1828,6 +1834,8 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= diff --git a/go.work.sum b/go.work.sum index adc620e5..b4326b07 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,137 +1,68 @@ cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w= cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= -cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME= -cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= -cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= -cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= -cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= -cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms= cloud.google.com/go/accessapproval v1.6.0 h1:x0cEHro/JFPd7eS4BlEWNTMecIj2HdXjOVB5BtvwER0= cloud.google.com/go/accessapproval v1.7.1 h1:/5YjNhR6lzCvmJZAnByYkfEgWjfAKwYP6nkuTk6nKFE= -cloud.google.com/go/accessapproval v1.7.2/go.mod h1:/gShiq9/kK/h8T/eEn1BTzalDvk0mZxJlhfw0p+Xuc0= -cloud.google.com/go/accessapproval v1.7.3/go.mod h1:4l8+pwIxGTNqSf4T3ds8nLO94NQf0W/KnMNuQ9PbnP8= -cloud.google.com/go/accessapproval v1.7.4/go.mod h1:/aTEh45LzplQgFYdQdwPMR9YdX0UlhBmvB84uAmQKUc= cloud.google.com/go/accessapproval v1.7.5 h1:uzmAMSgYcnlHa9X9YSQZ4Q1wlfl4NNkZyQgho1Z6p04= -cloud.google.com/go/accessapproval v1.7.5/go.mod h1:g88i1ok5dvQ9XJsxpUInWWvUBrIZhyPDPbk4T01OoJ0= cloud.google.com/go/accessapproval v1.7.11 h1:MgtE8CI+YJWPGGHnxQ9z1VQqV87h+vSGy2MeM/m0ggQ= cloud.google.com/go/accessapproval v1.7.11/go.mod h1:KGK3+CLDWm4BvjN0wFtZqdFUGhxlTvTF6PhAwQJGL4M= cloud.google.com/go/accesscontextmanager v1.7.0 h1:MG60JgnEoawHJrbWw0jGdv6HLNSf6gQvYRiXpuzqgEA= cloud.google.com/go/accesscontextmanager v1.8.1 h1:WIAt9lW9AXtqw/bnvrEUaE8VG/7bAAeMzRCBGMkc4+w= -cloud.google.com/go/accesscontextmanager v1.8.2/go.mod h1:E6/SCRM30elQJ2PKtFMs2YhfJpZSNcJyejhuzoId4Zk= -cloud.google.com/go/accesscontextmanager v1.8.3/go.mod h1:4i/JkF2JiFbhLnnpnfoTX5vRXfhf9ukhU1ANOTALTOQ= -cloud.google.com/go/accesscontextmanager v1.8.4/go.mod h1:ParU+WbMpD34s5JFEnGAnPBYAgUHozaTmDJU7aCU9+M= cloud.google.com/go/accesscontextmanager v1.8.5 h1:2GLNaNu9KRJhJBFTIVRoPwk6xE5mUDgD47abBq4Zp/I= -cloud.google.com/go/accesscontextmanager v1.8.5/go.mod h1:TInEhcZ7V9jptGNqN3EzZ5XMhT6ijWxTGjzyETwmL0Q= cloud.google.com/go/accesscontextmanager v1.8.11 h1:IQ3KLJmNKPgstN0ZcRw0niU4KfsiOZmzvcGCF+NT618= cloud.google.com/go/accesscontextmanager v1.8.11/go.mod h1:nwPysISS3KR5qXipAU6cW/UbDavDdTBBgPohbkhGSok= cloud.google.com/go/aiplatform v1.37.0 h1:zTw+suCVchgZyO+k847wjzdVjWmrAuehxdvcZvJwfGg= -cloud.google.com/go/aiplatform v1.48.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.50.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= cloud.google.com/go/aiplatform v1.51.0 h1:OEYe9gBca6ydogM2G0mQRMHE+aUhEV4cyaqxbk3+M1o= -cloud.google.com/go/aiplatform v1.51.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.51.1/go.mod h1:kY3nIMAVQOK2XDqDPHaOuD9e+FdMA6OOpfBjsvaFSOo= -cloud.google.com/go/aiplatform v1.51.2/go.mod h1:hCqVYB3mY45w99TmetEoe8eCQEwZEp9WHxeZdcv9phw= -cloud.google.com/go/aiplatform v1.52.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.54.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.57.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.58.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.58.2/go.mod h1:c3kCiVmb6UC1dHAjZjcpDj6ZS0bHQ2slL88ZjC2LtlA= cloud.google.com/go/aiplatform v1.60.0 h1:0cSrii1ZeLr16MbBoocyy5KVnrSdiQ3KN/vtrTe7RqE= -cloud.google.com/go/aiplatform v1.60.0/go.mod h1:eTlGuHOahHprZw3Hio5VKmtThIOak5/qy6pzdsqcQnM= cloud.google.com/go/aiplatform v1.68.0 h1:EPPqgHDJpBZKRvv+OsB3cr0jYz3EL2pZ+802rBPcG8U= cloud.google.com/go/aiplatform v1.68.0/go.mod h1:105MFA3svHjC3Oazl7yjXAmIR89LKhRAeNdnDKJczME= cloud.google.com/go/analytics v0.19.0 h1:LqAo3tAh2FU9+w/r7vc3hBjU23Kv7GhO/PDIW7kIYgM= cloud.google.com/go/analytics v0.21.3 h1:TFBC1ZAqX9/jL56GEXdLrVe5vT3I22bDVWyDwZX4IEg= -cloud.google.com/go/analytics v0.21.3/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.4/go.mod h1:zZgNCxLCy8b2rKKVfC1YkC2vTrpfZmeRCySM3aUbskA= -cloud.google.com/go/analytics v0.21.5/go.mod h1:BQtOBHWTlJ96axpPPnw5CvGJ6i3Ve/qX2fTxR8qWyr8= -cloud.google.com/go/analytics v0.21.6/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= -cloud.google.com/go/analytics v0.22.0/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= cloud.google.com/go/analytics v0.23.0 h1:Q+y94XH84jM8SK8O7qiY/PJRexb6n7dRbQ6PiUa4YGM= -cloud.google.com/go/analytics v0.23.0/go.mod h1:YPd7Bvik3WS95KBok2gPXDqQPHy08TsCQG6CdUCb+u0= cloud.google.com/go/analytics v0.23.6 h1:BY8ZY7hQwKBi+lNp1IkiMTOK4xe4lxZCeYv3S9ARXtE= cloud.google.com/go/analytics v0.23.6/go.mod h1:cFz5GwWHrWQi8OHKP9ep3Z4pvHgGcG9lPnFQ+8kXsNo= cloud.google.com/go/apigateway v1.5.0 h1:ZI9mVO7x3E9RK/BURm2p1aw9YTBSCQe3klmyP1WxWEg= cloud.google.com/go/apigateway v1.6.1 h1:aBSwCQPcp9rZ0zVEUeJbR623palnqtvxJlUyvzsKGQc= -cloud.google.com/go/apigateway v1.6.2/go.mod h1:CwMC90nnZElorCW63P2pAYm25AtQrHfuOkbRSHj0bT8= -cloud.google.com/go/apigateway v1.6.3/go.mod h1:k68PXWpEs6BVDTtnLQAyG606Q3mz8pshItwPXjgv44Y= -cloud.google.com/go/apigateway v1.6.4/go.mod h1:0EpJlVGH5HwAN4VF4Iec8TAzGN1aQgbxAWGJsnPCGGY= cloud.google.com/go/apigateway v1.6.5 h1:sPXnpk+6TneKIrjCjcpX5YGsAKy3PTdpIchoj8/74OE= -cloud.google.com/go/apigateway v1.6.5/go.mod h1:6wCwvYRckRQogyDDltpANi3zsCDl6kWi0b4Je+w2UiI= cloud.google.com/go/apigateway v1.6.11 h1:VtEvpnqqY2T5gZBzo+p7C87yGH3omHUkPIbRQkmGS9I= cloud.google.com/go/apigateway v1.6.11/go.mod h1:4KsrYHn/kSWx8SNUgizvaz+lBZ4uZfU7mUDsGhmkWfM= cloud.google.com/go/apigeeconnect v1.5.0 h1:sWOmgDyAsi1AZ48XRHcATC0tsi9SkPT7DA/+VCfkaeA= cloud.google.com/go/apigeeconnect v1.6.1 h1:6u/jj0P2c3Mcm+H9qLsXI7gYcTiG9ueyQL3n6vCmFJM= -cloud.google.com/go/apigeeconnect v1.6.2/go.mod h1:s6O0CgXT9RgAxlq3DLXvG8riw8PYYbU/v25jqP3Dy18= -cloud.google.com/go/apigeeconnect v1.6.3/go.mod h1:peG0HFQ0si2bN15M6QSjEW/W7Gy3NYkWGz7pFz13cbo= -cloud.google.com/go/apigeeconnect v1.6.4/go.mod h1:CapQCWZ8TCjnU0d7PobxhpOdVz/OVJ2Hr/Zcuu1xFx0= cloud.google.com/go/apigeeconnect v1.6.5 h1:CrfIKv9Go3fh/QfQgisU3MeP90Ww7l/sVGmr3TpECo8= -cloud.google.com/go/apigeeconnect v1.6.5/go.mod h1:MEKm3AiT7s11PqTfKE3KZluZA9O91FNysvd3E6SJ6Ow= cloud.google.com/go/apigeeconnect v1.6.11 h1:CftZgGXFRLJeD2/5ZIdWuAMxW/88UG9tHhRPI/NY75M= cloud.google.com/go/apigeeconnect v1.6.11/go.mod h1:iMQLTeKxtKL+sb0D+pFlS/TO6za2IUOh/cwMEtn/4g0= cloud.google.com/go/apigeeregistry v0.6.0 h1:E43RdhhCxdlV+I161gUY2rI4eOaMzHTA5kNkvRsFXvc= cloud.google.com/go/apigeeregistry v0.7.1 h1:hgq0ANLDx7t2FDZDJQrCMtCtddR/pjCqVuvQWGrQbXw= -cloud.google.com/go/apigeeregistry v0.7.2/go.mod h1:9CA2B2+TGsPKtfi3F7/1ncCCsL62NXBRfM6iPoGSM+8= -cloud.google.com/go/apigeeregistry v0.8.1/go.mod h1:MW4ig1N4JZQsXmBSwH4rwpgDonocz7FPBSw6XPGHmYw= -cloud.google.com/go/apigeeregistry v0.8.2/go.mod h1:h4v11TDGdeXJDJvImtgK2AFVvMIgGWjSb0HRnBSjcX8= cloud.google.com/go/apigeeregistry v0.8.3 h1:C+QU2K+DzDjk4g074ouwHQGkoff1h5OMQp6sblCVreQ= -cloud.google.com/go/apigeeregistry v0.8.3/go.mod h1:aInOWnqF4yMQx8kTjDqHNXjZGh/mxeNlAf52YqtASUs= cloud.google.com/go/apigeeregistry v0.8.9 h1:3vLwk0tS9L++6ZyV4RDH4UCydfVoqxJbpWvqG6MTtUw= cloud.google.com/go/apigeeregistry v0.8.9/go.mod h1:4XivwtSdfSO16XZdMEQDBCMCWDp3jkCBRhVgamQfLSA= cloud.google.com/go/apikeys v0.6.0 h1:B9CdHFZTFjVti89tmyXXrO+7vSNo2jvZuHG8zD5trdQ= cloud.google.com/go/appengine v1.7.1 h1:aBGDKmRIaRRoWJ2tAoN0oVSHoWLhtO9aj/NvUyP4aYs= cloud.google.com/go/appengine v1.8.1 h1:J+aaUZ6IbTpBegXbmEsh8qZZy864ZVnOoWyfa1XSNbI= -cloud.google.com/go/appengine v1.8.2/go.mod h1:WMeJV9oZ51pvclqFN2PqHoGnys7rK0rz6s3Mp6yMvDo= -cloud.google.com/go/appengine v1.8.3/go.mod h1:2oUPZ1LVZ5EXi+AF1ihNAF+S8JrzQ3till5m9VQkrsk= -cloud.google.com/go/appengine v1.8.4/go.mod h1:TZ24v+wXBujtkK77CXCpjZbnuTvsFNT41MUaZ28D6vg= cloud.google.com/go/appengine v1.8.5 h1:l2SviT44zWQiOv8bPoMBzW0vOcMO22iO0s+nVtVhdts= -cloud.google.com/go/appengine v1.8.5/go.mod h1:uHBgNoGLTS5di7BvU25NFDuKa82v0qQLjyMJLuPQrVo= cloud.google.com/go/appengine v1.8.11 h1:ZLoWWwakgRzRnXX2bsgk2g1sdzti3wq+ebunTJsZNog= cloud.google.com/go/appengine v1.8.11/go.mod h1:xET3coaDUj+OP4TgnZlgQ+rG2R9fG2nblya13czP56Q= cloud.google.com/go/area120 v0.7.1 h1:ugckkFh4XkHJMPhTIx0CyvdoBxmOpMe8rNs4Ok8GAag= cloud.google.com/go/area120 v0.8.1 h1:wiOq3KDpdqXmaHzvZwKdpoM+3lDcqsI2Lwhyac7stss= -cloud.google.com/go/area120 v0.8.2/go.mod h1:a5qfo+x77SRLXnCynFWPUZhnZGeSgvQ+Y0v1kSItkh4= -cloud.google.com/go/area120 v0.8.3/go.mod h1:5zj6pMzVTH+SVHljdSKC35sriR/CVvQZzG/Icdyriw0= -cloud.google.com/go/area120 v0.8.4/go.mod h1:jfawXjxf29wyBXr48+W+GyX/f8fflxp642D/bb9v68M= cloud.google.com/go/area120 v0.8.5 h1:vTs08KPLN/iMzTbxpu5ciL06KcsrVPMjz4IwcQyZ4uY= -cloud.google.com/go/area120 v0.8.5/go.mod h1:BcoFCbDLZjsfe4EkCnEq1LKvHSK0Ew/zk5UFu6GMyA0= cloud.google.com/go/area120 v0.8.11 h1:UID1dl7lW2zs8OpYVtVZ5WsXU9kUcxC1nd3nnToHW70= cloud.google.com/go/area120 v0.8.11/go.mod h1:VBxJejRAJqeuzXQBbh5iHBYUkIjZk5UzFZLCXmzap2o= cloud.google.com/go/artifactregistry v1.13.0 h1:o1Q80vqEB6Qp8WLEH3b8FBLNUCrGQ4k5RFj0sn/sgO8= cloud.google.com/go/artifactregistry v1.14.2 h1:xJIxeBs9ZYH2j8Wg/K/aCkroNUnDXgIhSHgz5FUE/Q4= -cloud.google.com/go/artifactregistry v1.14.2/go.mod h1:Xk+QbsKEb0ElmyeMfdHAey41B+qBq3q5R5f5xD4XT3U= -cloud.google.com/go/artifactregistry v1.14.3/go.mod h1:A2/E9GXnsyXl7GUvQ/2CjHA+mVRoWAXC0brg2os+kNI= -cloud.google.com/go/artifactregistry v1.14.4/go.mod h1:SJJcZTMv6ce0LDMUnihCN7WSrI+kBSFV0KIKo8S8aYU= -cloud.google.com/go/artifactregistry v1.14.6/go.mod h1:np9LSFotNWHcjnOgh8UVK0RFPCTUGbO0ve3384xyHfE= cloud.google.com/go/artifactregistry v1.14.7 h1:W9sVlyb1VRcUf83w7aM3yMsnp4HS4PoyGqYQNG0O5lI= -cloud.google.com/go/artifactregistry v1.14.7/go.mod h1:0AUKhzWQzfmeTvT4SjfI4zjot72EMfrkvL9g9aRjnnM= cloud.google.com/go/artifactregistry v1.14.13 h1:NNK4vYVA5NGQmbmYidfJhnfmYU6SSSRUM2oopNouJNs= cloud.google.com/go/artifactregistry v1.14.13/go.mod h1:zQ/T4xoAFPtcxshl+Q4TJBgsy7APYR/BLd2z3xEAqRA= cloud.google.com/go/asset v1.13.0 h1:YAsssO08BqZ6mncbb6FPlj9h6ACS7bJQUOlzciSfbNk= cloud.google.com/go/asset v1.15.0 h1:4SdWreholqB0ZOHjBO+K+RSsW9TcZBbfpfXtFir23R0= -cloud.google.com/go/asset v1.15.0/go.mod h1:tpKafV6mEut3+vN9ScGvCHXHj7FALFVta+okxFECHcg= -cloud.google.com/go/asset v1.15.1/go.mod h1:yX/amTvFWRpp5rcFq6XbCxzKT8RJUam1UoboE179jU4= -cloud.google.com/go/asset v1.15.2/go.mod h1:B6H5tclkXvXz7PD22qCA2TDxSVQfasa3iDlM89O2NXs= -cloud.google.com/go/asset v1.15.3/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= -cloud.google.com/go/asset v1.16.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= -cloud.google.com/go/asset v1.17.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= -cloud.google.com/go/asset v1.17.1/go.mod h1:byvDw36UME5AzGNK7o4JnOnINkwOZ1yRrGrKIahHrng= cloud.google.com/go/asset v1.17.2 h1:xgFnBP3luSbUcC9RWJvb3Zkt+y/wW6PKwPHr3ssnIP8= -cloud.google.com/go/asset v1.17.2/go.mod h1:SVbzde67ehddSoKf5uebOD1sYw8Ab/jD/9EIeWg99q4= cloud.google.com/go/asset v1.19.5 h1:/R2XZS6lR8oj/Y3L+epD2yy7mf44Zp62H4xZ4vzaR/Y= cloud.google.com/go/asset v1.19.5/go.mod h1:sqyLOYaLLfc4ACcn3YxqHno+J7lRt9NJTdO50zCUcY0= cloud.google.com/go/assuredworkloads v1.10.0 h1:VLGnVFta+N4WM+ASHbhc14ZOItOabDLH1MSoDv+Xuag= cloud.google.com/go/assuredworkloads v1.11.1 h1:yaO0kwS+SnhVSTF7BqTyVGt3DTocI6Jqo+S3hHmCwNk= -cloud.google.com/go/assuredworkloads v1.11.2/go.mod h1:O1dfr+oZJMlE6mw0Bp0P1KZSlj5SghMBvTpZqIcUAW4= -cloud.google.com/go/assuredworkloads v1.11.3/go.mod h1:vEjfTKYyRUaIeA0bsGJceFV2JKpVRgyG2op3jfa59Zs= -cloud.google.com/go/assuredworkloads v1.11.4/go.mod h1:4pwwGNwy1RP0m+y12ef3Q/8PaiWrIDQ6nD2E8kvWI9U= cloud.google.com/go/assuredworkloads v1.11.5 h1:gCrN3IyvqY3cP0wh2h43d99CgH3G+WYs9CeuFVKChR8= -cloud.google.com/go/assuredworkloads v1.11.5/go.mod h1:FKJ3g3ZvkL2D7qtqIGnDufFkHxwIpNM9vtmhvt+6wqk= cloud.google.com/go/assuredworkloads v1.11.11 h1:pwZp9o8aF5QmX4Z0YNlRe1ZOUzDw0UALmkem3aPobZc= cloud.google.com/go/assuredworkloads v1.11.11/go.mod h1:vaYs6+MHqJvLKYgZBOsuuOhBgNNIguhRU0Kt7JTGcnI= cloud.google.com/go/auth v0.3.0/go.mod h1:lBv6NKTWp8E3LPzmO1TbiiRKc4drLOfHsgmlH9ogv5w= @@ -144,359 +75,167 @@ cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRk cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= cloud.google.com/go/automl v1.12.0 h1:50VugllC+U4IGl3tDNcZaWvApHBTrn/TvyHDJ0wM+Uw= cloud.google.com/go/automl v1.13.1 h1:iP9iQurb0qbz+YOOMfKSEjhONA/WcoOIjt6/m+6pIgo= -cloud.google.com/go/automl v1.13.2/go.mod h1:gNY/fUmDEN40sP8amAX3MaXkxcqPIn7F1UIIPZpy4Mg= -cloud.google.com/go/automl v1.13.3/go.mod h1:Y8KwvyAZFOsMAPqUCfNu1AyclbC6ivCUF/MTwORymyY= -cloud.google.com/go/automl v1.13.4/go.mod h1:ULqwX/OLZ4hBVfKQaMtxMSTlPx0GqGbWN8uA/1EqCP8= cloud.google.com/go/automl v1.13.5 h1:ijiJy9sYWh75WrqImXsfWc1e3HR3iO+ef9fvW03Ig/4= -cloud.google.com/go/automl v1.13.5/go.mod h1:MDw3vLem3yh+SvmSgeYUmUKqyls6NzSumDm9OJ3xJ1Y= cloud.google.com/go/automl v1.13.11 h1:FBCLjGS+Did/wtRHqyS055bRs/EJXx3meTvHPcdZgk8= cloud.google.com/go/automl v1.13.11/go.mod h1:oMJdXRDOVC+Eq3PnGhhxSut5Hm9TSyVx1aLEOgerOw8= cloud.google.com/go/baremetalsolution v0.5.0 h1:2AipdYXL0VxMboelTTw8c1UJ7gYu35LZYUbuRv9Q28s= -cloud.google.com/go/baremetalsolution v1.1.1/go.mod h1:D1AV6xwOksJMV4OSlWHtWuFNZZYujJknMAP4Qa27QIA= cloud.google.com/go/baremetalsolution v1.2.0 h1:3zztyuQHjfU0C0qEsI9LkC3kf5/TQQ3jUJhbmetUoRA= -cloud.google.com/go/baremetalsolution v1.2.0/go.mod h1:68wi9AwPYkEWIUT4SvSGS9UJwKzNpshjHsH4lzk8iOw= -cloud.google.com/go/baremetalsolution v1.2.1/go.mod h1:3qKpKIw12RPXStwQXcbhfxVj1dqQGEvcmA+SX/mUR88= -cloud.google.com/go/baremetalsolution v1.2.2/go.mod h1:O5V6Uu1vzVelYahKfwEWRMaS3AbCkeYHy3145s1FkhM= -cloud.google.com/go/baremetalsolution v1.2.3/go.mod h1:/UAQ5xG3faDdy180rCUv47e0jvpp3BFxT+Cl0PFjw5g= cloud.google.com/go/baremetalsolution v1.2.4 h1:LFydisRmS7hQk9P/YhekwuZGqb45TW4QavcrMToWo5A= -cloud.google.com/go/baremetalsolution v1.2.4/go.mod h1:BHCmxgpevw9IEryE99HbYEfxXkAEA3hkMJbYYsHtIuY= cloud.google.com/go/baremetalsolution v1.2.10 h1:VvBiXT9QJ4VpNVyfzHhLScY1aymZxpQgOa20yUvgphw= cloud.google.com/go/baremetalsolution v1.2.10/go.mod h1:eO2c2NMRy5ytcNPhG78KPsWGNsX5W/tUsCOWmYihx6I= cloud.google.com/go/batch v0.7.0 h1:YbMt0E6BtqeD5FvSv1d56jbVsWEzlGm55lYte+M6Mzs= -cloud.google.com/go/batch v1.3.1/go.mod h1:VguXeQKXIYaeeIYbuozUmBR13AfL4SJP7IltNPS+A4A= -cloud.google.com/go/batch v1.4.1/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= cloud.google.com/go/batch v1.5.0 h1:xjhQeEcBXJDxW2cBZEQgCKlGeXRlVJildU67rtoBY6A= -cloud.google.com/go/batch v1.5.0/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.5.1/go.mod h1:RpBuIYLkQu8+CWDk3dFD/t/jOCGuUpkpX+Y0n1Xccs8= -cloud.google.com/go/batch v1.6.1/go.mod h1:urdpD13zPe6YOK+6iZs/8/x2VBRofvblLpx0t57vM98= -cloud.google.com/go/batch v1.6.3/go.mod h1:J64gD4vsNSA2O5TtDB5AAux3nJ9iV8U3ilg3JDBYejU= -cloud.google.com/go/batch v1.7.0/go.mod h1:J64gD4vsNSA2O5TtDB5AAux3nJ9iV8U3ilg3JDBYejU= cloud.google.com/go/batch v1.8.0 h1:2HK4JerwVaIcCh/lJiHwh6+uswPthiMMWhiSWLELayk= -cloud.google.com/go/batch v1.8.0/go.mod h1:k8V7f6VE2Suc0zUM4WtoibNrA6D3dqBpB+++e3vSGYc= cloud.google.com/go/batch v1.9.2 h1:o1RAjc0ExGAAm41YB9LbJZyJDgZR4M6SKyITsd/Smr4= cloud.google.com/go/batch v1.9.2/go.mod h1:smqwS4sleDJVAEzBt/TzFfXLktmWjFNugGDWl8coKX4= cloud.google.com/go/beyondcorp v0.5.0 h1:UkY2BTZkEUAVrgqnSdOJ4p3y9ZRBPEe1LkjgC8Bj/Pc= cloud.google.com/go/beyondcorp v1.0.0 h1:VPg+fZXULQjs8LiMeWdLaB5oe8G9sEoZ0I0j6IMiG1Q= -cloud.google.com/go/beyondcorp v1.0.0/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.1/go.mod h1:zl/rWWAFVeV+kx+X2Javly7o1EIQThU4WlkynffL/lk= -cloud.google.com/go/beyondcorp v1.0.2/go.mod h1:m8cpG7caD+5su+1eZr+TSvF6r21NdLJk4f9u4SP2Ntc= -cloud.google.com/go/beyondcorp v1.0.3/go.mod h1:HcBvnEd7eYr+HGDd5ZbuVmBYX019C6CEXBonXbCVwJo= cloud.google.com/go/beyondcorp v1.0.4 h1:qs0J0O9Ol2h1yA0AU+r7l3hOCPzs2MjE1d6d/kaHIKo= -cloud.google.com/go/beyondcorp v1.0.4/go.mod h1:Gx8/Rk2MxrvWfn4WIhHIG1NV7IBfg14pTKv1+EArVcc= cloud.google.com/go/beyondcorp v1.0.10 h1:K4blSIQZn3YO4F4LmvWrH52pb8Y0L3NOrwkf22+x67M= cloud.google.com/go/beyondcorp v1.0.10/go.mod h1:G09WxvxJASbxbrzaJUMVvNsB1ZiaKxpbtkjiFtpDtbo= cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA= cloud.google.com/go/bigquery v1.50.0 h1:RscMV6LbnAmhAzD893Lv9nXXy2WCaJmbxYPWDLbGqNQ= -cloud.google.com/go/bigquery v1.53.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.55.0/go.mod h1:9Y5I3PN9kQWuid6183JFhOGOW3GcirA5LpsKCUn+2ec= cloud.google.com/go/bigquery v1.56.0 h1:LHIc9E7Kw+ftFpQFKzZYBB88IAFz7qONawXXx0F3QBo= -cloud.google.com/go/bigquery v1.56.0/go.mod h1:KDcsploXTEY7XT3fDQzMUZlpQLHzE4itubHrnmhUrZA= -cloud.google.com/go/bigquery v1.57.1/go.mod h1:iYzC0tGVWt1jqSzBHqCr3lrRn0u13E8e+AqowBsDgug= -cloud.google.com/go/bigquery v1.58.0/go.mod h1:0eh4mWNY0KrBTjUzLjoYImapGORq9gEPT7MWjCy9lik= cloud.google.com/go/bigquery v1.59.1 h1:CpT+/njKuKT3CEmswm6IbhNu9u35zt5dO4yPDLW+nG4= -cloud.google.com/go/bigquery v1.59.1/go.mod h1:VP1UJYgevyTwsV7desjzNzDND5p6hZB+Z8gZJN1GQUc= cloud.google.com/go/bigquery v1.62.0 h1:SYEA2f7fKqbSRRBHb7g0iHTtZvtPSPYdXfmqsjpsBwo= cloud.google.com/go/bigquery v1.62.0/go.mod h1:5ee+ZkF1x/ntgCsFQJAQTM3QkAZOecfCmvxhkJsWRSA= cloud.google.com/go/bigtable v1.27.2-0.20240802230159-f371928b558f h1:UR2/6M/bSN8PPQlhaq+57w21VZLcEvq4ujsHd1p/G2s= cloud.google.com/go/bigtable v1.27.2-0.20240802230159-f371928b558f/go.mod h1:avmXcmxVbLJAo9moICRYMgDyTTPoV0MA0lHKnyqV4fQ= cloud.google.com/go/billing v1.13.0 h1:JYj28UYF5w6VBAh0gQYlgHJ/OD1oA+JgW29YZQU+UHM= -cloud.google.com/go/billing v1.17.0/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= cloud.google.com/go/billing v1.17.1 h1:YSu8a17uJ6sOnlrnJVOBWkimmHZGtSpSkyElv9+JjRM= -cloud.google.com/go/billing v1.17.1/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.2/go.mod h1:u/AdV/3wr3xoRBk5xvUzYMS1IawOAPwQMuHgHMdljDg= -cloud.google.com/go/billing v1.17.3/go.mod h1:z83AkoZ7mZwBGT3yTnt6rSGI1OOsHSIi6a5M3mJ8NaU= -cloud.google.com/go/billing v1.17.4/go.mod h1:5DOYQStCxquGprqfuid/7haD7th74kyMBHkjO/OvDtk= -cloud.google.com/go/billing v1.18.0/go.mod h1:5DOYQStCxquGprqfuid/7haD7th74kyMBHkjO/OvDtk= cloud.google.com/go/billing v1.18.2 h1:oWUEQvuC4JvtnqLZ35zgzdbuHt4Itbftvzbe6aEyFdE= -cloud.google.com/go/billing v1.18.2/go.mod h1:PPIwVsOOQ7xzbADCwNe8nvK776QpfrOAUkvKjCUcpSE= cloud.google.com/go/billing v1.18.9 h1:sGRWx7PvsfHuZyx151Xr6CrORIgjvCMO4GRabihSdQQ= cloud.google.com/go/billing v1.18.9/go.mod h1:bKTnh8MBfCMUT1fzZ936CPN9rZG7ZEiHB2J3SjIjByc= cloud.google.com/go/binaryauthorization v1.5.0 h1:d3pMDBCCNivxt5a4eaV7FwL7cSH0H7RrEnFrTb1QKWs= cloud.google.com/go/binaryauthorization v1.7.0 h1:7L6uUWo/xNCfdVNnnzh2M4x5YA732YPgqRdCG8aKVAU= -cloud.google.com/go/binaryauthorization v1.7.0/go.mod h1:Zn+S6QqTMn6odcMU1zDZCJxPjU2tZPV1oDl45lWY154= -cloud.google.com/go/binaryauthorization v1.7.1/go.mod h1:GTAyfRWYgcbsP3NJogpV3yeunbUIjx2T9xVeYovtURE= -cloud.google.com/go/binaryauthorization v1.7.2/go.mod h1:kFK5fQtxEp97m92ziy+hbu+uKocka1qRRL8MVJIgjv0= -cloud.google.com/go/binaryauthorization v1.7.3/go.mod h1:VQ/nUGRKhrStlGr+8GMS8f6/vznYLkdK5vaKfdCIpvU= -cloud.google.com/go/binaryauthorization v1.8.0/go.mod h1:VQ/nUGRKhrStlGr+8GMS8f6/vznYLkdK5vaKfdCIpvU= cloud.google.com/go/binaryauthorization v1.8.1 h1:1jcyh2uIUwSZkJ/JmL8kd5SUkL/Krbv8zmYLEbAz6kY= -cloud.google.com/go/binaryauthorization v1.8.1/go.mod h1:1HVRyBerREA/nhI7yLang4Zn7vfNVA3okoAR9qYQJAQ= cloud.google.com/go/binaryauthorization v1.8.7 h1:ItT9uR/0/ok2Ru3LCcbSIBUPsKqTA49ZmxCupqQaeFo= cloud.google.com/go/binaryauthorization v1.8.7/go.mod h1:cRj4teQhOme5SbWQa96vTDATQdMftdT5324BznxANtg= cloud.google.com/go/certificatemanager v1.6.0 h1:5C5UWeSt8Jkgp7OWn2rCkLmYurar/vIWIoSQ2+LaTOc= cloud.google.com/go/certificatemanager v1.7.1 h1:uKsohpE0hiobx1Eak9jNcPCznwfB6gvyQCcS28Ah9E8= -cloud.google.com/go/certificatemanager v1.7.2/go.mod h1:15SYTDQMd00kdoW0+XY5d9e+JbOPjp24AvF48D8BbcQ= -cloud.google.com/go/certificatemanager v1.7.3/go.mod h1:T/sZYuC30PTag0TLo28VedIRIj1KPGcOQzjWAptHa00= -cloud.google.com/go/certificatemanager v1.7.4/go.mod h1:FHAylPe/6IIKuaRmHbjbdLhGhVQ+CWHSD5Jq0k4+cCE= cloud.google.com/go/certificatemanager v1.7.5 h1:UMBr/twXvH3jcT5J5/YjRxf2tvwTYIfrpemTebe0txc= -cloud.google.com/go/certificatemanager v1.7.5/go.mod h1:uX+v7kWqy0Y3NG/ZhNvffh0kuqkKZIXdvlZRO7z0VtM= cloud.google.com/go/certificatemanager v1.8.5 h1:ASC9N81NU8JnGzi9kiY2QTqtTgOziwGv48sjt3YG420= cloud.google.com/go/certificatemanager v1.8.5/go.mod h1:r2xINtJ/4xSz85VsqvjY53qdlrdCjyniib9Jp98ZKKM= cloud.google.com/go/channel v1.12.0 h1:GpcQY5UJKeOekYgsX3QXbzzAc/kRGtBq43fTmyKe6Uw= cloud.google.com/go/channel v1.17.0 h1:Hy2EaOiOB7BS1IJmg2lLilEo8uMfFWTy7RgjTzbUqjM= -cloud.google.com/go/channel v1.17.0/go.mod h1:RpbhJsGi/lXWAUM1eF4IbQGbsfVlg2o8Iiy2/YLfVT0= -cloud.google.com/go/channel v1.17.1/go.mod h1:xqfzcOZAcP4b/hUDH0GkGg1Sd5to6di1HOJn/pi5uBQ= -cloud.google.com/go/channel v1.17.2/go.mod h1:aT2LhnftnyfQceFql5I/mP8mIbiiJS4lWqgXA815zMk= -cloud.google.com/go/channel v1.17.3/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= -cloud.google.com/go/channel v1.17.4/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= cloud.google.com/go/channel v1.17.5 h1:/omiBnyFjm4S1ETHoOmJbL7LH7Ljcei4rYG6Sj3hc80= -cloud.google.com/go/channel v1.17.5/go.mod h1:FlpaOSINDAXgEext0KMaBq/vwpLMkkPAw9b2mApQeHc= cloud.google.com/go/channel v1.17.11 h1:AkKyMl2pSoJxBQtjAd6LYOtMgOaCl/kuiKoSg/Gf/H4= cloud.google.com/go/channel v1.17.11/go.mod h1:gjWCDBcTGQce/BSMoe2lAqhlq0dIRiZuktvBKXUawp0= cloud.google.com/go/cloudbuild v1.9.0 h1:GHQCjV4WlPPVU/j3Rlpc8vNIDwThhd1U9qSY/NPZdko= -cloud.google.com/go/cloudbuild v1.13.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= cloud.google.com/go/cloudbuild v1.14.0 h1:YTMxmFra7eIjKFgnyQUxOwWNseNqeO38kGh7thy7v4s= -cloud.google.com/go/cloudbuild v1.14.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.1/go.mod h1:K7wGc/3zfvmYWOWwYTgF/d/UVJhS4pu+HAy7PL7mCsU= -cloud.google.com/go/cloudbuild v1.14.2/go.mod h1:Bn6RO0mBYk8Vlrt+8NLrru7WXlQ9/RDWz2uo5KG1/sg= -cloud.google.com/go/cloudbuild v1.14.3/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= -cloud.google.com/go/cloudbuild v1.15.0/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= cloud.google.com/go/cloudbuild v1.15.1 h1:ZB6oOmJo+MTov9n629fiCrO9YZPOg25FZvQ7gIHu5ng= -cloud.google.com/go/cloudbuild v1.15.1/go.mod h1:gIofXZSu+XD2Uy+qkOrGKEx45zd7s28u/k8f99qKals= cloud.google.com/go/cloudbuild v1.16.5 h1:RvK5r8JBCLNg9XmfGPy05t3bmhLJV3Xh3sDHGHAATgM= cloud.google.com/go/cloudbuild v1.16.5/go.mod h1:HXLpZ8QeYZgmDIWpbl9Gs22p6o6uScgQ/cV9HF9cIZU= cloud.google.com/go/clouddms v1.5.0 h1:E7v4TpDGUyEm1C/4KIrpVSOCTm0P6vWdHT0I4mostRA= cloud.google.com/go/clouddms v1.7.0 h1:vTcaFaFZTZZ11gXB6aZHdAx+zn30P8YJw4X/S3NC+VQ= -cloud.google.com/go/clouddms v1.7.0/go.mod h1:MW1dC6SOtI/tPNCciTsXtsGNEM0i0OccykPvv3hiYeM= -cloud.google.com/go/clouddms v1.7.1/go.mod h1:o4SR8U95+P7gZ/TX+YbJxehOCsM+fe6/brlrFquiszk= -cloud.google.com/go/clouddms v1.7.2/go.mod h1:Rk32TmWmHo64XqDvW7jgkFQet1tUKNVzs7oajtJT3jU= -cloud.google.com/go/clouddms v1.7.3/go.mod h1:fkN2HQQNUYInAU3NQ3vRLkV2iWs8lIdmBKOx4nrL6Hc= cloud.google.com/go/clouddms v1.7.4 h1:Sr0Zo5EAcPQiCBgHWICg3VGkcdS/LLP1d9SR7qQBM/s= -cloud.google.com/go/clouddms v1.7.4/go.mod h1:RdrVqoFG9RWI5AvZ81SxJ/xvxPdtcRhFotwdE79DieY= cloud.google.com/go/clouddms v1.7.10 h1:EA3y9v5TZiAlwgHJh2vPOEelqYiCxXBYZRCNnGK5q+g= cloud.google.com/go/clouddms v1.7.10/go.mod h1:PzHELq0QDyA7VaD9z6mzh2mxeBz4kM6oDe8YxMxd4RA= cloud.google.com/go/cloudtasks v1.10.0 h1:uK5k6abf4yligFgYFnG0ni8msai/dSv6mDmiBulU0hU= cloud.google.com/go/cloudtasks v1.12.1 h1:cMh9Q6dkvh+Ry5LAPbD/U2aw6KAqdiU6FttwhbTo69w= -cloud.google.com/go/cloudtasks v1.12.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.2/go.mod h1:A7nYkjNlW2gUoROg1kvJrQGhJP/38UaWwsnuBDOBVUk= -cloud.google.com/go/cloudtasks v1.12.3/go.mod h1:GPVXhIOSGEaR+3xT4Fp72ScI+HjHffSS4B8+BaBB5Ys= -cloud.google.com/go/cloudtasks v1.12.4/go.mod h1:BEPu0Gtt2dU6FxZHNqqNdGqIG86qyWKBPGnsb7udGY0= cloud.google.com/go/cloudtasks v1.12.6 h1:EUt1hIZ9bLv8Iz9yWaCrqgMnIU+Tdh0yXM1MMVGhjfE= -cloud.google.com/go/cloudtasks v1.12.6/go.mod h1:b7c7fe4+TJsFZfDyzO51F7cjq7HLUlRi/KZQLQjDsaY= cloud.google.com/go/cloudtasks v1.12.12 h1:p91Brp4nJkyRRI/maYdO+FT+e9tU+2xoGr20s2rvalU= cloud.google.com/go/cloudtasks v1.12.12/go.mod h1:8UmM+duMrQpzzRREo0i3x3TrFjsgI/3FQw3664/JblA= cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= -cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= -cloud.google.com/go/compute v1.23.4/go.mod h1:/EJMj55asU6kAFnuZET8zqgwgJ9FvXWXOkkfQZa4ioI= cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= -cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= cloud.google.com/go/compute v1.27.4 h1:XM8ulx6crjdl09XBfji7viFgZOEQuIxBwKmjRH9Rtmc= cloud.google.com/go/compute v1.27.4/go.mod h1:7JZS+h21ERAGHOy5qb7+EPyXlQwzshzrx1x6L9JhTqU= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/contactcenterinsights v1.6.0 h1:jXIpfcH/VYSE1SYcPzO0n1VVb+sAamiLOgCw45JbOQk= -cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= cloud.google.com/go/contactcenterinsights v1.11.0 h1:u3GlrTrchHF91z58TBSdQ80G6UbVvF6Egb4utrjSvtI= -cloud.google.com/go/contactcenterinsights v1.11.0/go.mod h1:hutBdImE4XNZ1NV4vbPJKSFOnQruhC5Lj9bZqWMTKiU= -cloud.google.com/go/contactcenterinsights v1.11.1/go.mod h1:FeNP3Kg8iteKM80lMwSk3zZZKVxr+PGnAId6soKuXwE= -cloud.google.com/go/contactcenterinsights v1.11.2/go.mod h1:A9PIR5ov5cRcd28KlDbmmXE8Aay+Gccer2h4wzkYFso= -cloud.google.com/go/contactcenterinsights v1.11.3/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= -cloud.google.com/go/contactcenterinsights v1.12.0/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= -cloud.google.com/go/contactcenterinsights v1.12.1/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= cloud.google.com/go/contactcenterinsights v1.13.0 h1:6Vs/YnDG5STGjlWMEjN/xtmft7MrOTOnOZYUZtGTx0w= -cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI= cloud.google.com/go/contactcenterinsights v1.13.6 h1:LRcI5RAlLIbjwT312sGt+gyXcaXTr+v7uEQlNyArO9g= cloud.google.com/go/contactcenterinsights v1.13.6/go.mod h1:mL+DbN3pMQGaAbDC4wZhryLciwSwHf5Tfk4Itr72Zyk= cloud.google.com/go/container v1.15.0 h1:NKlY/wCDapfVZlbVVaeuu2UZZED5Dy1z4Zx1KhEzm8c= -cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= cloud.google.com/go/container v1.26.0 h1:SszQdI0qlyKsImz8/l26rpTZMyqvaH9yfua7rirDZvY= -cloud.google.com/go/container v1.26.0/go.mod h1:YJCmRet6+6jnYYRS000T6k0D0xUXQgBSaJ7VwI8FBj4= -cloud.google.com/go/container v1.26.1/go.mod h1:5smONjPRUxeEpDG7bMKWfDL4sauswqEtnBK1/KKpR04= -cloud.google.com/go/container v1.26.2/go.mod h1:YlO84xCt5xupVbLaMY4s3XNE79MUJ+49VmkInr6HvF4= -cloud.google.com/go/container v1.27.1/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= -cloud.google.com/go/container v1.28.0/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= -cloud.google.com/go/container v1.29.0/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= -cloud.google.com/go/container v1.30.1/go.mod h1:vkbfX0EnAKL/vgVECs5BZn24e1cJROzgszJirRKQ4Bg= cloud.google.com/go/container v1.31.0 h1:MAaNH7VRNPWEhvqOypq2j+7ONJKrKzon4v9nS3nLZe0= -cloud.google.com/go/container v1.31.0/go.mod h1:7yABn5s3Iv3lmw7oMmyGbeV6tQj86njcTijkkGuvdZA= cloud.google.com/go/container v1.38.0 h1:GP5zLamfvPZeOTifnGBSER/br76D5eJ97xhcXXrh5tM= cloud.google.com/go/container v1.38.0/go.mod h1:U0uPBvkVWOJGY/0qTVuPS7NeafFEUsHSPqT5pB8+fCY= cloud.google.com/go/containeranalysis v0.9.0 h1:EQ4FFxNaEAg8PqQCO7bVQfWz9NVwZCUKaM1b3ycfx3U= cloud.google.com/go/containeranalysis v0.11.0 h1:/EsoP+UTIjvl4yqrLA4WgUG83kwQhqZmbXEfqirT2LM= -cloud.google.com/go/containeranalysis v0.11.0/go.mod h1:4n2e99ZwpGxpNcz+YsFT1dfOHPQFGcAC8FN2M2/ne/U= -cloud.google.com/go/containeranalysis v0.11.1/go.mod h1:rYlUOM7nem1OJMKwE1SadufX0JP3wnXj844EtZAwWLY= -cloud.google.com/go/containeranalysis v0.11.2/go.mod h1:xibioGBC1MD2j4reTyV1xY1/MvKaz+fyM9ENWhmIeP8= -cloud.google.com/go/containeranalysis v0.11.3/go.mod h1:kMeST7yWFQMGjiG9K7Eov+fPNQcGhb8mXj/UcTiWw9U= cloud.google.com/go/containeranalysis v0.11.4 h1:doJ0M1ljS4hS0D2UbHywlHGwB7sQLNrt9vFk9Zyi7vY= -cloud.google.com/go/containeranalysis v0.11.4/go.mod h1:cVZT7rXYBS9NG1rhQbWL9pWbXCKHWJPYraE8/FTSYPE= cloud.google.com/go/containeranalysis v0.12.1 h1:Xb8Eu7vVmWR5nAl5WPTGTx/dCr+R+oF7VbuYV47EHHs= cloud.google.com/go/containeranalysis v0.12.1/go.mod h1:+/lcJIQSFt45TC0N9Nq7/dPbl0isk6hnC4EvBBqyXsM= cloud.google.com/go/datacatalog v1.13.0 h1:4H5IJiyUE0X6ShQBqgFFZvGGcrwGVndTwUSLP4c52gw= -cloud.google.com/go/datacatalog v1.16.0/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.17.1/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= cloud.google.com/go/datacatalog v1.18.0 h1:AZHHhoSEK4n3yMsHFLibUjMX5jQz/0FcKKD4T1vxyGM= -cloud.google.com/go/datacatalog v1.18.0/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.1/go.mod h1:TzAWaz+ON1tkNr4MOcak8EBHX7wIRX/gZKM+yTVsv+A= -cloud.google.com/go/datacatalog v1.18.2/go.mod h1:SPVgWW2WEMuWHA+fHodYjmxPiMqcOiWfhc9OD5msigk= -cloud.google.com/go/datacatalog v1.18.3/go.mod h1:5FR6ZIF8RZrtml0VUao22FxhdjkoG+a0866rEnObryM= -cloud.google.com/go/datacatalog v1.19.0/go.mod h1:5FR6ZIF8RZrtml0VUao22FxhdjkoG+a0866rEnObryM= -cloud.google.com/go/datacatalog v1.19.2/go.mod h1:2YbODwmhpLM4lOFe3PuEhHK9EyTzQJ5AXgIy7EDKTEE= cloud.google.com/go/datacatalog v1.19.3 h1:A0vKYCQdxQuV4Pi0LL9p39Vwvg4jH5yYveMv50gU5Tw= -cloud.google.com/go/datacatalog v1.19.3/go.mod h1:ra8V3UAsciBpJKQ+z9Whkxzxv7jmQg1hfODr3N3YPJ4= cloud.google.com/go/datacatalog v1.21.0 h1:vl0pQT9TZ5rKi9e69FgtXNCR7I8MVRj4+CnbeXhz6UQ= cloud.google.com/go/datacatalog v1.21.0/go.mod h1:DB0QWF9nelpsbB0eR/tA0xbHZZMvpoFD1XFy3Qv/McI= cloud.google.com/go/dataflow v0.8.0 h1:eYyD9o/8Nm6EttsKZaEGD84xC17bNgSKCu0ZxwqUbpg= cloud.google.com/go/dataflow v0.9.1 h1:VzG2tqsk/HbmOtq/XSfdF4cBvUWRK+S+oL9k4eWkENQ= -cloud.google.com/go/dataflow v0.9.2/go.mod h1:vBfdBZ/ejlTaYIGB3zB4T08UshH70vbtZeMD+urnUSo= -cloud.google.com/go/dataflow v0.9.3/go.mod h1:HI4kMVjcHGTs3jTHW/kv3501YW+eloiJSLxkJa/vqFE= -cloud.google.com/go/dataflow v0.9.4/go.mod h1:4G8vAkHYCSzU8b/kmsoR2lWyHJD85oMJPHMtan40K8w= cloud.google.com/go/dataflow v0.9.5 h1:RYHtcPhmE664+F0Je46p+NvFbG8z//KCXp+uEqB4jZU= -cloud.google.com/go/dataflow v0.9.5/go.mod h1:udl6oi8pfUHnL0z6UN9Lf9chGqzDMVqcYTcZ1aPnCZQ= cloud.google.com/go/dataflow v0.9.11 h1:YIhStasKFDESaUdpnsHsp/5bACYL/yvW0OuZ6zPQ6nY= cloud.google.com/go/dataflow v0.9.11/go.mod h1:CCLufd7I4pPfyp54qMgil/volrL2ZKYjXeYLfQmBGJs= cloud.google.com/go/dataform v0.7.0 h1:Dyk+fufup1FR6cbHjFpMuP4SfPiF3LI3JtoIIALoq48= cloud.google.com/go/dataform v0.8.1 h1:xcWso0hKOoxeW72AjBSIp/UfkvpqHNzzS0/oygHlcqY= -cloud.google.com/go/dataform v0.8.2/go.mod h1:X9RIqDs6NbGPLR80tnYoPNiO1w0wenKTb8PxxlhTMKM= -cloud.google.com/go/dataform v0.8.3/go.mod h1:8nI/tvv5Fso0drO3pEjtowz58lodx8MVkdV2q0aPlqg= -cloud.google.com/go/dataform v0.9.1/go.mod h1:pWTg+zGQ7i16pyn0bS1ruqIE91SdL2FDMvEYu/8oQxs= cloud.google.com/go/dataform v0.9.2 h1:5e4eqGrd0iDTCg4Q+VlAao5j2naKAA7xRurNtwmUknU= -cloud.google.com/go/dataform v0.9.2/go.mod h1:S8cQUwPNWXo7m/g3DhWHsLBoufRNn9EgFrMgne2j7cI= cloud.google.com/go/dataform v0.9.8 h1:oNtTx9PdH7aPnvrKIsPrh+Y6Mw+8Bw5/ZgLWVHAev/c= cloud.google.com/go/dataform v0.9.8/go.mod h1:cGJdyVdunN7tkeXHPNosuMzmryx55mp6cInYBgxN3oA= cloud.google.com/go/datafusion v1.6.0 h1:sZjRnS3TWkGsu1LjYPFD/fHeMLZNXDK6PDHi2s2s/bk= cloud.google.com/go/datafusion v1.7.1 h1:eX9CZoyhKQW6g1Xj7+RONeDj1mV8KQDKEB9KLELX9/8= -cloud.google.com/go/datafusion v1.7.2/go.mod h1:62K2NEC6DRlpNmI43WHMWf9Vg/YvN6QVi8EVwifElI0= -cloud.google.com/go/datafusion v1.7.3/go.mod h1:eoLt1uFXKGBq48jy9LZ+Is8EAVLnmn50lNncLzwYokE= -cloud.google.com/go/datafusion v1.7.4/go.mod h1:BBs78WTOLYkT4GVZIXQCZT3GFpkpDN4aBY4NDX/jVlM= cloud.google.com/go/datafusion v1.7.5 h1:HQ/BUOP8OIGJxuztpYvNvlb+/U+/Bfs9SO8tQbh61fk= -cloud.google.com/go/datafusion v1.7.5/go.mod h1:bYH53Oa5UiqahfbNK9YuYKteeD4RbQSNMx7JF7peGHc= cloud.google.com/go/datafusion v1.7.11 h1:GVcVisjVKmoj1eNnIp3G3qjjo+7koHr0Kf8tF6Cjqe0= cloud.google.com/go/datafusion v1.7.11/go.mod h1:aU9zoBHgYmoPp4dzccgm/Gi4xWDMXodSZlNZ4WNeptw= cloud.google.com/go/datalabeling v0.7.0 h1:ch4qA2yvddGRUrlfwrNJCr79qLqhS9QBwofPHfFlDIk= cloud.google.com/go/datalabeling v0.8.1 h1:zxsCD/BLKXhNuRssen8lVXChUj8VxF3ofN06JfdWOXw= -cloud.google.com/go/datalabeling v0.8.2/go.mod h1:cyDvGHuJWu9U/cLDA7d8sb9a0tWLEletStu2sTmg3BE= -cloud.google.com/go/datalabeling v0.8.3/go.mod h1:tvPhpGyS/V7lqjmb3V0TaDdGvhzgR1JoW7G2bpi2UTI= -cloud.google.com/go/datalabeling v0.8.4/go.mod h1:Z1z3E6LHtffBGrNUkKwbwbDxTiXEApLzIgmymj8A3S8= cloud.google.com/go/datalabeling v0.8.5 h1:GpIFRdm0qIZNsxqURFJwHt0ZBJZ0nF/mUVEigR7PH/8= -cloud.google.com/go/datalabeling v0.8.5/go.mod h1:IABB2lxQnkdUbMnQaOl2prCOfms20mcPxDBm36lps+s= cloud.google.com/go/datalabeling v0.8.11 h1:7jSuJEAc7upeMmyICzqfU0OyxUV38JSWW+8r5GmoHX0= cloud.google.com/go/datalabeling v0.8.11/go.mod h1:6IGUV3z7hlkAU5ndKVshv/8z+7pxE+k0qXsEjyzO1Xg= cloud.google.com/go/dataplex v1.6.0 h1:RvoZ5T7gySwm1CHzAw7yY1QwwqaGswunmqEssPxU/AM= -cloud.google.com/go/dataplex v1.9.0/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= cloud.google.com/go/dataplex v1.9.1 h1:wqPAP1vRskOoWwNka1yey2wxxCrxRrcxJf78MyFvrbs= -cloud.google.com/go/dataplex v1.9.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.10.1/go.mod h1:1MzmBv8FvjYfc7vDdxhnLFNskikkB+3vl475/XdCDhs= -cloud.google.com/go/dataplex v1.10.2/go.mod h1:xdC8URdTrCrZMW6keY779ZT1cTOfV8KEPNsw+LTRT1Y= -cloud.google.com/go/dataplex v1.11.1/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.11.2/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.13.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.14.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.14.1/go.mod h1:bWxQAbg6Smg+sca2+Ex7s8D9a5qU6xfXtwmq4BVReps= cloud.google.com/go/dataplex v1.14.2 h1:fxIfdU8fxzR3clhOoNI7XFppvAmndxDu1AMH+qX9WKQ= -cloud.google.com/go/dataplex v1.14.2/go.mod h1:0oGOSFlEKef1cQeAHXy4GZPB/Ife0fz/PxBf+ZymA2U= cloud.google.com/go/dataplex v1.18.2 h1:bIU1r1YnsX6P1qTnaRnah/STHoLJ3EHUZVCjJl2+1Eo= cloud.google.com/go/dataplex v1.18.2/go.mod h1:NuBpJJMGGQn2xctX+foHEDKRbizwuiHJamKvvSteY3Q= cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= -cloud.google.com/go/dataproc/v2 v2.0.1/go.mod h1:7Ez3KRHdFGcfY7GcevBbvozX+zyWGcwLJvvAMwCaoZ4= cloud.google.com/go/dataproc/v2 v2.2.0 h1:jKijbdsERm2hy/5dFl/LeQN+7CNssLdGXQYBMvMH/M4= -cloud.google.com/go/dataproc/v2 v2.2.0/go.mod h1:lZR7AQtwZPvmINx5J87DSOOpTfof9LVZju6/Qo4lmcY= -cloud.google.com/go/dataproc/v2 v2.2.1/go.mod h1:QdAJLaBjh+l4PVlVZcmrmhGccosY/omC1qwfQ61Zv/o= -cloud.google.com/go/dataproc/v2 v2.2.2/go.mod h1:aocQywVmQVF4i8CL740rNI/ZRpsaaC1Wh2++BJ7HEJ4= -cloud.google.com/go/dataproc/v2 v2.2.3/go.mod h1:G5R6GBc9r36SXv/RtZIVfB8SipI+xVn0bX5SxUzVYbY= -cloud.google.com/go/dataproc/v2 v2.3.0/go.mod h1:G5R6GBc9r36SXv/RtZIVfB8SipI+xVn0bX5SxUzVYbY= cloud.google.com/go/dataproc/v2 v2.4.0 h1:/u81Fd+BvCLp+xjctI1DiWVJn6cn9/s3Akc8xPH02yk= -cloud.google.com/go/dataproc/v2 v2.4.0/go.mod h1:3B1Ht2aRB8VZIteGxQS/iNSJGzt9+CA0WGnDVMEm7Z4= cloud.google.com/go/dataproc/v2 v2.5.3 h1:OgTfUARkF8AfkNmoyT0wyLLXNh4LbT3l55s5gUlvFOk= cloud.google.com/go/dataproc/v2 v2.5.3/go.mod h1:RgA5QR7v++3xfP7DlgY3DUmoDSTaaemPe0ayKrQfyeg= cloud.google.com/go/dataqna v0.7.0 h1:yFzi/YU4YAdjyo7pXkBE2FeHbgz5OQQBVDdbErEHmVQ= cloud.google.com/go/dataqna v0.8.1 h1:ITpUJep04hC9V7C+gcK390HO++xesQFSUJ7S4nSnF3U= -cloud.google.com/go/dataqna v0.8.2/go.mod h1:KNEqgx8TTmUipnQsScOoDpq/VlXVptUqVMZnt30WAPs= -cloud.google.com/go/dataqna v0.8.3/go.mod h1:wXNBW2uvc9e7Gl5k8adyAMnLush1KVV6lZUhB+rqNu4= -cloud.google.com/go/dataqna v0.8.4/go.mod h1:mySRKjKg5Lz784P6sCov3p1QD+RZQONRMRjzGNcFd0c= cloud.google.com/go/dataqna v0.8.5 h1:9ybXs3nr9BzxSGC04SsvtuXaHY0qmJSLIpIAbZo9GqQ= -cloud.google.com/go/dataqna v0.8.5/go.mod h1:vgihg1mz6n7pb5q2YJF7KlXve6tCglInd6XO0JGOlWM= cloud.google.com/go/dataqna v0.8.11 h1:bEUidOYRS0EQ7qHbZtcnospuks72iTapboszXU9poz8= cloud.google.com/go/dataqna v0.8.11/go.mod h1:74Icl1oFKKZXPd+W7YDtqJLa+VwLV6wZ+UF+sHo2QZQ= cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/datastore v1.11.0 h1:iF6I/HaLs3Ado8uRKMvZRvF/ZLkWaWE9i8AiHzbC774= -cloud.google.com/go/datastore v1.13.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.14.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= cloud.google.com/go/datastore v1.15.0 h1:0P9WcsQeTWjuD1H14JIY7XQscIPQ4Laje8ti96IC5vg= -cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= cloud.google.com/go/datastore v1.17.1 h1:6Me8ugrAOAxssGhSo8im0YSuy4YvYk4mbGvCadAH5aE= cloud.google.com/go/datastore v1.17.1/go.mod h1:mtzZ2HcVtz90OVrEXXGDc2pO4NM1kiBQy8YV4qGe0ZM= cloud.google.com/go/datastream v1.7.0 h1:BBCBTnWMDwwEzQQmipUXxATa7Cm7CA/gKjKcR2w35T0= cloud.google.com/go/datastream v1.10.0 h1:ra/+jMv36zTAGPfi8TRne1hXme+UsKtdcK4j6bnqQiw= -cloud.google.com/go/datastream v1.10.0/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.1/go.mod h1:7ngSYwnw95YFyTd5tOGBxHlOZiL+OtpjheqU7t2/s/c= -cloud.google.com/go/datastream v1.10.2/go.mod h1:W42TFgKAs/om6x/CdXX5E4oiAsKlH+e8MTGy81zdYt0= -cloud.google.com/go/datastream v1.10.3/go.mod h1:YR0USzgjhqA/Id0Ycu1VvZe8hEWwrkjuXrGbzeDOSEA= cloud.google.com/go/datastream v1.10.4 h1:o1QDKMo/hk0FN7vhoUQURREuA0rgKmnYapB+1M+7Qz4= -cloud.google.com/go/datastream v1.10.4/go.mod h1:7kRxPdxZxhPg3MFeCSulmAJnil8NJGGvSNdn4p1sRZo= cloud.google.com/go/datastream v1.10.10 h1:klGhjQCLoLIRHMzMFIqM73cPNKliGveqC+Vrms+ce6A= cloud.google.com/go/datastream v1.10.10/go.mod h1:NqchuNjhPlISvWbk426/AU/S+Kgv7srlID9P5XOAbtg= cloud.google.com/go/deploy v1.8.0 h1:otshdKEbmsi1ELYeCKNYppwV0UH5xD05drSdBm7ouTk= cloud.google.com/go/deploy v1.13.0 h1:A+w/xpWgz99EYzB6e31gMGAI/P5jTZ2UO7veQK5jQ8o= -cloud.google.com/go/deploy v1.13.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.1/go.mod h1:8jeadyLkH9qu9xgO3hVWw8jVr29N1mnW42gRJT8GY6g= -cloud.google.com/go/deploy v1.14.1/go.mod h1:N8S0b+aIHSEeSr5ORVoC0+/mOPUysVt8ae4QkZYolAw= -cloud.google.com/go/deploy v1.14.2/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= -cloud.google.com/go/deploy v1.15.0/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= -cloud.google.com/go/deploy v1.16.0/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= -cloud.google.com/go/deploy v1.17.0/go.mod h1:XBr42U5jIr64t92gcpOXxNrqL2PStQCXHuKK5GRUuYo= cloud.google.com/go/deploy v1.17.1 h1:m27Ojwj03gvpJqCbodLYiVmE9x4/LrHGGMjzc0LBfM4= -cloud.google.com/go/deploy v1.17.1/go.mod h1:SXQyfsXrk0fBmgBHRzBjQbZhMfKZ3hMQBw5ym7MN/50= cloud.google.com/go/deploy v1.21.0 h1:/qnNETfztKemA9JmUBOrnH/rG/XFkHOBHygN1Vy5lkg= cloud.google.com/go/deploy v1.21.0/go.mod h1:PaOfS47VrvmYnxG5vhHg0KU60cKeWcqyLbMBjxS8DW8= cloud.google.com/go/dialogflow v1.32.0 h1:uVlKKzp6G/VtSW0E7IH1Y5o0H48/UOCmqksG2riYCwQ= -cloud.google.com/go/dialogflow v1.40.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.43.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= cloud.google.com/go/dialogflow v1.44.0 h1:F/fSUxRD/fAfjqjClwSzg1OsQGdDG7SbO1i4x5SHuUI= -cloud.google.com/go/dialogflow v1.44.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.1/go.mod h1:n/h+/N2ouKOO+rbe/ZnI186xImpqvCVj2DdsWS/0EAk= -cloud.google.com/go/dialogflow v1.44.2/go.mod h1:QzFYndeJhpVPElnFkUXxdlptx0wPnBWLCBT9BvtC3/c= -cloud.google.com/go/dialogflow v1.44.3/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= -cloud.google.com/go/dialogflow v1.47.0/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= -cloud.google.com/go/dialogflow v1.48.0/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= -cloud.google.com/go/dialogflow v1.48.1/go.mod h1:C1sjs2/g9cEwjCltkKeYp3FFpz8BOzNondEaAlCpt+A= -cloud.google.com/go/dialogflow v1.48.2/go.mod h1:7A2oDf6JJ1/+hdpnFRfb/RjJUOh2X3rhIa5P8wQSEX4= cloud.google.com/go/dialogflow v1.49.0 h1:KqG0oxGE71qo0lRVyAoeBozefCvsMfcDzDjoLYSY0F4= -cloud.google.com/go/dialogflow v1.49.0/go.mod h1:dhVrXKETtdPlpPhE7+2/k4Z8FRNUp6kMV3EW3oz/fe0= cloud.google.com/go/dialogflow v1.55.0 h1:H28O0WAm2waHpNAz2n9jbv8FApfXxeKAkfHObdP2MMk= cloud.google.com/go/dialogflow v1.55.0/go.mod h1:0u0hSlJiFpMkMpMNoFrQETwDjaRm8Q8hYKv+jz5JeRA= cloud.google.com/go/dlp v1.9.0 h1:1JoJqezlgu6NWCroBxr4rOZnwNFILXr4cB9dMaSKO4A= cloud.google.com/go/dlp v1.10.1 h1:tF3wsJ2QulRhRLWPzWVkeDz3FkOGVoMl6cmDUHtfYxw= -cloud.google.com/go/dlp v1.10.2/go.mod h1:ZbdKIhcnyhILgccwVDzkwqybthh7+MplGC3kZVZsIOQ= -cloud.google.com/go/dlp v1.10.3/go.mod h1:iUaTc/ln8I+QT6Ai5vmuwfw8fqTk2kaz0FvCwhLCom0= -cloud.google.com/go/dlp v1.11.1/go.mod h1:/PA2EnioBeXTL/0hInwgj0rfsQb3lpE3R8XUJxqUNKI= cloud.google.com/go/dlp v1.11.2 h1:lTipOuJaSjlYnnotPMbEhKURLC6GzCMDDzVbJAEbmYM= -cloud.google.com/go/dlp v1.11.2/go.mod h1:9Czi+8Y/FegpWzgSfkRlyz+jwW6Te9Rv26P3UfU/h/w= cloud.google.com/go/dlp v1.16.0 h1:mYjBqgVjseYXlx1TOOFsxSeZLboqxxKR7TqRGOG9vIU= cloud.google.com/go/dlp v1.16.0/go.mod h1:LtPZxZAenBXKzvWIOB2hdHIXuEcK0wW0En8//u+/nNA= cloud.google.com/go/documentai v1.18.0 h1:KM3Xh0QQyyEdC8Gs2vhZfU+rt6OCPF0dwVwxKgLmWfI= -cloud.google.com/go/documentai v1.22.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.1/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= cloud.google.com/go/documentai v1.23.0 h1:Gxrx8dgCjEPsJGjsI6wPdaURNG9tniQv7xDGQmLPNw0= -cloud.google.com/go/documentai v1.23.0/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.2/go.mod h1:Q/wcRT+qnuXOpjAkvOV4A+IeQl04q2/ReT7SSbytLSo= -cloud.google.com/go/documentai v1.23.4/go.mod h1:4MYAaEMnADPN1LPN5xboDR5QVB6AgsaxgFdJhitlE2Y= -cloud.google.com/go/documentai v1.23.5/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= -cloud.google.com/go/documentai v1.23.6/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= -cloud.google.com/go/documentai v1.23.7/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= -cloud.google.com/go/documentai v1.23.8/go.mod h1:Vd/y5PosxCpUHmwC+v9arZyeMfTqBR9VIwOwIqQYYfA= cloud.google.com/go/documentai v1.25.0 h1:lI62GMEEPO6vXJI9hj+G9WjOvnR0hEjvjokrnex4cxA= -cloud.google.com/go/documentai v1.25.0/go.mod h1:ftLnzw5VcXkLItp6pw1mFic91tMRyfv6hHEY5br4KzY= cloud.google.com/go/documentai v1.31.0 h1:YRkFK+0ZgEciz1svDkuL9fjbQLq8xvVa1d3NUlhO6B4= cloud.google.com/go/documentai v1.31.0/go.mod h1:5ajlDvaPyl9tc+K/jZE8WtYIqSXqAD33Z1YAYIjfad4= cloud.google.com/go/domains v0.8.0 h1:2ti/o9tlWL4N+wIuWUNH+LbfgpwxPr8J1sv9RHA4bYQ= cloud.google.com/go/domains v0.9.1 h1:rqz6KY7mEg7Zs/69U6m6LMbB7PxFDWmT3QWNXIqhHm0= -cloud.google.com/go/domains v0.9.2/go.mod h1:3YvXGYzZG1Temjbk7EyGCuGGiXHJwVNmwIf+E/cUp5I= -cloud.google.com/go/domains v0.9.3/go.mod h1:29k66YNDLDY9LCFKpGFeh6Nj9r62ZKm5EsUJxAl84KU= -cloud.google.com/go/domains v0.9.4/go.mod h1:27jmJGShuXYdUNjyDG0SodTfT5RwLi7xmH334Gvi3fY= cloud.google.com/go/domains v0.9.5 h1:Mml/R6s3vQQvFPpi/9oX3O5dRirgjyJ8cksK8N19Y7g= -cloud.google.com/go/domains v0.9.5/go.mod h1:dBzlxgepazdFhvG7u23XMhmMKBjrkoUNaw0A8AQB55Y= cloud.google.com/go/domains v0.9.11 h1:8peNiXtaMNIF9Wybci859M/yprFcEve1R2z08pErUBs= cloud.google.com/go/domains v0.9.11/go.mod h1:efo5552kUyxsXEz30+RaoIS2lR7tp3M/rhiYtKXkhkk= cloud.google.com/go/edgecontainer v1.0.0 h1:O0YVE5v+O0Q/ODXYsQHmHb+sYM8KNjGZw2pjX2Ws41c= cloud.google.com/go/edgecontainer v1.1.1 h1:zhHWnLzg6AqzE+I3gzJqiIwHfjEBhWctNQEzqb+FaRo= -cloud.google.com/go/edgecontainer v1.1.2/go.mod h1:wQRjIzqxEs9e9wrtle4hQPSR1Y51kqN75dgF7UllZZ4= -cloud.google.com/go/edgecontainer v1.1.3/go.mod h1:Ll2DtIABzEfaxaVSbwj3QHFaOOovlDFiWVDu349jSsA= -cloud.google.com/go/edgecontainer v1.1.4/go.mod h1:AvFdVuZuVGdgaE5YvlL1faAoa1ndRR/5XhXZvPBHbsE= cloud.google.com/go/edgecontainer v1.1.5 h1:tBY32km78ScpK2aOP84JoW/+wtpx5WluyPUSEE3270U= -cloud.google.com/go/edgecontainer v1.1.5/go.mod h1:rgcjrba3DEDEQAidT4yuzaKWTbkTI5zAMu3yy6ZWS0M= cloud.google.com/go/edgecontainer v1.2.5 h1:wTo0ulZDSsDzeoVjICJZjZMzZ1Nn9y//AwAQlXbaTbs= cloud.google.com/go/edgecontainer v1.2.5/go.mod h1:OAb6tElD3F3oBujFAup14PKOs9B/lYobTb6LARmoACY= cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= @@ -504,385 +243,205 @@ cloud.google.com/go/errorreporting v0.3.1 h1:E/gLk+rL7u5JZB9oq72iL1bnhVlLrnfslrg cloud.google.com/go/errorreporting v0.3.1/go.mod h1:6xVQXU1UuntfAf+bVkFk6nld41+CPyF2NSPCyXE3Ztk= cloud.google.com/go/essentialcontacts v1.5.0 h1:gIzEhCoOT7bi+6QZqZIzX1Erj4SswMPIteNvYVlu+pM= cloud.google.com/go/essentialcontacts v1.6.2 h1:OEJ0MLXXCW/tX1fkxzEZOsv/wRfyFsvDVNaHWBAvoV0= -cloud.google.com/go/essentialcontacts v1.6.3/go.mod h1:yiPCD7f2TkP82oJEFXFTou8Jl8L6LBRPeBEkTaO0Ggo= -cloud.google.com/go/essentialcontacts v1.6.4/go.mod h1:iju5Vy3d9tJUg0PYMd1nHhjV7xoCXaOAVabrwLaPBEM= -cloud.google.com/go/essentialcontacts v1.6.5/go.mod h1:jjYbPzw0x+yglXC890l6ECJWdYeZ5dlYACTFL0U/VuM= cloud.google.com/go/essentialcontacts v1.6.6 h1:13eHn5qBnsawxI7mIrv4jRIEmQ1xg0Ztqw5ZGqtUNfA= -cloud.google.com/go/essentialcontacts v1.6.6/go.mod h1:XbqHJGaiH0v2UvtuucfOzFXN+rpL/aU5BCZLn4DYl1Q= cloud.google.com/go/essentialcontacts v1.6.12 h1:JaQXS+qCFYs8yectfZHpzw4+NjTvFqTuDMCtfPzMvbw= cloud.google.com/go/essentialcontacts v1.6.12/go.mod h1:UGhWTIYewH8Ma4wDRJp8cMAHUCeAOCKsuwd6GLmmQLc= cloud.google.com/go/eventarc v1.11.0 h1:fsJmNeqvqtk74FsaVDU6cH79lyZNCYP8Rrv7EhaB/PU= cloud.google.com/go/eventarc v1.13.0 h1:xIP3XZi0Xawx8DEfh++mE2lrIi5kQmCr/KcWhJ1q0J4= -cloud.google.com/go/eventarc v1.13.0/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.1/go.mod h1:EqBxmGHFrruIara4FUQ3RHlgfCn7yo1HYsu2Hpt/C3Y= -cloud.google.com/go/eventarc v1.13.2/go.mod h1:X9A80ShVu19fb4e5sc/OLV7mpFUKZMwfJFeeWhcIObM= -cloud.google.com/go/eventarc v1.13.3/go.mod h1:RWH10IAZIRcj1s/vClXkBgMHwh59ts7hSWcqD3kaclg= cloud.google.com/go/eventarc v1.13.4 h1:ORkd6/UV5FIdA8KZQDLNZYKS7BBOrj0p01DXPmT4tE4= -cloud.google.com/go/eventarc v1.13.4/go.mod h1:zV5sFVoAa9orc/52Q+OuYUG9xL2IIZTbbuTHC6JSY8s= cloud.google.com/go/eventarc v1.13.10 h1:HVJmOVc+7eVFAqMpJRrq0nY0KlYBEBVZW7Gz7TxTio8= cloud.google.com/go/eventarc v1.13.10/go.mod h1:KlCcOMApmUaqOEZUpZRVH+p0nnnsY1HaJB26U4X5KXE= cloud.google.com/go/filestore v1.6.0 h1:ckTEXN5towyTMu4q0uQ1Mde/JwTHur0gXs8oaIZnKfw= cloud.google.com/go/filestore v1.7.1 h1:Eiz8xZzMJc5ppBWkuaod/PUdUZGCFR8ku0uS+Ah2fRw= -cloud.google.com/go/filestore v1.7.2/go.mod h1:TYOlyJs25f/omgj+vY7/tIG/E7BX369triSPzE4LdgE= -cloud.google.com/go/filestore v1.7.3/go.mod h1:Qp8WaEERR3cSkxToxFPHh/b8AACkSut+4qlCjAmKTV0= -cloud.google.com/go/filestore v1.7.4/go.mod h1:S5JCxIbFjeBhWMTfIYH2Jx24J6BqjwpkkPl+nBA5DlI= -cloud.google.com/go/filestore v1.8.0/go.mod h1:S5JCxIbFjeBhWMTfIYH2Jx24J6BqjwpkkPl+nBA5DlI= cloud.google.com/go/filestore v1.8.1 h1:X5G4y/vrUo1B8Nsz93qSWTMAcM8LXbGUldq33OdcdCw= -cloud.google.com/go/filestore v1.8.1/go.mod h1:MbN9KcaM47DRTIuLfQhJEsjaocVebNtNQhSLhKCF5GM= cloud.google.com/go/filestore v1.8.7 h1:LF9t5MClPyFJMuXdez/AjF1uyO9xHKUFF3GUqA+xFPI= cloud.google.com/go/filestore v1.8.7/go.mod h1:dKfyH0YdPAKdYHqAR/bxZeil85Y5QmrEVQwIYuRjcXI= cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY= cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= -cloud.google.com/go/firestore v1.12.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= cloud.google.com/go/firestore v1.13.0 h1:/3S4RssUV4GO/kvgJZB+tayjhOfyAHs+KcpJgRVu/Qk= -cloud.google.com/go/firestore v1.13.0/go.mod h1:QojqqOh8IntInDUSTAh0c8ZsPYAr68Ma8c5DWOy8xb8= cloud.google.com/go/firestore v1.14.0 h1:8aLcKnMPoldYU3YHgu4t2exrKhLQkqaXAGqT0ljrFVw= -cloud.google.com/go/firestore v1.14.0/go.mod h1:96MVaHLsEhbvkBEdZgfN+AS/GIkco1LRpH9Xp9YZfzQ= cloud.google.com/go/firestore v1.16.0 h1:YwmDHcyrxVRErWcgxunzEaZxtNbc8QoFYA/JOEwDPgc= cloud.google.com/go/firestore v1.16.0/go.mod h1:+22v/7p+WNBSQwdSwP57vz47aZiY+HrDkrOsJNhk7rg= cloud.google.com/go/functions v1.13.0 h1:pPDqtsXG2g9HeOQLoquLbmvmb82Y4Ezdo1GXuotFoWg= cloud.google.com/go/functions v1.15.1 h1:LtAyqvO1TFmNLcROzHZhV0agEJfBi+zfMZsF4RT/a7U= -cloud.google.com/go/functions v1.15.2/go.mod h1:CHAjtcR6OU4XF2HuiVeriEdELNcnvRZSk1Q8RMqy4lE= -cloud.google.com/go/functions v1.15.3/go.mod h1:r/AMHwBheapkkySEhiZYLDBwVJCdlRwsm4ieJu35/Ug= -cloud.google.com/go/functions v1.15.4/go.mod h1:CAsTc3VlRMVvx+XqXxKqVevguqJpnVip4DdonFsX28I= cloud.google.com/go/functions v1.16.0 h1:IWVylmK5F6hJ3R5zaRW7jI5PrWhCvtBVU4axQLmXSo4= -cloud.google.com/go/functions v1.16.0/go.mod h1:nbNpfAG7SG7Duw/o1iZ6ohvL7mc6MapWQVpqtM29n8k= cloud.google.com/go/functions v1.16.6 h1:tPe3/48RpjcFk96VeB6jOKQpK8nliGJLsgjh6pUOyFQ= cloud.google.com/go/functions v1.16.6/go.mod h1:wOzZakhMueNQaBUJdf0yjsJIe0GBRu+ZTvdSTzqHLs0= cloud.google.com/go/gaming v1.9.0 h1:7vEhFnZmd931Mo7sZ6pJy7uQPDxF7m7v8xtBheG08tc= cloud.google.com/go/gaming v1.10.1 h1:5qZmZEWzMf8GEFgm9NeC3bjFRpt7x4S6U7oLbxaf7N8= cloud.google.com/go/gkebackup v0.4.0 h1:za3QZvw6ujR0uyqkhomKKKNoXDyqYGPJies3voUK8DA= -cloud.google.com/go/gkebackup v1.3.0/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= cloud.google.com/go/gkebackup v1.3.1 h1:Kfha8SOF2tqsu4O4jVle66mk7qNdlJ2KhL3E2YyiNZc= -cloud.google.com/go/gkebackup v1.3.1/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.2/go.mod h1:OMZbXzEJloyXMC7gqdSB+EOEQ1AKcpGYvO3s1ec5ixk= -cloud.google.com/go/gkebackup v1.3.3/go.mod h1:eMk7/wVV5P22KBakhQnJxWSVftL1p4VBFLpv0kIft7I= -cloud.google.com/go/gkebackup v1.3.4/go.mod h1:gLVlbM8h/nHIs09ns1qx3q3eaXcGSELgNu1DWXYz1HI= cloud.google.com/go/gkebackup v1.3.5 h1:iuE8KNtTsPOc79qeWoNS8zOWoXPD9SAdOmwgxtlCmh8= -cloud.google.com/go/gkebackup v1.3.5/go.mod h1:KJ77KkNN7Wm1LdMopOelV6OodM01pMuK2/5Zt1t4Tvc= cloud.google.com/go/gkebackup v1.5.4 h1:mufh0PNpvqbfLV+TcxzSGESX8jGBcjKgctldv7kwQns= cloud.google.com/go/gkebackup v1.5.4/go.mod h1:V+llvHlRD0bCyrkYaAMJX+CHralceQcaOWjNQs8/Ymw= cloud.google.com/go/gkeconnect v0.7.0 h1:gXYKciHS/Lgq0GJ5Kc9SzPA35NGc3yqu6SkjonpEr2Q= cloud.google.com/go/gkeconnect v0.8.1 h1:a1ckRvVznnuvDWESM2zZDzSVFvggeBaVY5+BVB8tbT0= -cloud.google.com/go/gkeconnect v0.8.2/go.mod h1:6nAVhwchBJYgQCXD2pHBFQNiJNyAd/wyxljpaa6ZPrY= -cloud.google.com/go/gkeconnect v0.8.3/go.mod h1:i9GDTrfzBSUZGCe98qSu1B8YB8qfapT57PenIb820Jo= -cloud.google.com/go/gkeconnect v0.8.4/go.mod h1:84hZz4UMlDCKl8ifVW8layK4WHlMAFeq8vbzjU0yJkw= cloud.google.com/go/gkeconnect v0.8.5 h1:17d+ZSSXKqG/RwZCq3oFMIWLPI8Zw3b8+a9/BEVlwH0= -cloud.google.com/go/gkeconnect v0.8.5/go.mod h1:LC/rS7+CuJ5fgIbXv8tCD/mdfnlAadTaUufgOkmijuk= cloud.google.com/go/gkeconnect v0.8.11 h1:4bZAzvqhuv1uP+i4yG9cEMQ6ggdP26nBVjUgroPU6IM= cloud.google.com/go/gkeconnect v0.8.11/go.mod h1:ejHv5ehbceIglu1GsMwlH0nZpTftjxEY6DX7tvaM8gA= cloud.google.com/go/gkehub v0.12.0 h1:TqCSPsEBQ6oZSJgEYZ3XT8x2gUadbvfwI32YB0kuHCs= cloud.google.com/go/gkehub v0.14.1 h1:2BLSb8i+Co1P05IYCKATXy5yaaIw/ZqGvVSBTLdzCQo= -cloud.google.com/go/gkehub v0.14.2/go.mod h1:iyjYH23XzAxSdhrbmfoQdePnlMj2EWcvnR+tHdBQsCY= -cloud.google.com/go/gkehub v0.14.3/go.mod h1:jAl6WafkHHW18qgq7kqcrXYzN08hXeK/Va3utN8VKg8= -cloud.google.com/go/gkehub v0.14.4/go.mod h1:Xispfu2MqnnFt8rV/2/3o73SK1snL8s9dYJ9G2oQMfc= cloud.google.com/go/gkehub v0.14.5 h1:RboLNFzf9wEMSo7DrKVBlf+YhK/A/jrLN454L5Tz99Q= -cloud.google.com/go/gkehub v0.14.5/go.mod h1:6bzqxM+a+vEH/h8W8ec4OJl4r36laxTs3A/fMNHJ0wA= cloud.google.com/go/gkehub v0.14.11 h1:hQkVCcOiW/vPVYsthvKl1nje430/TpdFfgeIuqcYVOA= cloud.google.com/go/gkehub v0.14.11/go.mod h1:CsmDJ4qbBnSPkoBltEubK6qGOjG0xNfeeT5jI5gCnRQ= cloud.google.com/go/gkemulticloud v0.5.0 h1:8I84Q4vl02rJRsFiinBxl7WCozfdLlUVBQuSrqr9Wtk= cloud.google.com/go/gkemulticloud v1.0.0 h1:MluqhtPVZReoriP5+adGIw+ij/RIeRik8KApCW2WMTw= -cloud.google.com/go/gkemulticloud v1.0.0/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.1/go.mod h1:AcrGoin6VLKT/fwZEYuqvVominLriQBCKmbjtnbMjG8= -cloud.google.com/go/gkemulticloud v1.0.2/go.mod h1:+ee5VXxKb3H1l4LZAcgWB/rvI16VTNTrInWxDjAGsGo= -cloud.google.com/go/gkemulticloud v1.0.3/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= -cloud.google.com/go/gkemulticloud v1.1.0/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= cloud.google.com/go/gkemulticloud v1.1.1 h1:rsSZAGLhyjyE/bE2ToT5fqo1qSW7S+Ubsc9jFOcbhSI= -cloud.google.com/go/gkemulticloud v1.1.1/go.mod h1:C+a4vcHlWeEIf45IB5FFR5XGjTeYhF83+AYIpTy4i2Q= cloud.google.com/go/gkemulticloud v1.2.4 h1:6zV05tyl37HoEjCGGY+zHFNxnKQCjvVpiqWAUVgGaEs= cloud.google.com/go/gkemulticloud v1.2.4/go.mod h1:PjTtoKLQpIRztrL+eKQw8030/S4c7rx/WvHydDJlpGE= cloud.google.com/go/grafeas v0.2.0 h1:CYjC+xzdPvbV65gi6Dr4YowKcmLo045pm18L0DhdELM= cloud.google.com/go/grafeas v0.3.0 h1:oyTL/KjiUeBs9eYLw/40cpSZglUC+0F7X4iu/8t7NWs= cloud.google.com/go/grafeas v0.3.4 h1:D4x32R/cHX3MTofKwirz015uEdVk4uAxvZkZCZkOrF4= -cloud.google.com/go/grafeas v0.3.4/go.mod h1:A5m316hcG+AulafjAbPKXBO/+I5itU4LOdKO2R/uDIc= cloud.google.com/go/grafeas v0.3.6 h1:7bcA10EBgTsxeAVypJhz2Dv3fhrdlO7Ml8l7ZZA2IkE= cloud.google.com/go/grafeas v0.3.6/go.mod h1:to6ECAPgRO2xeqD8ISXHc70nObJuaKZThreQOjeOH3o= cloud.google.com/go/gsuiteaddons v1.5.0 h1:1mvhXqJzV0Vg5Fa95QwckljODJJfDFXV4pn+iL50zzA= cloud.google.com/go/gsuiteaddons v1.6.1 h1:mi9jxZpzVjLQibTS/XfPZvl+Jr6D5Bs8pGqUjllRb00= -cloud.google.com/go/gsuiteaddons v1.6.2/go.mod h1:K65m9XSgs8hTF3X9nNTPi8IQueljSdYo9F+Mi+s4MyU= -cloud.google.com/go/gsuiteaddons v1.6.3/go.mod h1:sCFJkZoMrLZT3JTb8uJqgKPNshH2tfXeCwTFRebTq48= -cloud.google.com/go/gsuiteaddons v1.6.4/go.mod h1:rxtstw7Fx22uLOXBpsvb9DUbC+fiXs7rF4U29KHM/pE= cloud.google.com/go/gsuiteaddons v1.6.5 h1:CZEbaBwmbYdhFw21Fwbo+C35HMe36fTE0FBSR4KSfWg= -cloud.google.com/go/gsuiteaddons v1.6.5/go.mod h1:Lo4P2IvO8uZ9W+RaC6s1JVxo42vgy+TX5a6hfBZ0ubs= cloud.google.com/go/gsuiteaddons v1.6.11 h1:zydWX0nVT0Ut/P1X25Sy+4Rqe2PH04IzhwlF1BJd8To= cloud.google.com/go/gsuiteaddons v1.6.11/go.mod h1:U7mk5PLBzDpHhgHv5aJkuvLp9RQzZFpa8hgWAB+xVIk= cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= cloud.google.com/go/iam v1.1.2 h1:gacbrBdWcoVmGLozRuStX45YKvJtzIjJdAolzUs1sm4= -cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= -cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8= -cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= -cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE= cloud.google.com/go/iam v1.1.12/go.mod h1:9LDX8J7dN5YRyzVHxwQzrQs9opFFqn0Mxs9nAeB+Hhg= cloud.google.com/go/iap v1.7.1 h1:PxVHFuMxmSZyfntKXHXhd8bo82WJ+LcATenq7HLdVnU= cloud.google.com/go/iap v1.9.0 h1:RNhVq/6OMI99/wjPVhqFxjlBxYOBRdaG6rLpBvyaqYY= -cloud.google.com/go/iap v1.9.0/go.mod h1:01OFxd1R+NFrg78S+hoPV5PxEzv22HXaNqUUlmNHFuY= -cloud.google.com/go/iap v1.9.1/go.mod h1:SIAkY7cGMLohLSdBR25BuIxO+I4fXJiL06IBL7cy/5Q= -cloud.google.com/go/iap v1.9.2/go.mod h1:GwDTOs047PPSnwRD0Us5FKf4WDRcVvHg1q9WVkKBhdI= -cloud.google.com/go/iap v1.9.3/go.mod h1:DTdutSZBqkkOm2HEOTBzhZxh2mwwxshfD/h3yofAiCw= cloud.google.com/go/iap v1.9.4 h1:94zirc2r4t6KzhAMW0R6Dme005eTP6yf7g6vN4IhRrA= -cloud.google.com/go/iap v1.9.4/go.mod h1:vO4mSq0xNf/Pu6E5paORLASBwEmphXEjgCFg7aeNu1w= cloud.google.com/go/iap v1.9.10 h1:j7jQqqSkZ2nWAOCiyaZfnJ+REycTJ2NP2dUEjLoW4aA= cloud.google.com/go/iap v1.9.10/go.mod h1:pO0FEirrhMOT1H0WVwpD5dD9r3oBhvsunyBQtNXzzc0= cloud.google.com/go/ids v1.3.0 h1:fodnCDtOXuMmS8LTC2y3h8t24U8F3eKWfhi+3LY6Qf0= cloud.google.com/go/ids v1.4.1 h1:khXYmSoDDhWGEVxHl4c4IgbwSRR+qE/L4hzP3vaU9Hc= -cloud.google.com/go/ids v1.4.2/go.mod h1:3vw8DX6YddRu9BncxuzMyWn0g8+ooUjI2gslJ7FH3vk= -cloud.google.com/go/ids v1.4.3/go.mod h1:9CXPqI3GedjmkjbMWCUhMZ2P2N7TUMzAkVXYEH2orYU= -cloud.google.com/go/ids v1.4.4/go.mod h1:z+WUc2eEl6S/1aZWzwtVNWoSZslgzPxAboS0lZX0HjI= cloud.google.com/go/ids v1.4.5 h1:xd4U7pgl3GHV+MABnv1BF4/Vy/zBF7CYC8XngkOLzag= -cloud.google.com/go/ids v1.4.5/go.mod h1:p0ZnyzjMWxww6d2DvMGnFwCsSxDJM666Iir1bK1UuBo= cloud.google.com/go/ids v1.4.11 h1:JhlR1d0XhMsj6YmSmbLbbXV5CGkffnUkPj0HNxJYNtc= cloud.google.com/go/ids v1.4.11/go.mod h1:+ZKqWELpJm8WcRRsSvKZWUdkriu4A3XsLLzToTv3418= cloud.google.com/go/iot v1.6.0 h1:39W5BFSarRNZfVG0eXI5LYux+OVQT8GkgpHCnrZL2vM= cloud.google.com/go/iot v1.7.1 h1:yrH0OSmicD5bqGBoMlWG8UltzdLkYzNUwNVUVz7OT54= -cloud.google.com/go/iot v1.7.2/go.mod h1:q+0P5zr1wRFpw7/MOgDXrG/HVA+l+cSwdObffkrpnSg= -cloud.google.com/go/iot v1.7.3/go.mod h1:t8itFchkol4VgNbHnIq9lXoOOtHNR3uAACQMYbN9N4I= -cloud.google.com/go/iot v1.7.4/go.mod h1:3TWqDVvsddYBG++nHSZmluoCAVGr1hAcabbWZNKEZLk= cloud.google.com/go/iot v1.7.5 h1:munTeBlbqI33iuTYgXy7S8lW2TCgi5l1hA4roSIY+EE= -cloud.google.com/go/iot v1.7.5/go.mod h1:nq3/sqTz3HGaWJi1xNiX7F41ThOzpud67vwk0YsSsqs= cloud.google.com/go/iot v1.7.11 h1:UBqSUZA6+7bM+mv6uvhl8tVsyT2Fi50njtBFRbrKSlI= cloud.google.com/go/iot v1.7.11/go.mod h1:0vZJOqFy9kVLbUXwTP95e0dWHakfR4u5IWqsKMGIfHk= cloud.google.com/go/kms v1.10.1 h1:7hm1bRqGCA1GBRQUrp831TwJ9TWhP+tvLuP497CQS2g= -cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= cloud.google.com/go/kms v1.15.2 h1:lh6qra6oC4AyWe5fUUUBe/S27k12OHAleOOOw6KakdE= -cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w= -cloud.google.com/go/kms v1.15.3/go.mod h1:AJdXqHxS2GlPyduM99s9iGqi2nwbviBbhV/hdmt4iOQ= -cloud.google.com/go/kms v1.15.4/go.mod h1:L3Sdj6QTHK8dfwK5D1JLsAyELsNMnd3tAIwGS4ltKpc= -cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= -cloud.google.com/go/kms v1.15.6/go.mod h1:yF75jttnIdHfGBoE51AKsD/Yqf+/jICzB9v1s1acsms= cloud.google.com/go/kms v1.15.7 h1:7caV9K3yIxvlQPAcaFffhlT7d1qpxjB1wHBtjWa13SM= -cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= cloud.google.com/go/kms v1.18.4/go.mod h1:SG1bgQ3UWW6/KdPo9uuJnzELXY5YTTMJtDYvajiQ22g= cloud.google.com/go/kms v1.18.5 h1:75LSlVs60hyHK3ubs2OHd4sE63OAMcM2BdSJc2bkuM4= cloud.google.com/go/kms v1.18.5/go.mod h1:yXunGUGzabH8rjUPImp2ndHiGolHeWJJ0LODLedicIY= cloud.google.com/go/language v1.9.0 h1:7Ulo2mDk9huBoBi8zCE3ONOoBrL6UXfAI71CLQ9GEIM= cloud.google.com/go/language v1.11.0 h1:KnYolG0T5Oex722ZW/sP5QErhVAVNcqpJ16tVJd9RTw= -cloud.google.com/go/language v1.11.0/go.mod h1:uDx+pFDdAKTY8ehpWbiXyQdz8tDSYLJbQcXsCkjYyvQ= -cloud.google.com/go/language v1.11.1/go.mod h1:Xyid9MG9WOX3utvDbpX7j3tXDmmDooMyMDqgUVpH17U= -cloud.google.com/go/language v1.12.1/go.mod h1:zQhalE2QlQIxbKIZt54IASBzmZpN/aDASea5zl1l+J4= -cloud.google.com/go/language v1.12.2/go.mod h1:9idWapzr/JKXBBQ4lWqVX/hcadxB194ry20m/bTrhWc= cloud.google.com/go/language v1.12.3 h1:iaJZg6K4j/2PvZZVcjeO/btcWWIllVRBhuTFjGO4LXs= -cloud.google.com/go/language v1.12.3/go.mod h1:evFX9wECX6mksEva8RbRnr/4wi/vKGYnAJrTRXU8+f8= cloud.google.com/go/language v1.13.0 h1:6Pl97Ei85A3wBJwjXW2S/1IWeUvhQf/lIPQBItnp0FA= cloud.google.com/go/language v1.13.0/go.mod h1:B9FbD17g1EkilctNGUDAdSrBHiFOlKNErLljO7jplDU= cloud.google.com/go/lifesciences v0.8.0 h1:uWrMjWTsGjLZpCTWEAzYvyXj+7fhiZST45u9AgasasI= cloud.google.com/go/lifesciences v0.9.1 h1:axkANGx1wiBXHiPcJZAE+TDjjYoJRIDzbHC/WYllCBU= -cloud.google.com/go/lifesciences v0.9.2/go.mod h1:QHEOO4tDzcSAzeJg7s2qwnLM2ji8IRpQl4p6m5Z9yTA= -cloud.google.com/go/lifesciences v0.9.3/go.mod h1:gNGBOJV80IWZdkd+xz4GQj4mbqaz737SCLHn2aRhQKM= -cloud.google.com/go/lifesciences v0.9.4/go.mod h1:bhm64duKhMi7s9jR9WYJYvjAFJwRqNj+Nia7hF0Z7JA= cloud.google.com/go/lifesciences v0.9.5 h1:gXvN70m2p+4zgJFzaz6gMKaxTuF9WJ0USYoMLWAOm8g= -cloud.google.com/go/lifesciences v0.9.5/go.mod h1:OdBm0n7C0Osh5yZB7j9BXyrMnTRGBJIZonUMxo5CzPw= cloud.google.com/go/lifesciences v0.9.11 h1:xyPSYICJWZElcELYgWCKs5PltyNX3TzOKaQAZA7d/I0= cloud.google.com/go/lifesciences v0.9.11/go.mod h1:NMxu++FYdv55TxOBEvLIhiAvah8acQwXsz79i9l9/RY= cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= cloud.google.com/go/logging v1.8.1 h1:26skQWPeYhvIasWKm48+Eq7oUqdcdbwsCVwz5Ys0FvU= -cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= cloud.google.com/go/logging v1.9.0 h1:iEIOXFO9EmSiTjDmfpbRjOxECO7R8C7b8IXUGOj7xZw= -cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE= cloud.google.com/go/logging v1.11.0 h1:v3ktVzXMV7CwHq1MBF65wcqLMA7i+z3YxbUsoK7mOKs= cloud.google.com/go/logging v1.11.0/go.mod h1:5LDiJC/RxTt+fHc1LAt20R9TKiUTReDg6RuuFOZ67+A= cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= -cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= -cloud.google.com/go/longrunning v0.5.3/go.mod h1:y/0ga59EYu58J6SHmmQOvekvND2qODbu8ywBBW7EK7Y= -cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg= -cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= cloud.google.com/go/longrunning v0.5.6/go.mod h1:vUaDrWYOMKRuhiv6JBnn49YxCPz2Ayn9GqyjaBT8/mA= cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng= cloud.google.com/go/longrunning v0.5.9/go.mod h1:HD+0l9/OOW0za6UWdKJtXoFAX/BGg/3Wj8p10NeWF7c= cloud.google.com/go/longrunning v0.5.11/go.mod h1:rDn7//lmlfWV1Dx6IB4RatCPenTwwmqXuiP0/RgoEO4= cloud.google.com/go/managedidentities v1.5.0 h1:ZRQ4k21/jAhrHBVKl/AY7SjgzeJwG1iZa+mJ82P+VNg= cloud.google.com/go/managedidentities v1.6.1 h1:2/qZuOeLgUHorSdxSQGtnOu9xQkBn37+j+oZQv/KHJY= -cloud.google.com/go/managedidentities v1.6.2/go.mod h1:5c2VG66eCa0WIq6IylRk3TBW83l161zkFvCj28X7jn8= -cloud.google.com/go/managedidentities v1.6.3/go.mod h1:tewiat9WLyFN0Fi7q1fDD5+0N4VUoL0SCX0OTCthZq4= -cloud.google.com/go/managedidentities v1.6.4/go.mod h1:WgyaECfHmF00t/1Uk8Oun3CQ2PGUtjc3e9Alh79wyiM= cloud.google.com/go/managedidentities v1.6.5 h1:+bpih1piZVLxla/XBqeSUzJBp8gv9plGHIMAI7DLpDM= -cloud.google.com/go/managedidentities v1.6.5/go.mod h1:fkFI2PwwyRQbjLxlm5bQ8SjtObFMW3ChBGNqaMcgZjI= cloud.google.com/go/managedidentities v1.6.11 h1:YU6NtRRBX5R1f3a8ryqhh1dUb1/pt3rnhSO50b63yZY= cloud.google.com/go/managedidentities v1.6.11/go.mod h1:df+8oZ1D4Eri+NrcpuiR5Hd6MGgiMqn0ZCzNmBYPS0A= cloud.google.com/go/maps v0.7.0 h1:mv9YaczD4oZBZkM5XJl6fXQ984IkJNHPwkc8MUsdkBo= -cloud.google.com/go/maps v1.3.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= cloud.google.com/go/maps v1.4.0 h1:PdfgpBLhAoSzZrQXP+/zBc78fIPLZSJp5y8+qSMn2UU= -cloud.google.com/go/maps v1.4.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.1/go.mod h1:BxSa0BnW1g2U2gNdbq5zikLlHUuHW0GFWh7sgML2kIY= -cloud.google.com/go/maps v1.5.1/go.mod h1:NPMZw1LJwQZYCfz4y+EIw+SI+24A4bpdFJqdKVr0lt4= -cloud.google.com/go/maps v1.6.1/go.mod h1:4+buOHhYXFBp58Zj/K+Lc1rCmJssxxF4pJ5CJnhdz18= -cloud.google.com/go/maps v1.6.2/go.mod h1:4+buOHhYXFBp58Zj/K+Lc1rCmJssxxF4pJ5CJnhdz18= -cloud.google.com/go/maps v1.6.3/go.mod h1:VGAn809ADswi1ASofL5lveOHPnE6Rk/SFTTBx1yuOLw= cloud.google.com/go/maps v1.6.4 h1:EVCZAiDvog9So46460BGbCasPhi613exoaQbpilMVlk= -cloud.google.com/go/maps v1.6.4/go.mod h1:rhjqRy8NWmDJ53saCfsXQ0LKwBHfi6OSh5wkq6BaMhI= cloud.google.com/go/maps v1.11.6 h1:HMI0drvgnT+BtsjBofb1Z80P53n63ybmm7l+1w1og9I= cloud.google.com/go/maps v1.11.6/go.mod h1:MOS/NN0L6b7Kumr8bLux9XTpd8+D54DYxBMUjq+XfXs= cloud.google.com/go/mediatranslation v0.7.0 h1:anPxH+/WWt8Yc3EdoEJhPMBRF7EhIdz426A+tuoA0OU= cloud.google.com/go/mediatranslation v0.8.1 h1:50cF7c1l3BanfKrpnTCaTvhf+Fo6kdF21DG0byG7gYU= -cloud.google.com/go/mediatranslation v0.8.2/go.mod h1:c9pUaDRLkgHRx3irYE5ZC8tfXGrMYwNZdmDqKMSfFp8= -cloud.google.com/go/mediatranslation v0.8.3/go.mod h1:F9OnXTy336rteOEywtY7FOqCk+J43o2RF638hkOQl4Y= -cloud.google.com/go/mediatranslation v0.8.4/go.mod h1:9WstgtNVAdN53m6TQa5GjIjLqKQPXe74hwSCxUP6nj4= cloud.google.com/go/mediatranslation v0.8.5 h1:c76KdIXljQHSCb/Cy47S8H4s05A4zbK3pAFGzwcczZo= -cloud.google.com/go/mediatranslation v0.8.5/go.mod h1:y7kTHYIPCIfgyLbKncgqouXJtLsU+26hZhHEEy80fSs= cloud.google.com/go/mediatranslation v0.8.11 h1:QvO405ocKTmcJqjfqL1zps08yrKk8rE+0E1ZNSWfjbw= cloud.google.com/go/mediatranslation v0.8.11/go.mod h1:3sNEm0fx61eHk7rfzBzrljVV9XKr931xI3OFacQBVFg= cloud.google.com/go/memcache v1.9.0 h1:8/VEmWCpnETCrBwS3z4MhT+tIdKgR1Z4Tr2tvYH32rg= cloud.google.com/go/memcache v1.10.1 h1:7lkLsF0QF+Mre0O/NvkD9Q5utUNwtzvIYjrOLOs0HO0= -cloud.google.com/go/memcache v1.10.2/go.mod h1:f9ZzJHLBrmd4BkguIAa/l/Vle6uTHzHokdnzSWOdQ6A= -cloud.google.com/go/memcache v1.10.3/go.mod h1:6z89A41MT2DVAW0P4iIRdu5cmRTsbsFn4cyiIx8gbwo= -cloud.google.com/go/memcache v1.10.4/go.mod h1:v/d8PuC8d1gD6Yn5+I3INzLR01IDn0N4Ym56RgikSI0= cloud.google.com/go/memcache v1.10.5 h1:yeDv5qxRedFosvpMSEswrqUsJM5OdWvssPHFliNFTc4= -cloud.google.com/go/memcache v1.10.5/go.mod h1:/FcblbNd0FdMsx4natdj+2GWzTq+cjZvMa1I+9QsuMA= cloud.google.com/go/memcache v1.10.11 h1:DGPEJOVL4Qix2GLKQKcgzGpNLD7gAnCFLr9ch9YSIhU= cloud.google.com/go/memcache v1.10.11/go.mod h1:ubJ7Gfz/xQawQY5WO5pht4Q0dhzXBFeEszAeEJnwBHU= cloud.google.com/go/metastore v1.10.0 h1:QCFhZVe2289KDBQ7WxaHV2rAmPrmRAdLC6gbjUd3HPo= -cloud.google.com/go/metastore v1.12.0/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= cloud.google.com/go/metastore v1.13.0 h1:iMMU4DY4yojvKatMfv1q9WqBHi3ZrcwAIYQ+ZrlXM2o= -cloud.google.com/go/metastore v1.13.0/go.mod h1:URDhpG6XLeh5K+Glq0NOt74OfrPKTwS62gEPZzb5SOk= -cloud.google.com/go/metastore v1.13.1/go.mod h1:IbF62JLxuZmhItCppcIfzBBfUFq0DIB9HPDoLgWrVOU= -cloud.google.com/go/metastore v1.13.2/go.mod h1:KS59dD+unBji/kFebVp8XU/quNSyo8b6N6tPGspKszA= -cloud.google.com/go/metastore v1.13.3/go.mod h1:K+wdjXdtkdk7AQg4+sXS8bRrQa9gcOr+foOMF2tqINE= cloud.google.com/go/metastore v1.13.4 h1:dR7vqWXlK6IYR8Wbu9mdFfwlVjodIBhd1JRrpZftTEg= -cloud.google.com/go/metastore v1.13.4/go.mod h1:FMv9bvPInEfX9Ac1cVcRXp8EBBQnBcqH6gz3KvJ9BAE= cloud.google.com/go/metastore v1.13.10 h1:E5eAxzIRoVP0DrV+ZtTLMYkkjSs4fcfsbL7wv1mXV2U= cloud.google.com/go/metastore v1.13.10/go.mod h1:RPhMnBxUmTLT1fN7fNbPqtH5EoGHueDxubmJ1R1yT84= cloud.google.com/go/monitoring v1.13.0 h1:2qsrgXGVoRXpP7otZ14eE1I568zAa92sJSDPyOJvwjM= cloud.google.com/go/monitoring v1.16.0 h1:rlndy4K8yknMY9JuGe2aK4SbCh21FXoCdX7SAGHmRgI= -cloud.google.com/go/monitoring v1.16.0/go.mod h1:Ptp15HgAyM1fNICAojDMoNc/wUmn67mLHQfyqbw+poY= -cloud.google.com/go/monitoring v1.16.1/go.mod h1:6HsxddR+3y9j+o/cMJH6q/KJ/CBTvM/38L/1m7bTRJ4= -cloud.google.com/go/monitoring v1.16.2/go.mod h1:B44KGwi4ZCF8Rk/5n+FWeispDXoKSk9oss2QNlXJBgc= -cloud.google.com/go/monitoring v1.16.3/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= -cloud.google.com/go/monitoring v1.17.0/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= -cloud.google.com/go/monitoring v1.17.1/go.mod h1:SJzPMakCF0GHOuKEH/r4hxVKF04zl+cRPQyc3d/fqII= cloud.google.com/go/monitoring v1.18.0 h1:NfkDLQDG2UR3WYZVQE8kwSbUIEyIqJUPl+aOQdFH1T4= -cloud.google.com/go/monitoring v1.18.0/go.mod h1:c92vVBCeq/OB4Ioyo+NbN2U7tlg5ZH41PZcdvfc+Lcg= cloud.google.com/go/monitoring v1.20.3/go.mod h1:GPIVIdNznIdGqEjtRKQWTLcUeRnPjZW85szouimiczU= cloud.google.com/go/monitoring v1.20.4 h1:zwcViK7mT9SV0kzKqLOI3spRadvsmvw/R9z1MHNeC0E= cloud.google.com/go/monitoring v1.20.4/go.mod h1:v7F/UcLRw15EX7xq565N7Ae5tnYEE28+Cl717aTXG4c= cloud.google.com/go/networkconnectivity v1.11.0 h1:ZD6b4Pk1jEtp/cx9nx0ZYcL3BKqDa+KixNDZ6Bjs1B8= -cloud.google.com/go/networkconnectivity v1.13.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= cloud.google.com/go/networkconnectivity v1.14.0 h1:NBuIU8e/kBNAJE7Rgij+wyXZMcybnlxVTpoZEyQppHc= -cloud.google.com/go/networkconnectivity v1.14.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.1/go.mod h1:LyGPXR742uQcDxZ/wv4EI0Vu5N6NKJ77ZYVnDe69Zug= -cloud.google.com/go/networkconnectivity v1.14.2/go.mod h1:5UFlwIisZylSkGG1AdwK/WZUaoz12PKu6wODwIbFzJo= -cloud.google.com/go/networkconnectivity v1.14.3/go.mod h1:4aoeFdrJpYEXNvrnfyD5kIzs8YtHg945Og4koAjHQek= cloud.google.com/go/networkconnectivity v1.14.4 h1:GBfXFhLyPspnaBE3nI/BRjdhW8vcbpT9QjE/4kDCDdc= -cloud.google.com/go/networkconnectivity v1.14.4/go.mod h1:PU12q++/IMnDJAB+3r+tJtuCXCfwfN+C6Niyj6ji1Po= cloud.google.com/go/networkconnectivity v1.14.10 h1:2EE8pKiv1AI8fBdZCdiUjNgQ+TaBgwE4GxIze4fDdY0= cloud.google.com/go/networkconnectivity v1.14.10/go.mod h1:f7ZbGl4CV08DDb7lw+NmMXQTKKjMhgCEEwFbEukWuOY= cloud.google.com/go/networkmanagement v1.6.0 h1:8KWEUNGcpSX9WwZXq7FtciuNGPdPdPN/ruDm769yAEM= cloud.google.com/go/networkmanagement v1.9.0 h1:aA6L8aioyM4S6nlPYzp2SvB88lBcByZmqMJM6ReafzU= -cloud.google.com/go/networkmanagement v1.9.0/go.mod h1:UTUaEU9YwbCAhhz3jEOHr+2/K/MrBk2XxOLS89LQzFw= -cloud.google.com/go/networkmanagement v1.9.1/go.mod h1:CCSYgrQQvW73EJawO2QamemYcOb57LvrDdDU51F0mcI= -cloud.google.com/go/networkmanagement v1.9.2/go.mod h1:iDGvGzAoYRghhp4j2Cji7sF899GnfGQcQRQwgVOWnDw= -cloud.google.com/go/networkmanagement v1.9.3/go.mod h1:y7WMO1bRLaP5h3Obm4tey+NquUvB93Co1oh4wpL+XcU= cloud.google.com/go/networkmanagement v1.9.4 h1:aLV5GcosBNmd6M8+a0ekB0XlLRexv4fvnJJrYnqeBcg= -cloud.google.com/go/networkmanagement v1.9.4/go.mod h1:daWJAl0KTFytFL7ar33I6R/oNBH8eEOX/rBNHrC/8TA= cloud.google.com/go/networkmanagement v1.13.6 h1:6TGn7ZZXyj5rloN0vv5Aw0awYbfbheNRg8BKroT7/2g= cloud.google.com/go/networkmanagement v1.13.6/go.mod h1:WXBijOnX90IFb6sberjnGrVtZbgDNcPDUYOlGXmG8+4= cloud.google.com/go/networksecurity v0.8.0 h1:sOc42Ig1K2LiKlzG71GUVloeSJ0J3mffEBYmvu+P0eo= cloud.google.com/go/networksecurity v0.9.1 h1:TBLEkMp3AE+6IV/wbIGRNTxnqLXHCTEQWoxRVC18TzY= -cloud.google.com/go/networksecurity v0.9.2/go.mod h1:jG0SeAttWzPMUILEHDUvFYdQTl8L/E/KC8iZDj85lEI= -cloud.google.com/go/networksecurity v0.9.3/go.mod h1:l+C0ynM6P+KV9YjOnx+kk5IZqMSLccdBqW6GUoF4p/0= -cloud.google.com/go/networksecurity v0.9.4/go.mod h1:E9CeMZ2zDsNBkr8axKSYm8XyTqNhiCHf1JO/Vb8mD1w= cloud.google.com/go/networksecurity v0.9.5 h1:+caSxBTj0E8OYVh/5wElFdjEMO1S/rZtE1152Cepchc= -cloud.google.com/go/networksecurity v0.9.5/go.mod h1:KNkjH/RsylSGyyZ8wXpue8xpCEK+bTtvof8SBfIhMG8= cloud.google.com/go/networksecurity v0.9.11 h1:6wUzyHCwDEOkDbAJjT6jxsAi+vMfe3aj2JWwqSFVXOQ= cloud.google.com/go/networksecurity v0.9.11/go.mod h1:4xbpOqCwplmFgymAjPFM6ZIplVC6+eQ4m7sIiEq9oJA= cloud.google.com/go/notebooks v1.8.0 h1:Kg2K3K7CbSXYJHZ1aGQpf1xi5x2GUvQWf2sFVuiZh8M= cloud.google.com/go/notebooks v1.10.0 h1:6x2K1JAWv6RW2yQO6oa+xtKUGOpGQseCmT94vpOt1vc= -cloud.google.com/go/notebooks v1.10.0/go.mod h1:SOPYMZnttHxqot0SGSFSkRrwE29eqnKPBJFqgWmiK2k= -cloud.google.com/go/notebooks v1.10.1/go.mod h1:5PdJc2SgAybE76kFQCWrTfJolCOUQXF97e+gteUUA6A= -cloud.google.com/go/notebooks v1.11.1/go.mod h1:V2Zkv8wX9kDCGRJqYoI+bQAaoVeE5kSiz4yYHd2yJwQ= -cloud.google.com/go/notebooks v1.11.2/go.mod h1:z0tlHI/lREXC8BS2mIsUeR3agM1AkgLiS+Isov3SS70= cloud.google.com/go/notebooks v1.11.3 h1:FH48boYmrWVQ6k0Mx/WrnNafXncT5iSYxA8CNyWTgy0= -cloud.google.com/go/notebooks v1.11.3/go.mod h1:0wQyI2dQC3AZyQqWnRsp+yA+kY4gC7ZIVP4Qg3AQcgo= cloud.google.com/go/notebooks v1.11.9 h1:c8I0EaLGqStRmvX29L7jb4mOrpigxn1mGyBt65OdS0s= cloud.google.com/go/notebooks v1.11.9/go.mod h1:JmnRX0eLgHRJiyxw8HOgumW9iRajImZxr7r75U16uXw= cloud.google.com/go/optimization v1.3.1 h1:dj8O4VOJRB4CUwZXdmwNViH1OtI0WtWL867/lnYH248= cloud.google.com/go/optimization v1.5.0 h1:sGvPVtBJUKNYAwldhJvFmnM+EEdOXjDzjcly3g0n0Xg= -cloud.google.com/go/optimization v1.5.0/go.mod h1:evo1OvTxeBRBu6ydPlrIRizKY/LJKo/drDMMRKqGEUU= -cloud.google.com/go/optimization v1.5.1/go.mod h1:NC0gnUD5MWVAF7XLdoYVPmYYVth93Q6BUzqAq3ZwtV8= -cloud.google.com/go/optimization v1.6.1/go.mod h1:hH2RYPTTM9e9zOiTaYPTiGPcGdNZVnBSBxjIAJzUkqo= -cloud.google.com/go/optimization v1.6.2/go.mod h1:mWNZ7B9/EyMCcwNl1frUGEuY6CPijSkz88Fz2vwKPOY= cloud.google.com/go/optimization v1.6.3 h1:63NZaWyN+5rZEKHPX4ACpw3BjgyeuY8+rCehiCMaGPY= -cloud.google.com/go/optimization v1.6.3/go.mod h1:8ve3svp3W6NFcAEFr4SfJxrldzhUl4VMUJmhrqVKtYA= cloud.google.com/go/optimization v1.6.9 h1:++U21U9LWFdgnnVFaq4kDeOafft6gI/CHzsiJ173c6U= cloud.google.com/go/optimization v1.6.9/go.mod h1:mcvkDy0p4s5k7iSaiKrwwpN0IkteHhGmuW5rP9nXA5M= cloud.google.com/go/orchestration v1.6.0 h1:Vw+CEXo8M/FZ1rb4EjcLv0gJqqw89b7+g+C/EmniTb8= cloud.google.com/go/orchestration v1.8.1 h1:KmN18kE/xa1n91cM5jhCh7s1/UfIguSCisw7nTMUzgE= -cloud.google.com/go/orchestration v1.8.2/go.mod h1:T1cP+6WyTmh6LSZzeUhvGf0uZVmJyTx7t8z7Vg87+A0= -cloud.google.com/go/orchestration v1.8.3/go.mod h1:xhgWAYqlbYjlz2ftbFghdyqENYW+JXuhBx9KsjMoGHs= -cloud.google.com/go/orchestration v1.8.4/go.mod h1:d0lywZSVYtIoSZXb0iFjv9SaL13PGyVOKDxqGxEf/qI= cloud.google.com/go/orchestration v1.8.5 h1:YHgWMlrPttIVGItgGfuvO2KM7x+y9ivN/Yk92pMm1a4= -cloud.google.com/go/orchestration v1.8.5/go.mod h1:C1J7HesE96Ba8/hZ71ISTV2UAat0bwN+pi85ky38Yq8= cloud.google.com/go/orchestration v1.9.6 h1:xfczjtNDabsXTnDySAwD/TMfDSkcxEgH1rxfS6BVQtM= cloud.google.com/go/orchestration v1.9.6/go.mod h1:gQvdIsHESZJigimnbUA8XLbYeFlSg/z+A7ppds5JULg= cloud.google.com/go/orgpolicy v1.10.0 h1:XDriMWug7sd0kYT1QKofRpRHzjad0bK8Q8uA9q+XrU4= cloud.google.com/go/orgpolicy v1.11.1 h1:I/7dHICQkNwym9erHqmlb50LRU588NPCvkfIY0Bx9jI= -cloud.google.com/go/orgpolicy v1.11.2/go.mod h1:biRDpNwfyytYnmCRWZWxrKF22Nkz9eNVj9zyaBdpm1o= -cloud.google.com/go/orgpolicy v1.11.3/go.mod h1:oKAtJ/gkMjum5icv2aujkP4CxROxPXsBbYGCDbPO8MM= -cloud.google.com/go/orgpolicy v1.11.4/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= -cloud.google.com/go/orgpolicy v1.12.0/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= cloud.google.com/go/orgpolicy v1.12.1 h1:2JbXigqBJVp8Dx5dONUttFqewu4fP0p3pgOdIZAhpYU= -cloud.google.com/go/orgpolicy v1.12.1/go.mod h1:aibX78RDl5pcK3jA8ysDQCFkVxLj3aOQqrbBaUL2V5I= cloud.google.com/go/orgpolicy v1.12.7 h1:StymaN9vS7949m15Nwgf5aKd9yaRtzWJ4VqHdbXcOEM= cloud.google.com/go/orgpolicy v1.12.7/go.mod h1:Os3GlUFRPf1UxOHTup5b70BARnhHeQNNVNZzJXPbWYI= cloud.google.com/go/osconfig v1.11.0 h1:PkSQx4OHit5xz2bNyr11KGcaFccL5oqglFPdTboyqwQ= cloud.google.com/go/osconfig v1.12.1 h1:dgyEHdfqML6cUW6/MkihNdTVc0INQst0qSE8Ou1ub9c= -cloud.google.com/go/osconfig v1.12.2/go.mod h1:eh9GPaMZpI6mEJEuhEjUJmaxvQ3gav+fFEJon1Y8Iw0= -cloud.google.com/go/osconfig v1.12.3/go.mod h1:L/fPS8LL6bEYUi1au832WtMnPeQNT94Zo3FwwV1/xGM= -cloud.google.com/go/osconfig v1.12.4/go.mod h1:B1qEwJ/jzqSRslvdOCI8Kdnp0gSng0xW4LOnIebQomA= cloud.google.com/go/osconfig v1.12.5 h1:Mo5jGAxOMKH/PmDY7fgY19yFcVbvwREb5D5zMPQjFfo= -cloud.google.com/go/osconfig v1.12.5/go.mod h1:D9QFdxzfjgw3h/+ZaAb5NypM8bhOMqBzgmbhzWViiW8= cloud.google.com/go/osconfig v1.13.2 h1:IbbTg7jtTEn4+iEJwgbCYck5NLMOc2eKrqVpQb7Xx6c= cloud.google.com/go/osconfig v1.13.2/go.mod h1:eupylkWQJCwSIEMkpVR4LqpgKkQi0mD4m1DzNCgpQso= cloud.google.com/go/oslogin v1.9.0 h1:whP7vhpmc+ufZa90eVpkfbgzJRK/Xomjz+XCD4aGwWw= cloud.google.com/go/oslogin v1.11.0 h1:7OA/BHWna8s+8k1sjTLHs0zRttoktR8a36qjWIvzTco= -cloud.google.com/go/oslogin v1.11.0/go.mod h1:8GMTJs4X2nOAUVJiPGqIWVcDaF0eniEto3xlOxaboXE= -cloud.google.com/go/oslogin v1.11.1/go.mod h1:OhD2icArCVNUxKqtK0mcSmKL7lgr0LVlQz+v9s1ujTg= -cloud.google.com/go/oslogin v1.12.1/go.mod h1:VfwTeFJGbnakxAY236eN8fsnglLiVXndlbcNomY4iZU= -cloud.google.com/go/oslogin v1.12.2/go.mod h1:CQ3V8Jvw4Qo4WRhNPF0o+HAM4DiLuE27Ul9CX9g2QdY= -cloud.google.com/go/oslogin v1.13.0/go.mod h1:xPJqLwpTZ90LSE5IL1/svko+6c5avZLluiyylMb/sRA= cloud.google.com/go/oslogin v1.13.1 h1:1K4nOT5VEZNt7XkhaTXupBYos5HjzvJMfhvyD2wWdFs= -cloud.google.com/go/oslogin v1.13.1/go.mod h1:vS8Sr/jR7QvPWpCjNqy6LYZr5Zs1e8ZGW/KPn9gmhws= cloud.google.com/go/oslogin v1.13.7 h1:q9x7tjKtfBpXMpiJKwb5UyhMA3GrwmJHvx56uCEuS8M= cloud.google.com/go/oslogin v1.13.7/go.mod h1:xq027cL0fojpcEcpEQdWayiDn8tIx3WEFYMM6+q7U+E= cloud.google.com/go/phishingprotection v0.7.0 h1:l6tDkT7qAEV49MNEJkEJTB6vOO/onbSOcNtAT09HPuA= cloud.google.com/go/phishingprotection v0.8.1 h1:aK/lNmSd1vtbft/vLe2g7edXK72sIQbqr2QyrZN/iME= -cloud.google.com/go/phishingprotection v0.8.2/go.mod h1:LhJ91uyVHEYKSKcMGhOa14zMMWfbEdxG032oT6ECbC8= -cloud.google.com/go/phishingprotection v0.8.3/go.mod h1:3B01yO7T2Ra/TMojifn8EoGd4G9jts/6cIO0DgDY9J8= -cloud.google.com/go/phishingprotection v0.8.4/go.mod h1:6b3kNPAc2AQ6jZfFHioZKg9MQNybDg4ixFd4RPZZ2nE= cloud.google.com/go/phishingprotection v0.8.5 h1:DH3WFLzEoJdW/6xgsmoDqOwT1xddFi7gKu0QGZQhpGU= -cloud.google.com/go/phishingprotection v0.8.5/go.mod h1:g1smd68F7mF1hgQPuYn3z8HDbNre8L6Z0b7XMYFmX7I= cloud.google.com/go/phishingprotection v0.8.11 h1:3Kr7TINZ+8pbdWe3JnJf9c84ibz60NRTvwLdVtI3SK8= cloud.google.com/go/phishingprotection v0.8.11/go.mod h1:Mge0cylqVFs+D0EyxlsTOJ1Guf3qDgrztHzxZqkhRQM= cloud.google.com/go/policytroubleshooter v1.6.0 h1:yKAGC4p9O61ttZUswaq9GAn1SZnEzTd0vUYXD7ZBT7Y= -cloud.google.com/go/policytroubleshooter v1.8.0/go.mod h1:tmn5Ir5EToWe384EuboTcVQT7nTag2+DuH3uHmKd1HU= cloud.google.com/go/policytroubleshooter v1.9.0 h1:pT4qSiL5o0hBSWHDiOcmes/s301PeLLWEhAr/eMQB/g= -cloud.google.com/go/policytroubleshooter v1.9.0/go.mod h1:+E2Lga7TycpeSTj2FsH4oXxTnrbHJGRlKhVZBLGgU64= -cloud.google.com/go/policytroubleshooter v1.9.1/go.mod h1:MYI8i0bCrL8cW+VHN1PoiBTyNZTstCg2WUw2eVC4c4U= -cloud.google.com/go/policytroubleshooter v1.10.1/go.mod h1:5C0rhT3TDZVxAu8813bwmTvd57Phbl8mr9F4ipOsxEs= -cloud.google.com/go/policytroubleshooter v1.10.2/go.mod h1:m4uF3f6LseVEnMV6nknlN2vYGRb+75ylQwJdnOXfnv0= cloud.google.com/go/policytroubleshooter v1.10.3 h1:c0WOzC6hz964QWNBkyKfna8A2jOIx1zzZa43Gx/P09o= -cloud.google.com/go/policytroubleshooter v1.10.3/go.mod h1:+ZqG3agHT7WPb4EBIRqUv4OyIwRTZvsVDHZ8GlZaoxk= cloud.google.com/go/policytroubleshooter v1.10.9 h1:EHXkBYgHQtVH8P41G2xxmQbMwQh+o5ggno8l3/9CXaA= cloud.google.com/go/policytroubleshooter v1.10.9/go.mod h1:X8HEPVBWz8E+qwI/QXnhBLahEHdcuPO3M9YvSj0LDek= cloud.google.com/go/privatecatalog v0.8.0 h1:EPEJ1DpEGXLDnmc7mnCAqFmkwUJbIsaLAiLHVOkkwtc= cloud.google.com/go/privatecatalog v0.9.1 h1:B/18xGo+E0EMS9LOEQ0zXz7F2asMgmVgTYGSI89MHOA= -cloud.google.com/go/privatecatalog v0.9.2/go.mod h1:RMA4ATa8IXfzvjrhhK8J6H4wwcztab+oZph3c6WmtFc= -cloud.google.com/go/privatecatalog v0.9.3/go.mod h1:K5pn2GrVmOPjXz3T26mzwXLcKivfIJ9R5N79AFCF9UE= -cloud.google.com/go/privatecatalog v0.9.4/go.mod h1:SOjm93f+5hp/U3PqMZAHTtBtluqLygrDrVO8X8tYtG0= cloud.google.com/go/privatecatalog v0.9.5 h1:UZ0assTnATXSggoxUIh61RjTQ4P9zCMk/kEMbn0nMYA= -cloud.google.com/go/privatecatalog v0.9.5/go.mod h1:fVWeBOVe7uj2n3kWRGlUQqR/pOd450J9yZoOECcQqJk= cloud.google.com/go/privatecatalog v0.9.11 h1:t8dJpQf22H6COeDvp7TDl7+KuwLT6yVmqAVRIUIUj6U= cloud.google.com/go/privatecatalog v0.9.11/go.mod h1:awEF2a8M6UgoqVJcF/MthkF8SSo6OoWQ7TtPNxUlljY= cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU= cloud.google.com/go/pubsub v1.30.0 h1:vCge8m7aUKBJYOgrZp7EsNDf6QMd2CAlXZqWTn3yq6s= cloud.google.com/go/pubsub v1.33.0 h1:6SPCPvWav64tj0sVX/+npCBKhUi/UjJehy9op/V3p2g= -cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.34.0/go.mod h1:alj4l4rBg+N3YTFDDC+/YyFTs6JAjam2QfYsddcAW4c= cloud.google.com/go/pubsub v1.36.1 h1:dfEPuGCHGbWUhaMCTHUFjfroILEkx55iUmKBZTP5f+Y= -cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE= cloud.google.com/go/pubsub v1.41.0 h1:ZPaM/CvTO6T+1tQOs/jJ4OEMpjtel0PTLV7j1JK+ZrI= cloud.google.com/go/pubsub v1.41.0/go.mod h1:g+YzC6w/3N91tzG66e2BZtp7WrpBBMXVa3Y9zVoOGpk= cloud.google.com/go/pubsublite v1.7.0 h1:cb9fsrtpINtETHiJ3ECeaVzrfIVhcGjhhJEjybHXHao= @@ -892,323 +451,171 @@ cloud.google.com/go/pubsublite v1.8.2/go.mod h1:4r8GSa9NznExjuLPEJlF1VjOPOpgf3IT cloud.google.com/go/recaptchaenterprise v1.3.1 h1:u6EznTGzIdsyOsvm+Xkw0aSuKFXQlyjGE9a4exk6iNQ= cloud.google.com/go/recaptchaenterprise/v2 v2.7.0 h1:6iOCujSNJ0YS7oNymI64hXsjGq60T4FK1zdLugxbzvU= cloud.google.com/go/recaptchaenterprise/v2 v2.8.0 h1:4qPn0UZ1LUZje+JcmJcVRtsR2qbCMwCFU+MmsCRzDbk= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.0/go.mod h1:QuE8EdU9dEnesG8/kG3XuJyNsjEqMlMzg3v3scCJ46c= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.1/go.mod h1:JZYZJOeZjgSSTGP4uz7NlQ4/d1w5hGmksVgM0lbEij0= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.2/go.mod h1:kpaDBOpkwD4G0GVMzG1W6Doy1tFFC97XAV3xy+Rd/pw= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.3/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.4/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= -cloud.google.com/go/recaptchaenterprise/v2 v2.9.0/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= cloud.google.com/go/recaptchaenterprise/v2 v2.9.2 h1:U3Wfq12X9cVMuTpsWDSURnXF0Z9hSPTHj+xsnXDRLsw= -cloud.google.com/go/recaptchaenterprise/v2 v2.9.2/go.mod h1:trwwGkfhCmp05Ll5MSJPXY7yvnO0p4v3orGANAFHAuU= cloud.google.com/go/recaptchaenterprise/v2 v2.14.2 h1:80Mx0i3uyv5dPNUYsNPFk9GJ+19AmTlnWnXFCTC9NkI= cloud.google.com/go/recaptchaenterprise/v2 v2.14.2/go.mod h1:MwPgdgvBkE46aWuuXeBTCB8hQJ88p+CpXInROZYCTkc= cloud.google.com/go/recommendationengine v0.7.0 h1:VibRFCwWXrFebEWKHfZAt2kta6pS7Tlimsnms0fjv7k= cloud.google.com/go/recommendationengine v0.8.1 h1:nMr1OEVHuDambRn+/y4RmNAmnR/pXCuHtH0Y4tCgGRQ= -cloud.google.com/go/recommendationengine v0.8.2/go.mod h1:QIybYHPK58qir9CV2ix/re/M//Ty10OxjnnhWdaKS1Y= -cloud.google.com/go/recommendationengine v0.8.3/go.mod h1:m3b0RZV02BnODE9FeSvGv1qibFo8g0OnmB/RMwYy4V8= -cloud.google.com/go/recommendationengine v0.8.4/go.mod h1:GEteCf1PATl5v5ZsQ60sTClUE0phbWmo3rQ1Js8louU= cloud.google.com/go/recommendationengine v0.8.5 h1:ineqLswaCSBY0csYv5/wuXJMBlxATK6Xc5jJkpiTEdM= -cloud.google.com/go/recommendationengine v0.8.5/go.mod h1:A38rIXHGFvoPvmy6pZLozr0g59NRNREz4cx7F58HAsQ= cloud.google.com/go/recommendationengine v0.8.11 h1:STJYdA/e/MAh2ZSdjss5YE/d0t0nt0WotBF9V0pgpPQ= cloud.google.com/go/recommendationengine v0.8.11/go.mod h1:cEkU4tCXAF88a4boMFZym7U7uyxvVwcQtKzS85IbQio= cloud.google.com/go/recommender v1.9.0 h1:ZnFRY5R6zOVk2IDS1Jbv5Bw+DExCI5rFumsTnMXiu/A= cloud.google.com/go/recommender v1.11.0 h1:SuzbMJhDAiPro7tR9QP7EX97+TI31urjsIgNh9XQHl8= -cloud.google.com/go/recommender v1.11.0/go.mod h1:kPiRQhPyTJ9kyXPCG6u/dlPLbYfFlkwHNRwdzPVAoII= -cloud.google.com/go/recommender v1.11.1/go.mod h1:sGwFFAyI57v2Hc5LbIj+lTwXipGu9NW015rkaEM5B18= -cloud.google.com/go/recommender v1.11.2/go.mod h1:AeoJuzOvFR/emIcXdVFkspVXVTYpliRCmKNYDnyBv6Y= -cloud.google.com/go/recommender v1.11.3/go.mod h1:+FJosKKJSId1MBFeJ/TTyoGQZiEelQQIZMKYYD8ruK4= -cloud.google.com/go/recommender v1.12.0/go.mod h1:+FJosKKJSId1MBFeJ/TTyoGQZiEelQQIZMKYYD8ruK4= cloud.google.com/go/recommender v1.12.1 h1:LVLYS3r3u0MSCxQSDUtLSkporEGi9OAE6hGvayrZNPs= -cloud.google.com/go/recommender v1.12.1/go.mod h1:gf95SInWNND5aPas3yjwl0I572dtudMhMIG4ni8nr+0= cloud.google.com/go/recommender v1.12.7 h1:asEAoj4a3inPCdH8nbPaZDJWhR/xwfKi4tuSmIlaS2I= cloud.google.com/go/recommender v1.12.7/go.mod h1:lG8DVtczLltWuaCv4IVpNphONZTzaCC9KdxLYeZM5G4= cloud.google.com/go/redis v1.11.0 h1:JoAd3SkeDt3rLFAAxEvw6wV4t+8y4ZzfZcZmddqphQ8= cloud.google.com/go/redis v1.13.1 h1:YrjQnCC7ydk+k30op7DSjSHw1yAYhqYXFcOq1bSXRYA= -cloud.google.com/go/redis v1.13.2/go.mod h1:0Hg7pCMXS9uz02q+LoEVl5dNHUkIQv+C/3L76fandSA= -cloud.google.com/go/redis v1.13.3/go.mod h1:vbUpCKUAZSYzFcWKmICnYgRAhTFg9r+djWqFxDYXi4U= -cloud.google.com/go/redis v1.14.1/go.mod h1:MbmBxN8bEnQI4doZPC1BzADU4HGocHBk2de3SbgOkqs= cloud.google.com/go/redis v1.14.2 h1:QF0maEdVv0Fj/2roU8sX3NpiDBzP9ICYTO+5F32gQNo= -cloud.google.com/go/redis v1.14.2/go.mod h1:g0Lu7RRRz46ENdFKQ2EcQZBAJ2PtJHJLuiiRuEXwyQw= cloud.google.com/go/redis v1.16.4 h1:9CO6EcuM9/CpgtcjG6JZV+GFw3oDrRfwLwmvwo/uM1o= cloud.google.com/go/redis v1.16.4/go.mod h1:unCVfLP5eFrVhGLDnb7IaSaWxuZ+7cBgwwBwbdG9m9w= cloud.google.com/go/resourcemanager v1.7.0 h1:NRM0p+RJkaQF9Ee9JMnUV9BQ2QBIOq/v8M+Pbv/wmCs= cloud.google.com/go/resourcemanager v1.9.1 h1:QIAMfndPOHR6yTmMUB0ZN+HSeRmPjR/21Smq5/xwghI= -cloud.google.com/go/resourcemanager v1.9.2/go.mod h1:OujkBg1UZg5lX2yIyMo5Vz9O5hf7XQOSV7WxqxxMtQE= -cloud.google.com/go/resourcemanager v1.9.3/go.mod h1:IqrY+g0ZgLsihcfcmqSe+RKp1hzjXwG904B92AwBz6U= -cloud.google.com/go/resourcemanager v1.9.4/go.mod h1:N1dhP9RFvo3lUfwtfLWVxfUWq8+KUQ+XLlHLH3BoFJ0= cloud.google.com/go/resourcemanager v1.9.5 h1:AZWr1vWVDKGwfLsVhcN+vcwOz3xqqYxtmMa0aABCMms= -cloud.google.com/go/resourcemanager v1.9.5/go.mod h1:hep6KjelHA+ToEjOfO3garMKi/CLYwTqeAw7YiEI9x8= cloud.google.com/go/resourcemanager v1.9.11 h1:N8CmqszjKNOgJnrQVsg+g8VWIEGgcwsD5rPiay9cMC4= cloud.google.com/go/resourcemanager v1.9.11/go.mod h1:SbNAbjVLoi2rt9G74bEYb3aw1iwvyWPOJMnij4SsmHA= cloud.google.com/go/resourcesettings v1.5.0 h1:8Dua37kQt27CCWHm4h/Q1XqCF6ByD7Ouu49xg95qJzI= cloud.google.com/go/resourcesettings v1.6.1 h1:Fdyq418U69LhvNPFdlEO29w+DRRjwDA4/pFamm4ksAg= -cloud.google.com/go/resourcesettings v1.6.2/go.mod h1:mJIEDd9MobzunWMeniaMp6tzg4I2GvD3TTmPkc8vBXk= -cloud.google.com/go/resourcesettings v1.6.3/go.mod h1:pno5D+7oDYkMWZ5BpPsb4SO0ewg3IXcmmrUZaMJrFic= -cloud.google.com/go/resourcesettings v1.6.4/go.mod h1:pYTTkWdv2lmQcjsthbZLNBP4QW140cs7wqA3DuqErVI= cloud.google.com/go/resourcesettings v1.6.5 h1:BTr5MVykJwClASci/7Og4Qfx70aQ4n3epsNLj94ZYgw= -cloud.google.com/go/resourcesettings v1.6.5/go.mod h1:WBOIWZraXZOGAgoR4ukNj0o0HiSMO62H9RpFi9WjP9I= cloud.google.com/go/resourcesettings v1.7.4 h1:1VwLfvJi8QtGrKPwuisGqr6gcgaCSR6A57wIvN+fqkM= cloud.google.com/go/resourcesettings v1.7.4/go.mod h1:seBdLuyeq+ol2u9G2+74GkSjQaxaBWF+vVb6mVzQFG0= cloud.google.com/go/retail v1.12.0 h1:1Dda2OpFNzIb4qWgFZjYlpP7sxX3aLeypKG6A3H4Yys= cloud.google.com/go/retail v1.14.1 h1:gYBrb9u/Hc5s5lUTFXX1Vsbc/9BEvgtioY6ZKaK0DK8= -cloud.google.com/go/retail v1.14.2/go.mod h1:W7rrNRChAEChX336QF7bnMxbsjugcOCPU44i5kbLiL8= -cloud.google.com/go/retail v1.14.3/go.mod h1:Omz2akDHeSlfCq8ArPKiBxlnRpKEBjUH386JYFLUvXo= -cloud.google.com/go/retail v1.14.4/go.mod h1:l/N7cMtY78yRnJqp5JW8emy7MB1nz8E4t2yfOmklYfg= -cloud.google.com/go/retail v1.15.1/go.mod h1:In9nSBOYhLbDGa87QvWlnE1XA14xBN2FpQRiRsUs9wU= cloud.google.com/go/retail v1.16.0 h1:Fn1GuAua1c6crCGqfJ1qMxG1Xh10Tg/x5EUODEHMqkw= -cloud.google.com/go/retail v1.16.0/go.mod h1:LW7tllVveZo4ReWt68VnldZFWJRzsh9np+01J9dYWzE= cloud.google.com/go/retail v1.17.4 h1:YJgpBwCarAPqzaJS8ycIhyn2sAQT1RhTJRiTVBjtJAI= cloud.google.com/go/retail v1.17.4/go.mod h1:oPkL1FzW7D+v/hX5alYIx52ro2FY/WPAviwR1kZZTMs= cloud.google.com/go/run v0.9.0 h1:ydJQo+k+MShYnBfhaRHSZYeD/SQKZzZLAROyfpeD9zw= -cloud.google.com/go/run v1.2.0/go.mod h1:36V1IlDzQ0XxbQjUx6IYbw8H3TJnWvhii963WW3B/bo= cloud.google.com/go/run v1.3.0 h1:NR3ibstYygrvNZQ+7+rSWmD+oKvbjB/B9Ve9mqhkj6s= -cloud.google.com/go/run v1.3.0/go.mod h1:S/osX/4jIPZGg+ssuqh6GNgg7syixKe3YnprwehzHKU= -cloud.google.com/go/run v1.3.1/go.mod h1:cymddtZOzdwLIAsmS6s+Asl4JoXIDm/K1cpZTxV4Q5s= -cloud.google.com/go/run v1.3.2/go.mod h1:SIhmqArbjdU/D9M6JoHaAqnAMKLFtXaVdNeq04NjnVE= -cloud.google.com/go/run v1.3.3/go.mod h1:WSM5pGyJ7cfYyYbONVQBN4buz42zFqwG67Q3ch07iK4= cloud.google.com/go/run v1.3.4 h1:m9WDA7DzTpczhZggwYlZcBWgCRb+kgSIisWn1sbw2rQ= -cloud.google.com/go/run v1.3.4/go.mod h1:FGieuZvQ3tj1e9GnzXqrMABSuir38AJg5xhiYq+SF3o= cloud.google.com/go/run v1.4.0 h1:ai1rnbX92iPqWg9MrbDbebsxlUSAiOK6N9dEDDQeVA0= cloud.google.com/go/run v1.4.0/go.mod h1:4G9iHLjdOC+CQ0CzA0+6nLeR6NezVPmlj+GULmb0zE4= cloud.google.com/go/scheduler v1.9.0 h1:NpQAHtx3sulByTLe2dMwWmah8PWgeoieFPpJpArwFV0= cloud.google.com/go/scheduler v1.10.1 h1:yoZbZR8880KgPGLmACOMCiY2tPk+iX4V/dkxqTirlz8= -cloud.google.com/go/scheduler v1.10.2/go.mod h1:O3jX6HRH5eKCA3FutMw375XHZJudNIKVonSCHv7ropY= -cloud.google.com/go/scheduler v1.10.3/go.mod h1:8ANskEM33+sIbpJ+R4xRfw/jzOG+ZFE8WVLy7/yGvbc= -cloud.google.com/go/scheduler v1.10.4/go.mod h1:MTuXcrJC9tqOHhixdbHDFSIuh7xZF2IysiINDuiq6NI= -cloud.google.com/go/scheduler v1.10.5/go.mod h1:MTuXcrJC9tqOHhixdbHDFSIuh7xZF2IysiINDuiq6NI= cloud.google.com/go/scheduler v1.10.6 h1:5U8iXLoQ03qOB+ZXlAecU7fiE33+u3QiM9nh4cd0eTE= -cloud.google.com/go/scheduler v1.10.6/go.mod h1:pe2pNCtJ+R01E06XCDOJs1XvAMbv28ZsQEbqknxGOuE= cloud.google.com/go/scheduler v1.10.12 h1:8BxDXoHCcsAe2fXsvFrkBbTxgl+5JBrIy1+/HRS0nxY= cloud.google.com/go/scheduler v1.10.12/go.mod h1:6DRtOddMWJ001HJ6MS148rtLSh/S2oqd2hQC3n5n9fQ= cloud.google.com/go/secretmanager v1.10.0 h1:pu03bha7ukxF8otyPKTFdDz+rr9sE3YauS5PliDXK60= cloud.google.com/go/secretmanager v1.11.1 h1:cLTCwAjFh9fKvU6F13Y4L9vPcx9yiWPyWXE4+zkuEQs= -cloud.google.com/go/secretmanager v1.11.2/go.mod h1:MQm4t3deoSub7+WNwiC4/tRYgDBHJgJPvswqQVB1Vss= -cloud.google.com/go/secretmanager v1.11.3/go.mod h1:0bA2o6FabmShrEy328i67aV+65XoUFFSmVeLBn/51jI= -cloud.google.com/go/secretmanager v1.11.4/go.mod h1:wreJlbS9Zdq21lMzWmJ0XhWW2ZxgPeahsqeV/vZoJ3w= cloud.google.com/go/secretmanager v1.11.5 h1:82fpF5vBBvu9XW4qj0FU2C6qVMtj1RM/XHwKXUEAfYY= -cloud.google.com/go/secretmanager v1.11.5/go.mod h1:eAGv+DaCHkeVyQi0BeXgAHOU0RdrMeZIASKc+S7VqH4= cloud.google.com/go/secretmanager v1.13.5/go.mod h1:/OeZ88l5Z6nBVilV0SXgv6XJ243KP2aIhSWRMrbvDCQ= cloud.google.com/go/secretmanager v1.13.6 h1:0ZEl/LuoB4xQsjVfQt3Gi/dZfOv36n4JmdPrMargzYs= cloud.google.com/go/secretmanager v1.13.6/go.mod h1:x2ySyOrqv3WGFRFn2Xk10iHmNmvmcEVSSqc30eb1bhw= cloud.google.com/go/security v1.13.0 h1:PYvDxopRQBfYAXKAuDpFCKBvDOWPWzp9k/H5nB3ud3o= cloud.google.com/go/security v1.15.1 h1:jR3itwycg/TgGA0uIgTItcVhA55hKWiNJxaNNpQJaZE= -cloud.google.com/go/security v1.15.2/go.mod h1:2GVE/v1oixIRHDaClVbHuPcZwAqFM28mXuAKCfMgYIg= -cloud.google.com/go/security v1.15.3/go.mod h1:gQ/7Q2JYUZZgOzqKtw9McShH+MjNvtDpL40J1cT+vBs= -cloud.google.com/go/security v1.15.4/go.mod h1:oN7C2uIZKhxCLiAAijKUCuHLZbIt/ghYEo8MqwD/Ty4= cloud.google.com/go/security v1.15.5 h1:wTKJQ10j8EYgvE8Y+KhovxDRVDk2iv/OsxZ6GrLP3kE= -cloud.google.com/go/security v1.15.5/go.mod h1:KS6X2eG3ynWjqcIX976fuToN5juVkF6Ra6c7MPnldtc= cloud.google.com/go/security v1.17.4 h1:ERhxAa02mnMEIIAXvzje+qJ+yWniP6l5uOX+k9ELCaA= cloud.google.com/go/security v1.17.4/go.mod h1:KMuDJH+sEB3KTODd/tLJ7kZK+u2PQt+Cfu0oAxzIhgo= cloud.google.com/go/securitycenter v1.19.0 h1:AF3c2s3awNTMoBtMX3oCUoOMmGlYxGOeuXSYHNBkf14= cloud.google.com/go/securitycenter v1.23.0 h1:XOGJ9OpnDtqg8izd7gYk/XUhj8ytjIalyjjsR6oyG0M= -cloud.google.com/go/securitycenter v1.23.1/go.mod h1:w2HV3Mv/yKhbXKwOCu2i8bCuLtNP1IMHuiYQn4HJq5s= -cloud.google.com/go/securitycenter v1.24.1/go.mod h1:3h9IdjjHhVMXdQnmqzVnM7b0wMn/1O/U20eWVpMpZjI= -cloud.google.com/go/securitycenter v1.24.2/go.mod h1:l1XejOngggzqwr4Fa2Cn+iWZGf+aBLTXtB/vXjy5vXM= -cloud.google.com/go/securitycenter v1.24.3/go.mod h1:l1XejOngggzqwr4Fa2Cn+iWZGf+aBLTXtB/vXjy5vXM= cloud.google.com/go/securitycenter v1.24.4 h1:/5jjkZ+uGe8hZ7pvd7pO30VW/a+pT2MrrdgOqjyucKQ= -cloud.google.com/go/securitycenter v1.24.4/go.mod h1:PSccin+o1EMYKcFQzz9HMMnZ2r9+7jbc+LvPjXhpwcU= cloud.google.com/go/securitycenter v1.33.1 h1:K+jfFUTum2jl//uWCN+QKkKXRgidxTyGfGTqXPyDvUY= cloud.google.com/go/securitycenter v1.33.1/go.mod h1:jeFisdYUWHr+ig72T4g0dnNCFhRwgwGoQV6GFuEwafw= cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE= cloud.google.com/go/servicedirectory v1.9.0 h1:SJwk0XX2e26o25ObYUORXx6torSFiYgsGkWSkZgkoSU= cloud.google.com/go/servicedirectory v1.11.0 h1:pBWpjCFVGWkzVTkqN3TBBIqNSoSHY86/6RL0soSQ4z8= -cloud.google.com/go/servicedirectory v1.11.0/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.1/go.mod h1:tJywXimEWzNzw9FvtNjsQxxJ3/41jseeILgwU/QLrGI= -cloud.google.com/go/servicedirectory v1.11.2/go.mod h1:KD9hCLhncWRV5jJphwIpugKwM5bn1x0GyVVD4NO8mGg= -cloud.google.com/go/servicedirectory v1.11.3/go.mod h1:LV+cHkomRLr67YoQy3Xq2tUXBGOs5z5bPofdq7qtiAw= cloud.google.com/go/servicedirectory v1.11.4 h1:da7HFI1229kyzIyuVEzHXip0cw0d+E0s8mjQby0WN+k= -cloud.google.com/go/servicedirectory v1.11.4/go.mod h1:Bz2T9t+/Ehg6x+Y7Ycq5xiShYLD96NfEsWNHyitj1qM= cloud.google.com/go/servicedirectory v1.11.11 h1:8Ky2lY0CWJJIIlsc+rKTn6C3SqOuVEwT3brDC6TJCjk= cloud.google.com/go/servicedirectory v1.11.11/go.mod h1:pnynaftaj9LmRLIc6t3r7r7rdCZZKKxui/HaF/RqYfs= cloud.google.com/go/servicemanagement v1.8.0 h1:fopAQI/IAzlxnVeiKn/8WiV6zKndjFkvi+gzu+NjywY= cloud.google.com/go/serviceusage v1.6.0 h1:rXyq+0+RSIm3HFypctp7WoXxIA563rn206CfMWdqXX4= cloud.google.com/go/shell v1.6.0 h1:wT0Uw7ib7+AgZST9eCDygwTJn4+bHMDtZo5fh7kGWDU= cloud.google.com/go/shell v1.7.1 h1:aHbwH9LSqs4r2rbay9f6fKEls61TAjT63jSyglsw7sI= -cloud.google.com/go/shell v1.7.2/go.mod h1:KqRPKwBV0UyLickMn0+BY1qIyE98kKyI216sH/TuHmc= -cloud.google.com/go/shell v1.7.3/go.mod h1:cTTEz/JdaBsQAeTQ3B6HHldZudFoYBOqjteev07FbIc= -cloud.google.com/go/shell v1.7.4/go.mod h1:yLeXB8eKLxw0dpEmXQ/FjriYrBijNsONpwnWsdPqlKM= cloud.google.com/go/shell v1.7.5 h1:3Fq2hzO0ZSyaqBboJrFkwwf/qMufDtqwwA6ep8EZxEI= -cloud.google.com/go/shell v1.7.5/go.mod h1:hL2++7F47/IfpfTO53KYf1EC+F56k3ThfNEXd4zcuiE= cloud.google.com/go/shell v1.7.11 h1:RobTXyL33DQITYQh//KJ9GjS4bsdj4fGmm2rkb/ywzM= cloud.google.com/go/shell v1.7.11/go.mod h1:SywZHWac7onifaT9m9MmegYp3GgCLm+tgk+w2lXK8vg= cloud.google.com/go/spanner v1.45.0 h1:7VdjZ8zj4sHbDw55atp5dfY6kn1j9sam9DRNpPQhqR4= -cloud.google.com/go/spanner v1.49.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= cloud.google.com/go/spanner v1.50.0 h1:QrJFOpaxCXdXF+GkiruLz642PHxkdj68PbbnLw3O2Zw= -cloud.google.com/go/spanner v1.50.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/spanner v1.51.0/go.mod h1:c5KNo5LQ1X5tJwma9rSQZsXNBDNvj4/n8BVc3LNahq0= -cloud.google.com/go/spanner v1.53.0/go.mod h1:liG4iCeLqm5L3fFLU5whFITqP0e0orsAW1uUSrd4rws= -cloud.google.com/go/spanner v1.53.1/go.mod h1:liG4iCeLqm5L3fFLU5whFITqP0e0orsAW1uUSrd4rws= -cloud.google.com/go/spanner v1.54.0/go.mod h1:wZvSQVBgngF0Gq86fKup6KIYmN2be7uOKjtK97X+bQU= -cloud.google.com/go/spanner v1.55.0/go.mod h1:HXEznMUVhC+PC+HDyo9YFG2Ajj5BQDkcbqB9Z2Ffxi0= cloud.google.com/go/spanner v1.56.0 h1:o/Cv7/zZ1WgRXVCd5g3Nc23ZI39p/1pWFqFwvg6Wcu8= -cloud.google.com/go/spanner v1.56.0/go.mod h1:DndqtUKQAt3VLuV2Le+9Y3WTnq5cNKrnLb/Piqcj+h0= -cloud.google.com/go/spanner v1.57.0/go.mod h1:aXQ5QDdhPRIqVhYmnkAdwPYvj/DRN0FguclhEWw+jOo= cloud.google.com/go/spanner v1.65.0 h1:XK15cs9lFFQo5n4Wh9nfrcPXAxWln6NdodDiQKmoD08= cloud.google.com/go/spanner v1.65.0/go.mod h1:dQGB+w5a67gtyE3qSKPPxzniedrnAmV6tewQeBY7Hxs= cloud.google.com/go/speech v1.15.0 h1:JEVoWGNnTF128kNty7T4aG4eqv2z86yiMJPT9Zjp+iw= cloud.google.com/go/speech v1.19.0 h1:MCagaq8ObV2tr1kZJcJYgXYbIn8Ai5rp42tyGYw9rls= -cloud.google.com/go/speech v1.19.0/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.1/go.mod h1:WcuaWz/3hOlzPFOVo9DUsblMIHwxP589y6ZMtaG+iAA= -cloud.google.com/go/speech v1.19.2/go.mod h1:2OYFfj+Ch5LWjsaSINuCZsre/789zlcCI3SY4oAi2oI= -cloud.google.com/go/speech v1.20.1/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= -cloud.google.com/go/speech v1.21.0/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= cloud.google.com/go/speech v1.21.1 h1:nuFc+Kj5B8de75nN4FdPyUbI2SiBoHZG6BLurXL56Q0= -cloud.google.com/go/speech v1.21.1/go.mod h1:E5GHZXYQlkqWQwY5xRSLHw2ci5NMQNG52FfMU1aZrIA= cloud.google.com/go/speech v1.24.0 h1:3j+WpeBY57C0FDJxg317vpKgOLjL/kNxlcNPGSqXkqE= cloud.google.com/go/speech v1.24.0/go.mod h1:HcVyIh5jRXM5zDMcbFCW+DF2uK/MSGN6Rastt6bj1ic= cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= -cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/storage v1.37.0 h1:WI8CsaFO8Q9KjPVtsZ5Cmi0dXV25zMoX0FklT7c3Jm4= -cloud.google.com/go/storage v1.37.0/go.mod h1:i34TiT2IhiNDmcj65PqwCjcoUX7Z5pLzS8DEmoiFq1k= cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= cloud.google.com/go/storage v1.41.0/go.mod h1:J1WCa/Z2FcgdEDuPUY8DxT5I+d9mFKsCepp5vR6Sq80= cloud.google.com/go/storagetransfer v1.8.0 h1:5T+PM+3ECU3EY2y9Brv0Sf3oka8pKmsCfpQ07+91G9o= cloud.google.com/go/storagetransfer v1.10.0 h1:+ZLkeXx0K0Pk5XdDmG0MnUVqIR18lllsihU/yq39I8Q= -cloud.google.com/go/storagetransfer v1.10.1/go.mod h1:rS7Sy0BtPviWYTTJVWCSV4QrbBitgPeuK4/FKa4IdLs= -cloud.google.com/go/storagetransfer v1.10.2/go.mod h1:meIhYQup5rg9juQJdyppnA/WLQCOguxtk1pr3/vBWzA= -cloud.google.com/go/storagetransfer v1.10.3/go.mod h1:Up8LY2p6X68SZ+WToswpQbQHnJpOty/ACcMafuey8gc= cloud.google.com/go/storagetransfer v1.10.4 h1:dy4fL3wO0VABvzM05ycMUPFHxTPbJz9Em8ikAJVqSbI= -cloud.google.com/go/storagetransfer v1.10.4/go.mod h1:vef30rZKu5HSEf/x1tK3WfWrL0XVoUQN/EPDRGPzjZs= cloud.google.com/go/storagetransfer v1.10.10 h1:GfxaYqX+kwlrSrJAENNmRTCGmSTgvouvS3XhgwKpOT8= cloud.google.com/go/storagetransfer v1.10.10/go.mod h1:8+nX+WgQ2ZJJnK8e+RbK/zCXk8T7HdwyQAJeY7cEcm0= cloud.google.com/go/talent v1.5.0 h1:nI9sVZPjMKiO2q3Uu0KhTDVov3Xrlpt63fghP9XjyEM= cloud.google.com/go/talent v1.6.2 h1:j46ZgD6N2YdpFPux9mc7OAf4YK3tiBCsbLKc8rQx+bU= -cloud.google.com/go/talent v1.6.3/go.mod h1:xoDO97Qd4AK43rGjJvyBHMskiEf3KulgYzcH6YWOVoo= -cloud.google.com/go/talent v1.6.4/go.mod h1:QsWvi5eKeh6gG2DlBkpMaFYZYrYUnIpo34f6/V5QykY= -cloud.google.com/go/talent v1.6.5/go.mod h1:Mf5cma696HmE+P2BWJ/ZwYqeJXEeU0UqjHFXVLadEDI= cloud.google.com/go/talent v1.6.6 h1:JssV0CE3FNujuSWn7SkosOzg7qrMxVnt6txOfGcMSa4= -cloud.google.com/go/talent v1.6.6/go.mod h1:y/WQDKrhVz12WagoarpAIyKKMeKGKHWPoReZ0g8tseQ= cloud.google.com/go/talent v1.6.12 h1:JN721EjG+UTfHVVaMhyxwKCCJPjUc8PiS0RnW/7kWfE= cloud.google.com/go/talent v1.6.12/go.mod h1:nT9kNVuJhZX2QgqKZS6t6eCWZs5XEBYRBv6bIMnPmo4= cloud.google.com/go/texttospeech v1.6.0 h1:H4g1ULStsbVtalbZGktyzXzw6jP26RjVGYx9RaYjBzc= cloud.google.com/go/texttospeech v1.7.1 h1:S/pR/GZT9p15R7Y2dk2OXD/3AufTct/NSxT4a7nxByw= -cloud.google.com/go/texttospeech v1.7.2/go.mod h1:VYPT6aTOEl3herQjFHYErTlSZJ4vB00Q2ZTmuVgluD4= -cloud.google.com/go/texttospeech v1.7.3/go.mod h1:Av/zpkcgWfXlDLRYob17lqMstGZ3GqlvJXqKMp2u8so= -cloud.google.com/go/texttospeech v1.7.4/go.mod h1:vgv0002WvR4liGuSd5BJbWy4nDn5Ozco0uJymY5+U74= cloud.google.com/go/texttospeech v1.7.5 h1:dxY2Q5mHCbrGa3oPR2O3PCicdnvKa1JmwGQK36EFLOw= -cloud.google.com/go/texttospeech v1.7.5/go.mod h1:tzpCuNWPwrNJnEa4Pu5taALuZL4QRRLcb+K9pbhXT6M= cloud.google.com/go/texttospeech v1.7.11 h1:jzko1ahItjLYEWr6n3lTIoBSinD1JzavEuDzYLWZNko= cloud.google.com/go/texttospeech v1.7.11/go.mod h1:Ua125HU+WT2IkIo5MzQtuNpNEk72soShJQVdorZ1SAE= cloud.google.com/go/tpu v1.5.0 h1:/34T6CbSi+kTv5E19Q9zbU/ix8IviInZpzwz3rsFE+A= cloud.google.com/go/tpu v1.6.1 h1:kQf1jgPY04UJBYYjNUO+3GrZtIb57MfGAW2bwgLbR3A= -cloud.google.com/go/tpu v1.6.2/go.mod h1:NXh3NDwt71TsPZdtGWgAG5ThDfGd32X1mJ2cMaRlVgU= -cloud.google.com/go/tpu v1.6.3/go.mod h1:lxiueqfVMlSToZY1151IaZqp89ELPSrk+3HIQ5HRkbY= -cloud.google.com/go/tpu v1.6.4/go.mod h1:NAm9q3Rq2wIlGnOhpYICNI7+bpBebMJbh0yyp3aNw1Y= cloud.google.com/go/tpu v1.6.5 h1:C8YyYda8WtNdBoCgFwwBzZd+S6+EScHOxM/z1h0NNp8= -cloud.google.com/go/tpu v1.6.5/go.mod h1:P9DFOEBIBhuEcZhXi+wPoVy/cji+0ICFi4TtTkMHSSs= cloud.google.com/go/tpu v1.6.11 h1:uMrwnK05cocNt3OOp+mZ16xlvIKaXUt3QUXkUbG4LdM= cloud.google.com/go/tpu v1.6.11/go.mod h1:W0C4xaSj1Ay3VX/H96FRvLt2HDs0CgdRPVI4e7PoCDk= cloud.google.com/go/trace v1.9.0 h1:olxC0QHC59zgJVALtgqfD9tGk0lfeCP5/AGXL3Px/no= cloud.google.com/go/trace v1.10.1 h1:EwGdOLCNfYOOPtgqo+D2sDLZmRCEO1AagRTJCU6ztdg= -cloud.google.com/go/trace v1.10.2/go.mod h1:NPXemMi6MToRFcSxRl2uDnu/qAlAQ3oULUphcHGh1vA= -cloud.google.com/go/trace v1.10.3/go.mod h1:Ke1bgfc73RV3wUFml+uQp7EsDw4dGaETLxB7Iq/r4CY= -cloud.google.com/go/trace v1.10.4/go.mod h1:Nso99EDIK8Mj5/zmB+iGr9dosS/bzWCJ8wGmE6TXNWY= cloud.google.com/go/trace v1.10.5 h1:0pr4lIKJ5XZFYD9GtxXEWr0KkVeigc3wlGpZco0X1oA= -cloud.google.com/go/trace v1.10.5/go.mod h1:9hjCV1nGBCtXbAE4YK7OqJ8pmPYSxPA0I67JwRd5s3M= cloud.google.com/go/trace v1.10.11/go.mod h1:fUr5L3wSXerNfT0f1bBg08W4axS2VbHGgYcfH4KuTXU= cloud.google.com/go/trace v1.10.12 h1:GoGZv1iAXEa73HgSGNjRl2vKqp5/f2AeKqErRFXA2kg= cloud.google.com/go/trace v1.10.12/go.mod h1:tYkAIta/gxgbBZ/PIzFxSH5blajgX4D00RpQqCG/GZs= cloud.google.com/go/translate v1.7.0 h1:GvLP4oQ4uPdChBmBaUSa/SaZxCdyWELtlAaKzpHsXdA= -cloud.google.com/go/translate v1.8.2/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= cloud.google.com/go/translate v1.9.0 h1:0na4gC54Lu05ir00dmUSuMkLAojDe1ALq4hBTUkhwjE= -cloud.google.com/go/translate v1.9.0/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.1/go.mod h1:TWIgDZknq2+JD4iRcojgeDtqGEp154HN/uL6hMvylS8= -cloud.google.com/go/translate v1.9.2/go.mod h1:E3Tc6rUTsQkVrXW6avbUhKJSr7ZE3j7zNmqzXKHqRrY= -cloud.google.com/go/translate v1.9.3/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= -cloud.google.com/go/translate v1.10.0/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= cloud.google.com/go/translate v1.10.1 h1:upovZ0wRMdzZvXnu+RPam41B0mRJ+coRXFP2cYFJ7ew= -cloud.google.com/go/translate v1.10.1/go.mod h1:adGZcQNom/3ogU65N9UXHOnnSvjPwA/jKQUMnsYXOyk= cloud.google.com/go/translate v1.10.3/go.mod h1:GW0vC1qvPtd3pgtypCv4k4U8B7EdgK9/QEF2aJEUovs= cloud.google.com/go/translate v1.10.7 h1:W16MpZ2Z3TWoHbNHmyHz9As276lGVTSwxRcquv454R0= cloud.google.com/go/translate v1.10.7/go.mod h1:mH/+8tvcItuy1cOWqU+/Y3iFHgkVUObNIQYI/kiFFiY= cloud.google.com/go/video v1.15.0 h1:upIbnGI0ZgACm58HPjAeBMleW3sl5cT84AbYQ8PWOgM= -cloud.google.com/go/video v1.19.0/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= cloud.google.com/go/video v1.20.0 h1:AkjXyJfQ7DtPyDOAbTMeiGcuKsO8/iKSb3fAmTUHYSg= -cloud.google.com/go/video v1.20.0/go.mod h1:U3G3FTnsvAGqglq9LxgqzOiBc/Nt8zis8S+850N2DUM= -cloud.google.com/go/video v1.20.1/go.mod h1:3gJS+iDprnj8SY6pe0SwLeC5BUW80NjhwX7INWEuWGU= -cloud.google.com/go/video v1.20.2/go.mod h1:lrixr5JeKNThsgfM9gqtwb6Okuqzfo4VrY2xynaViTA= -cloud.google.com/go/video v1.20.3/go.mod h1:TnH/mNZKVHeNtpamsSPygSR0iHtvrR/cW1/GDjN5+GU= cloud.google.com/go/video v1.20.4 h1:TXwotxkShP1OqgKsbd+b8N5hrIHavSyLGvYnLGCZ7xc= -cloud.google.com/go/video v1.20.4/go.mod h1:LyUVjyW+Bwj7dh3UJnUGZfyqjEto9DnrvTe1f/+QrW0= cloud.google.com/go/video v1.22.0 h1:+FTZi7NtT4FV2Y1j3zC3zYjaRrlGqKsZpbLweredEWM= cloud.google.com/go/video v1.22.0/go.mod h1:CxPshUNAb1ucnzbtruEHlAal9XY+SPG2cFqC/woJzII= cloud.google.com/go/videointelligence v1.10.0 h1:Uh5BdoET8XXqXX2uXIahGb+wTKbLkGH7s4GXR58RrG8= cloud.google.com/go/videointelligence v1.11.1 h1:MBMWnkQ78GQnRz5lfdTAbBq/8QMCF3wahgtHh3s/J+k= -cloud.google.com/go/videointelligence v1.11.2/go.mod h1:ocfIGYtIVmIcWk1DsSGOoDiXca4vaZQII1C85qtoplc= -cloud.google.com/go/videointelligence v1.11.3/go.mod h1:tf0NUaGTjU1iS2KEkGWvO5hRHeCkFK3nPo0/cOZhZAo= -cloud.google.com/go/videointelligence v1.11.4/go.mod h1:kPBMAYsTPFiQxMLmmjpcZUMklJp3nC9+ipJJtprccD8= cloud.google.com/go/videointelligence v1.11.5 h1:mYaWH8uhUCXLJCN3gdXswKzRa2+lK0zN6/KsIubm6pE= -cloud.google.com/go/videointelligence v1.11.5/go.mod h1:/PkeQjpRponmOerPeJxNPuxvi12HlW7Em0lJO14FC3I= cloud.google.com/go/videointelligence v1.11.11 h1:zl8xijOEavernn/t6mZZ4fg0pIVc2yquHH73oj0Leo4= cloud.google.com/go/videointelligence v1.11.11/go.mod h1:dab2Ca3AXT6vNJmt3/6ieuquYRckpsActDekLcsd6dU= cloud.google.com/go/vision v1.2.0 h1:/CsSTkbmO9HC8iQpxbK8ATms3OQaX3YQUeTMGCxlaK4= cloud.google.com/go/vision/v2 v2.7.0 h1:8C8RXUJoflCI4yVdqhTy9tRyygSHmp60aP363z23HKg= cloud.google.com/go/vision/v2 v2.7.2 h1:ccK6/YgPfGHR/CyESz1mvIbsht5Y2xRsWCPqmTNydEw= -cloud.google.com/go/vision/v2 v2.7.3/go.mod h1:V0IcLCY7W+hpMKXK1JYE0LV5llEqVmj+UJChjvA1WsM= -cloud.google.com/go/vision/v2 v2.7.4/go.mod h1:ynDKnsDN/0RtqkKxQZ2iatv3Dm9O+HfRb5djl7l4Vvw= -cloud.google.com/go/vision/v2 v2.7.5/go.mod h1:GcviprJLFfK9OLf0z8Gm6lQb6ZFUulvpZws+mm6yPLM= -cloud.google.com/go/vision/v2 v2.7.6/go.mod h1:ZkvWTVNPBU3YZYzgF9Y1jwEbD1NBOCyJn0KFdQfE6Bw= cloud.google.com/go/vision/v2 v2.8.0 h1:W52z1b6LdGI66MVhE70g/NFty9zCYYcjdKuycqmlhtg= -cloud.google.com/go/vision/v2 v2.8.0/go.mod h1:ocqDiA2j97pvgogdyhoxiQp2ZkDCyr0HWpicywGGRhU= cloud.google.com/go/vision/v2 v2.8.6 h1:HyFEUXQa0SvlF0LASCn/x+juNCH4kIXQrUqi6SIcYvE= cloud.google.com/go/vision/v2 v2.8.6/go.mod h1:G3v0uovxCye3u369JfrHGY43H6u/IQ08x9dw5aVH8yY= cloud.google.com/go/vmmigration v1.6.0 h1:Azs5WKtfOC8pxvkyrDvt7J0/4DYBch0cVbuFfCCFt5k= cloud.google.com/go/vmmigration v1.7.1 h1:gnjIclgqbEMc+cF5IJuPxp53wjBIlqZ8h9hE8Rkwp7A= -cloud.google.com/go/vmmigration v1.7.2/go.mod h1:iA2hVj22sm2LLYXGPT1pB63mXHhrH1m/ruux9TwWLd8= -cloud.google.com/go/vmmigration v1.7.3/go.mod h1:ZCQC7cENwmSWlwyTrZcWivchn78YnFniEQYRWQ65tBo= -cloud.google.com/go/vmmigration v1.7.4/go.mod h1:yBXCmiLaB99hEl/G9ZooNx2GyzgsjKnw5fWcINRgD70= cloud.google.com/go/vmmigration v1.7.5 h1:5v9RT2vWyuw3pK2ox0HQpkoftO7Q7/8591dTxxQc79g= -cloud.google.com/go/vmmigration v1.7.5/go.mod h1:pkvO6huVnVWzkFioxSghZxIGcsstDvYiVCxQ9ZH3eYI= cloud.google.com/go/vmmigration v1.7.11 h1:yqwkTPpvSw9dUfnl9/APAVrwO9UW1jJZtgbZpNQ+WdU= cloud.google.com/go/vmmigration v1.7.11/go.mod h1:PmD1fDB0TEHGQR1tDZt9GEXFB9mnKKalLcTVRJKzcQA= cloud.google.com/go/vmwareengine v0.3.0 h1:b0NBu7S294l0gmtrT0nOJneMYgZapr5x9tVWvgDoVEM= cloud.google.com/go/vmwareengine v1.0.0 h1:qsJ0CPlOQu/3MFBGklu752v3AkD+Pdu091UmXJ+EjTA= -cloud.google.com/go/vmwareengine v1.0.0/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.1/go.mod h1:aT3Xsm5sNx0QShk1Jc1B8OddrxAScYLwzVoaiXfdzzk= -cloud.google.com/go/vmwareengine v1.0.2/go.mod h1:xMSNjIk8/itYrz1JA8nV3Ajg4L4n3N+ugP8JKzk3OaA= -cloud.google.com/go/vmwareengine v1.0.3/go.mod h1:QSpdZ1stlbfKtyt6Iu19M6XRxjmXO+vb5a/R6Fvy2y4= cloud.google.com/go/vmwareengine v1.1.1 h1:EGdDi9QbqThfZq3ILcDK5g+m9jTevc34AY5tACx5v7k= -cloud.google.com/go/vmwareengine v1.1.1/go.mod h1:nMpdsIVkUrSaX8UvmnBhzVzG7PPvNYc5BszcvIVudYs= cloud.google.com/go/vmwareengine v1.2.0 h1:9Fjn/RoeOMo8UQt1TbXmmw7rJApC26BqnISAI1AERcc= cloud.google.com/go/vmwareengine v1.2.0/go.mod h1:rPjCHu6hG9N8d6PhkoDWFkqL9xpbFY+ueVW+0pNFbZg= cloud.google.com/go/vpcaccess v1.6.0 h1:FOe6CuiQD3BhHJWt7E8QlbBcaIzVRddupwJlp7eqmn4= cloud.google.com/go/vpcaccess v1.7.1 h1:ram0GzjNWElmbxXMIzeOZUkQ9J8ZAahD6V8ilPGqX0Y= -cloud.google.com/go/vpcaccess v1.7.2/go.mod h1:mmg/MnRHv+3e8FJUjeSibVFvQF1cCy2MsFaFqxeY1HU= -cloud.google.com/go/vpcaccess v1.7.3/go.mod h1:YX4skyfW3NC8vI3Fk+EegJnlYFatA+dXK4o236EUCUc= -cloud.google.com/go/vpcaccess v1.7.4/go.mod h1:lA0KTvhtEOb/VOdnH/gwPuOzGgM+CWsmGu6bb4IoMKk= cloud.google.com/go/vpcaccess v1.7.5 h1:XyL6hTLtEM/eE4F1GEge8xUN9ZCkiVWn44K/YA7z1rQ= -cloud.google.com/go/vpcaccess v1.7.5/go.mod h1:slc5ZRvvjP78c2dnL7m4l4R9GwL3wDLcpIWz6P/ziig= cloud.google.com/go/vpcaccess v1.7.11 h1:1XgRP+Q2X6MvE/xnexpQ7ydgav+IO5UcKUIJEbL65J8= cloud.google.com/go/vpcaccess v1.7.11/go.mod h1:a2cuAiSCI4TVK0Dt6/dRjf22qQvfY+podxst2VvAkcI= cloud.google.com/go/webrisk v1.8.0 h1:IY+L2+UwxcVm2zayMAtBhZleecdIFLiC+QJMzgb0kT0= cloud.google.com/go/webrisk v1.9.1 h1:Ssy3MkOMOnyRV5H2bkMQ13Umv7CwB/kugo3qkAX83Fk= -cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd/5uQIPBc= -cloud.google.com/go/webrisk v1.9.3/go.mod h1:RUYXe9X/wBDXhVilss7EDLW9ZNa06aowPuinUOPCXH8= -cloud.google.com/go/webrisk v1.9.4/go.mod h1:w7m4Ib4C+OseSr2GL66m0zMBywdrVNTDKsdEsfMl7X0= cloud.google.com/go/webrisk v1.9.5 h1:251MvGuC8wisNN7+jqu9DDDZAi38KiMXxOpA/EWy4dE= -cloud.google.com/go/webrisk v1.9.5/go.mod h1:aako0Fzep1Q714cPEM5E+mtYX8/jsfegAuS8aivxy3U= cloud.google.com/go/webrisk v1.9.11 h1:2qwEqnXrToIv2Y4xvsUSxCk7R2Ki+3W2+GNyrytoKTQ= cloud.google.com/go/webrisk v1.9.11/go.mod h1:mK6M8KEO0ZI7VkrjCq3Tjzw4vYq+3c4DzlMUDVaiswE= cloud.google.com/go/websecurityscanner v1.5.0 h1:AHC1xmaNMOZtNqxI9Rmm87IJEyPaRkOxeI0gpAacXGk= cloud.google.com/go/websecurityscanner v1.6.1 h1:CfEF/vZ+xXyAR3zC9iaC/QRdf1MEgS20r5UR17Q4gOg= -cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas= -cloud.google.com/go/websecurityscanner v1.6.3/go.mod h1:x9XANObUFR+83Cya3g/B9M/yoHVqzxPnFtgF8yYGAXw= -cloud.google.com/go/websecurityscanner v1.6.4/go.mod h1:mUiyMQ+dGpPPRkHgknIZeCzSHJ45+fY4F52nZFDHm2o= cloud.google.com/go/websecurityscanner v1.6.5 h1:YqWZrZYabG88TZt7364XWRJGhxmxhony2ZUyZEYMF2k= -cloud.google.com/go/websecurityscanner v1.6.5/go.mod h1:QR+DWaxAz2pWooylsBF854/Ijvuoa3FCyS1zBa1rAVQ= cloud.google.com/go/websecurityscanner v1.6.11 h1:r3ePI3YN7ujwX8c9gIkgbVjYVwP4yQA4X2z6P7+HNxI= cloud.google.com/go/websecurityscanner v1.6.11/go.mod h1:vhAZjksELSg58EZfUQ1BMExD+hxqpn0G0DuyCZQjiTg= cloud.google.com/go/workflows v1.10.0 h1:FfGp9w0cYnaKZJhUOMqCOJCYT/WlvYBfTQhFWV3sRKI= cloud.google.com/go/workflows v1.12.0 h1:cSUlx4PVV9O0vYCl+pHAUmu0996A7eN602d4wjjVHRs= -cloud.google.com/go/workflows v1.12.0/go.mod h1:PYhSk2b6DhZ508tj8HXKaBh+OFe+xdl0dHF/tJdzPQM= -cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM= -cloud.google.com/go/workflows v1.12.2/go.mod h1:+OmBIgNqYJPVggnMo9nqmizW0qEXHhmnAzK/CnBqsHc= -cloud.google.com/go/workflows v1.12.3/go.mod h1:fmOUeeqEwPzIU81foMjTRQIdwQHADi/vEr1cx9R1m5g= cloud.google.com/go/workflows v1.12.4 h1:uHNmUiatTbPQ4H1pabwfzpfEYD4BBnqDHqMm2IesOh4= -cloud.google.com/go/workflows v1.12.4/go.mod h1:yQ7HUqOkdJK4duVtMeBCAOPiN1ZF1E9pAMX51vpwB/w= cloud.google.com/go/workflows v1.12.10 h1:EGJeZmwgE71jxFOI5s9iKST2Bivif3DSzlqVbiXACXQ= cloud.google.com/go/workflows v1.12.10/go.mod h1:RcKqCiOmKs8wFUEf3EwWZPH5eHc7Oq0kamIyOUCk0IE= contrib.go.opencensus.io/exporter/aws v0.0.0-20230502192102-15967c811cec h1:CSNP8nIEQt4sZEo2sGUiWSmVJ9c5QdyIQvwzZAsn+8Y= @@ -1304,18 +711,13 @@ github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 h1:bLzehmpyCwQiqCE1Qe9Ny6fbF github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8= github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw= github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= -github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0= -github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU= github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= -github.com/alecthomas/participle/v2 v2.0.0/go.mod h1:rAKZdJldHu8084ojcWevWAL8KmEU+AT+Olodb+WoN2Y= github.com/alecthomas/participle/v2 v2.1.0 h1:z7dElHRrOEEq45F2TG5cbQihMtNTv8vwldytDj7Wrz4= -github.com/alecthomas/participle/v2 v2.1.0/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= -github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -1326,7 +728,6 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAu github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e h1:GCzyKMDDjSGnlpl3clrdAK7I1AaVoaiKDOYkUzChZzg= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= @@ -1338,16 +739,13 @@ github.com/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6 github.com/apache/arrow/go/v11 v11.0.0 h1:hqauxvFQxww+0mEU/2XHG6LT7eZternCZq+A5Yly2uM= github.com/apache/arrow/go/v12 v12.0.0 h1:xtZE63VWl7qLdB0JObIXvvhGjoVNrQ9ciIHG2OK5cmc= github.com/apache/arrow/go/v12 v12.0.1 h1:JsR2+hzYYjgSUkBSaahpqCetqZMr76djX80fF/DiJbg= -github.com/apache/arrow/go/v12 v12.0.1/go.mod h1:weuTY7JvTG/HDPtMQxEUp7pU73vkLWMLpY67QwZ/WWw= github.com/apache/arrow/go/v14 v14.0.2 h1:N8OkaJEOfI3mEZt07BIkvo4sC6XDbL+48MBPWO5IONw= -github.com/apache/arrow/go/v14 v14.0.2/go.mod h1:u3fgh3EdgN/YQ8cVQRguVW3R+seMybFg8QBQ5LU+eBY= github.com/apache/arrow/go/v15 v15.0.2 h1:60IliRbiyTWCWjERBCkO1W4Qun9svcYoZrSLcyOsMLE= github.com/apache/arrow/go/v15 v15.0.2/go.mod h1:DGXsR3ajT524njufqf95822i+KTh+yea1jass9YXgjA= github.com/apache/pulsar-client-go v0.13.1 h1:XAAKXjF99du7LP6qu/nBII1HC2nS483/vQoQIWmm5Yg= github.com/apache/pulsar-client-go v0.13.1/go.mod h1:0X5UCs+Cv5w6Ds38EZebUMfyVUFIh+URF2BeipEVhIU= github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= github.com/apache/thrift v0.17.0 h1:cMd2aj52n+8VoAtvSvLn4kDC3aZ6IAkBuqWQ2IDu7wo= -github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q= github.com/ardielle/ardielle-tools v1.5.4 h1:2uL/7wZRUF4LGV7r2eTaaeyhkBoqdiqEitSXMd6k8F8= github.com/argoproj/argo-workflows/v3 v3.4.7 h1:jtJtlaJArdgnHBxm3Ue6lt/mrzpPF99K3MMC/vY4TWg= github.com/argoproj/argo-workflows/v3 v3.4.7/go.mod h1:qI55HmnDc2pFNItTPGQnasF96MP9fOyLT/CCClK4tqg= @@ -1427,16 +825,13 @@ github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3I github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.2.0 h1:+eqR0HfOetur4tgnC8ftU5imRnhi4te+BadWS95c5AM= -github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= -github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20210722231415-061457976a23 h1:dZ0/VyGgQdVGAss6Ju0dt5P0QltE0SFY5Woh6hbIfiQ= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4= @@ -1449,10 +844,7 @@ github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nC github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed h1:OZmjad4L3H8ncOIR8rnb5MREYqG8ixi5+WbeUsquF0c= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195 h1:58f1tJ1ra+zFINPlwLWvQsR9CzAKt2e+EWV2yX9oXQ4= -github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= @@ -1536,6 +928,7 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1: github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/dgraph-io/ristretto/v2 v2.0.1 h1:7W0LfEP+USCmtrUjJsk+Jv2jbhJmb72N4yRI7GrLdMI= github.com/dgraph-io/ristretto/v2 v2.0.1/go.mod h1:K7caLeufSdxm+ITp1n/73U+VbFVAHrexfLbz4n14hpo= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= @@ -1578,14 +971,10 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0 h1:dulLQAYQFYtG5MTplgNGHWuV2D+OBD+Z8lmDBmbLg+s= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.11.0 h1:jtLewhRR2vMRNnq2ZZUoCjUlgut+Y0+sDDWPOfwOi1o= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.10.0 h1:oIfnZFdC0YhpNNEX+SuIqko4cqqVZeN9IGTrhZje83Y= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -1597,14 +986,10 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZM github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -1641,8 +1026,6 @@ github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNV github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= github.com/go-openapi/analysis v0.19.5 h1:8b2ZgKfKIUTVQpTb77MoRDIMEIwvDVw40o3aOXdfYzI= @@ -1658,13 +1041,9 @@ github.com/go-openapi/strfmt v0.19.5 h1:0utjKrw+BAh8s57XE9Xz8DUBsVvPmRUB6styvl9w github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= github.com/go-pdf/fpdf v0.6.0 h1:MlgtGIfsdMEEQJr2le6b/HNr1ZlQwxyWr77r2aj2U/8= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= @@ -1685,10 +1064,7 @@ github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE= github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54= -github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng= github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -1702,7 +1078,6 @@ github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= @@ -1724,13 +1099,10 @@ github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u github.com/google/cel-spec v0.6.0 h1:xuthJSiJGoSzq+lVEBIW1MTpaaZXknMCYC4WzVAWOsE= github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg= -github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-containerregistry v0.14.0 h1:z58vMqHxuwvAsVwvKEkmVBz2TlgBgH5k6koEXBtlYkw= github.com/google/go-containerregistry v0.14.0/go.mod h1:aiJ2fp/SXvkWgmYHioXnbMdlgB8eXiiYOY55gfN91Wk= github.com/google/go-pkcs11 v0.2.0 h1:5meDPB26aJ98f+K9G21f0AqZwo/S5BJMJh8nuhMbdsI= -github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9 h1:OF1IPgv+F4NmqmJ98KTjdN97Vs1JxDPB3vbmYzV2dpk= -github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -1738,25 +1110,17 @@ github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/ github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/cloud-bigtable-clients-test v0.0.2 h1:S+sCHWAiAc+urcEnvg5JYJUOdlQEm/SEzQ/c/IdAH5M= github.com/googleapis/cloud-bigtable-clients-test v0.0.2/go.mod h1:mk3CrkrouRgtnhID6UZQDK3DrFFa7cYCAJcEmNsHYrY= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= -github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/gax-go/v2 v2.12.1/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI= @@ -1784,7 +1148,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY= @@ -1819,13 +1182,10 @@ github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/r github.com/heptiolabs/healthcheck v0.0.0-20180807145615-6ff867650f40 h1:GT4RsKmHh1uZyhmTkWJTDALRjSHYQp6FRKrotf0zhAs= github.com/heptiolabs/healthcheck v0.0.0-20180807145615-6ff867650f40/go.mod h1:NtmN9h8vrTveVQRLHcX2HQ5wIPBDCsZ351TGbZWgg38= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= -github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= -github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -1861,11 +1221,8 @@ github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHz github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= @@ -1879,17 +1236,20 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtB github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4= github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80= github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= +github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= +github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A= github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= @@ -1900,8 +1260,6 @@ github.com/lestrrat-go/jwx/v2 v2.0.11 h1:ViHMnaMeaO0qV16RZWBHM7GTrAnX2aFLVKofc7F github.com/lestrrat-go/jwx/v2 v2.0.11/go.mod h1:ZtPtMFlrfDrH2Y0iwfa3dRFn8VzwBrB+cyrm3IBWdDg= github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= -github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= @@ -1920,16 +1278,11 @@ github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= @@ -1937,7 +1290,6 @@ github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lL github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw= github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= -github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -2044,7 +1396,6 @@ github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHf github.com/phpdave11/gofpdi v1.0.13 h1:o61duiW8M9sMlkVXWlvP92sZJtGKENvW3VExs6dZukQ= github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= -github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc= @@ -2106,8 +1457,6 @@ github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIH github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sebest/xff v0.0.0-20160910043805-6c115e0ffa35 h1:eajwn6K3weW5cd1ZXLu2sJ4pvwlBiCWY4uDejOr73gM= github.com/sebest/xff v0.0.0-20160910043805-6c115e0ffa35/go.mod h1:wozgYq9WEBQBaIJe4YZ0qTSFAMxmcwBhQH0fO0R34Z0= -github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= -github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= @@ -2125,7 +1474,6 @@ github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= @@ -2143,7 +1491,6 @@ github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= -github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/streamnative/function-mesh v0.21.6 h1:rhoV8Wkeq3kcIeTdVqUqO1z7hiUhswu0/1tuhpAxja0= github.com/streamnative/function-mesh v0.21.6/go.mod h1:ygi3CUABkOeAAu3OPVthLkwafXhXfE/L9wxiFQWqebI= github.com/streamnative/kube-instrumentation v0.3.2 h1:B1jQ2IdJlPtBXXdOfZaTqMB0dwi/lqXdKaZIanoHJ6U= @@ -2172,10 +1519,8 @@ github.com/streamnative/unified-rbac/sdk/sdk-go-cloud v0.5.0/go.mod h1:J+9wYAVAY github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/substrait-io/substrait-go v0.4.2 h1:buDnjsb3qAqTaNbOR7VKmNgXf4lYQxWEcnSGUWBtmN8= -github.com/substrait-io/substrait-go v0.4.2/go.mod h1:qhpnLmrcvAnlZsUyPXZRqldiHapPTXC3t7xFgDi3aQg= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= @@ -2216,7 +1561,6 @@ github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= go.einride.tech/aip v0.66.0 h1:XfV+NQX6L7EOYK11yoHHFtndeaWh3KbD9/cN/6iWEt8= -go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= go.einride.tech/aip v0.67.1 h1:d/4TW92OxXBngkSOwWS2CH5rez869KpKMaN44mdxkFI= go.einride.tech/aip v0.67.1/go.mod h1:ZGX4/zKw8dcgzdLsrvpOOGxfxI2QSk12SlP7d6c0/XI= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= @@ -2353,7 +1697,6 @@ go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzc go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -2372,12 +1715,6 @@ go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= @@ -2387,9 +1724,7 @@ golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/ golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= -golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= @@ -2397,8 +1732,6 @@ golang.org/x/image v0.0.0-20220302094943-723b81ca9867 h1:TcHcE0vrmgzNH1v3ppjcMGb golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= @@ -2408,11 +1741,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= @@ -2424,17 +1752,10 @@ golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -2451,18 +1772,10 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -2473,26 +1786,18 @@ golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/I golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= @@ -2507,24 +1812,14 @@ golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o= -gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/plot v0.10.1 h1:dnifSs43YJuNMDzB7v8wV64O4ABBHReuAVAoBxqBqS4= google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA= google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= -google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= google.golang.org/api v0.139.0 h1:A1TrCPgMmOiYu0AiNkvQIpIx+D8blHTDcJ5EogkP7LI= -google.golang.org/api v0.139.0/go.mod h1:CVagp6Eekz9CjGZ718Z+sloknzkDJE7Vc1Ckj9+viBk= -google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= -google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= -google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= -google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= -google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps= -google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= google.golang.org/api v0.164.0/go.mod h1:2OatzO7ZDQsoS7IFf3rvsE17/TldiU3F/zxFHeqUB5o= google.golang.org/api v0.166.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA= google.golang.org/api v0.177.0/go.mod h1:srbhue4MLjkjbkux5p3dw/ocYOSZTaIEvf7bCOnFQDw= @@ -2534,61 +1829,20 @@ google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9 google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 h1:NHN4wOCScVzKhPenJ2dt+BTs3X/XkBVI/Rh4iDt55T8= google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= -google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= -google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9RmpZ1mxVr8MUjCnSiY09IbAXZxhLE6EhHIdPU= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a h1:fwgW9j3vHirt4ObdHoYNwuO24BEZjSzbh+zPaNWoiY8= -google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= -google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= -google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY= -google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= -google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= -google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= -google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= google.golang.org/genproto v0.0.0-20240528184218-531527333157/go.mod h1:ubQlAQnzejB8uZzszhrTCU2Fyp6Vi7ZE5nn0c3W8+qQ= google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:s7iA721uChleev562UJO2OYB0PPT9CMFjV+Ce7VJH5M= google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:mCr1K1c8kX+1iSBREvU3Juo11CB+QOEWxbRS01wWl5M= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw= -google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= -google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= -google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405/go.mod h1:oT32Z4o8Zv2xPQTg0pbVaPr0MPOH6f14RgXt7zfIpwg= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= -google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go.mod h1:k2dtGpRrbsSyKcNPKKI5sstZkrNCZwpU/ns96JoHbGg= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= -google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= -google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= -google.golang.org/genproto/googleapis/api v0.0.0-20240122161410-6c6643bf1457/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I= -google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:PVreiBMirk8ypES6aw9d4p6iiBNSIfZEBqr3UGoAi2E= google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= google.golang.org/genproto/googleapis/api v0.0.0-20240429193739-8cf5692501f6/go.mod h1:10yRODfgim2/T8csjQsMPgZOMvtytXKTDRzH6HRGzRw= @@ -2602,40 +1856,15 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go. google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:OFMYQFHJ4TM3JRlWDZhJbZfra2uqc3WLBZiaaqP4DtU= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc h1:g3hIDl0jRNd9PPTs2uBzYuaD5mQuwOkZY0vSc0LR32o= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577 h1:ZX0eQu2J+jOO87sq8fQG8J/Nfp7D7BhHpixIE5EYK/k= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:qDbnxtViX5J6CvFbxeNUSzKgVlDLJ/6L+caxye9+Flo= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20231212172506-995d672761c0/go.mod h1:guYXGPwC6jwxgWKW5Y405fKWOFNwlvUlUnzyp9i0uqo= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:ZSvZ8l+AWJwXw91DoTjWjaVLpWU6o0eZ4YLYpH8aLeQ= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240125205218-1f4bbc51befe h1:weYsP+dNijSQVoLAb5bpUos3ciBpNU/NEVlHFKrk8pg= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:SCz6T5xjNXM4QFPRwxHcfChp7V+9DcXR3ay2TkHR8Tg= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240304161311-37d4d3c04a78 h1:YqFWYZXim8bG9v68xU8WjTZmYKb5M5dMeSOWIp6jogI= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:vh/N7795ftP0AkN1w8XKqN4w1OdUKXW5Eummda+ofv8= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240730163845-b1a4ccb954bf h1:T4tsZBlZYXK3j40sQNP5MBO32I+rn6ypV1PpklsiV8k= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:5/MT647Cn/GGhwTpXC7QqcaR5Cnee4v4MKCU1/nwnIQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920183334-c177e329c48b/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240122161410-6c6643bf1457/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk= google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= @@ -2674,12 +1903,8 @@ google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFL google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 h1:FVCohIoYO7IJoDDVpV2pdq7SgrMH6wHnuTyrdrxJNoY= @@ -2706,7 +1931,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= helm.sh/helm/v3 v3.7.1 h1:kED/HWx09QHHSJhYaJY6ttj/BhmzBmT1oupKslncibY= helm.sh/helm/v3 v3.7.1/go.mod h1:3eOeBD3Z+O/ELiuu19zynZSN8jP1ErXLuyP21SZeMq8= honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= @@ -2795,21 +2019,15 @@ k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrD k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= -lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg= -modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= -modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0= modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM= modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/libc v1.17.1 h1:Q8/Cpi36V/QBfuQaFVeisEBs3WqoGAJprZzmf7TfEYI= -modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= -modernc.org/libc v1.21.2/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= modernc.org/libc v1.22.4 h1:wymSbZb0AlrjdAVX3cjreCHTPCpPARbQXNz6BHPzdwQ= -modernc.org/libc v1.22.4/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/memory v1.2.1 h1:dkRh86wgmq/bJu2cAS2oqBCz/KsMZU7TUM4CibQ7eBs= modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= @@ -2817,17 +2035,14 @@ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/sqlite v1.18.1 h1:ko32eKt3jf7eqIkCgPAeHMBXw3riNSLhl2f3loEF7o8= modernc.org/sqlite v1.18.2 h1:S2uFiaNPd/vTAP/4EmyY8Qe2Quzu26A2L1e25xRNTio= modernc.org/sqlite v1.21.2 h1:ixuUG0QS413Vfzyx6FWx6PYTmHaOegTY+hjzhn7L+a0= -modernc.org/sqlite v1.21.2/go.mod h1:cxbLkB5WS32DnQqeH4h4o1B0eMr8W/y8/RGuxQ3JsC0= modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao= modernc.org/tcl v1.13.2 h1:5PQgL/29XkQ9wsEmmNPjzKs+7iPCaYqUJAhzPvQbjDA= modernc.org/tcl v1.15.1 h1:mOQwiEK4p7HruMZcwKTZPw/aqtGM4aY00uzWhlKKYws= -modernc.org/tcl v1.15.1/go.mod h1:aEjeGJX2gz1oWKOLDVZ2tnEWLUrIn8H+GFu+akoDhqs= modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM= modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= -modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= oras.land/oras-go v0.4.0 h1:u6+7D+raZDYHwlz/uOwNANiRmyYDSSMW7A9E1xXycUQ= oras.land/oras-go v0.4.0/go.mod h1:VJcU+VE4rkclUbum5C0O7deEZbBYnsnpbGSACwTjOcg= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= diff --git a/main.go b/main.go index ba87ae2e..f0d7cb5a 100644 --- a/main.go +++ b/main.go @@ -170,6 +170,37 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Secret") os.Exit(1) } + + // Set up ServiceAccount controller + if err = (&controllers.ServiceAccountReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + ConnectionManager: connectionManager, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ServiceAccount") + os.Exit(1) + } + + // Set up ServiceAccountBinding controller + if err = (&controllers.ServiceAccountBindingReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + ConnectionManager: connectionManager, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ServiceAccountBinding") + os.Exit(1) + } + + // Set up APIKey controller + if err = (&controllers.APIKeyReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + ConnectionManager: connectionManager, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "APIKey") + os.Exit(1) + } + //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/pkg/crypto/jwe.go b/pkg/crypto/jwe.go new file mode 100644 index 00000000..d87b71a5 --- /dev/null +++ b/pkg/crypto/jwe.go @@ -0,0 +1,45 @@ +// Copyright 2025 StreamNative +// +// 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 crypto + +import ( + "crypto/rsa" + "fmt" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwe" +) + +// EncryptTokenToJWE encrypts a token using JWE with the provided public key +func EncryptTokenToJWE(token string, publicKey *rsa.PublicKey) (string, error) { + // Encrypt the token using RSA-OAEP for key encryption and A256GCM for content encryption + encrypted, err := jwe.Encrypt([]byte(token), jwe.WithKey(jwa.RSA_OAEP, publicKey)) + if err != nil { + return "", fmt.Errorf("failed to encrypt token: %w", err) + } + + return string(encrypted), nil +} + +// DecryptJWEToken decrypts a JWE token using the provided private key +func DecryptJWEToken(jweToken string, privateKey *rsa.PrivateKey) (string, error) { + // Decrypt the JWE token + decrypted, err := jwe.Decrypt([]byte(jweToken), jwe.WithKey(jwa.RSA_OAEP, privateKey)) + if err != nil { + return "", fmt.Errorf("failed to decrypt token: %w", err) + } + + return string(decrypted), nil +} diff --git a/pkg/crypto/rsa.go b/pkg/crypto/rsa.go new file mode 100644 index 00000000..48f4f03b --- /dev/null +++ b/pkg/crypto/rsa.go @@ -0,0 +1,91 @@ +// Copyright 2025 StreamNative +// +// 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 crypto + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" +) + +// GenerateRSAKeyPair generates a new RSA private and public key pair +func GenerateRSAKeyPair() (*rsa.PrivateKey, error) { + // Generate a 2048-bit RSA key + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, fmt.Errorf("failed to generate RSA key pair: %w", err) + } + return privateKey, nil +} + +// ExportPublicKeyAsPEM exports the public key in PEM format +func ExportPublicKeyAsPEM(privateKey *rsa.PrivateKey) (string, error) { + publicKey := &privateKey.PublicKey + publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { + return "", fmt.Errorf("failed to marshal public key: %w", err) + } + + publicKeyBlock := &pem.Block{ + Type: "RSA PUBLIC KEY", + Bytes: publicKeyBytes, + } + + publicKeyPEM := pem.EncodeToMemory(publicKeyBlock) + return string(publicKeyPEM), nil +} + +// ExportPrivateKeyAsPEM exports the private key in PEM format +func ExportPrivateKeyAsPEM(privateKey *rsa.PrivateKey) string { + privKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey) + privKeyBlock := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: privKeyBytes, + } + privKeyPEM := pem.EncodeToMemory(privKeyBlock) + return string(privKeyPEM) +} + +// ImportPrivateKeyFromPEM imports a private key from PEM format +func ImportPrivateKeyFromPEM(privateKeyPEM string) (*rsa.PrivateKey, error) { + block, _ := pem.Decode([]byte(privateKeyPEM)) + if block == nil { + return nil, fmt.Errorf("failed to parse PEM block containing the private key") + } + + privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("failed to parse private key: %w", err) + } + + return privateKey, nil +} + +// EncodeToBase64 encodes a string to base64 +func EncodeToBase64(data string) string { + return base64.StdEncoding.EncodeToString([]byte(data)) +} + +// DecodeFromBase64 decodes a base64 string +func DecodeFromBase64(data string) (string, error) { + decoded, err := base64.StdEncoding.DecodeString(data) + if err != nil { + return "", fmt.Errorf("failed to decode base64 string: %w", err) + } + return string(decoded), nil +} diff --git a/pkg/streamnativecloud/apikey_client.go b/pkg/streamnativecloud/apikey_client.go index 1c40e2c0..7ff81dc5 100644 --- a/pkg/streamnativecloud/apikey_client.go +++ b/pkg/streamnativecloud/apikey_client.go @@ -16,6 +16,10 @@ package streamnativecloud import ( "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -98,15 +102,9 @@ func convertToCloudAPIKey(apiKey *resourcev1alpha1.APIKey) *cloudapi.APIKey { if apiKey.Spec.ExpirationTime != nil { cloudAPIKey.Spec.ExpirationTime = apiKey.Spec.ExpirationTime } - cloudAPIKey.Spec.Revoke = apiKey.Spec.Revoke - - // Copy encryption key if provided - if apiKey.Spec.EncryptionKey != nil { - cloudAPIKey.Spec.EncryptionKey = &cloudapi.EncryptionKey{ - PEM: apiKey.Spec.EncryptionKey.PEM, - JWK: apiKey.Spec.EncryptionKey.JWK, - } - } + + privateKey := GenerateEncryptionKey() + cloudAPIKey.Spec.EncryptionKey = ExportPublicKey(privateKey) return cloudAPIKey } @@ -140,3 +138,25 @@ func (c *APIKeyClient) UpdateAPIKey(ctx context.Context, apiKey *resourcev1alpha func (c *APIKeyClient) DeleteAPIKey(ctx context.Context, apiKey *resourcev1alpha1.APIKey) error { return c.client.CloudV1alpha1().APIKeys(c.organization).Delete(ctx, apiKey.Name, metav1.DeleteOptions{}) } + +func GenerateEncryptionKey() *rsa.PrivateKey { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + panic(err) + } + return privateKey +} + +func ExportPublicKey(key *rsa.PrivateKey) *cloudapi.EncryptionKey { + der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) + if err != nil { + panic(err) + } + pemKey := pem.EncodeToMemory(&pem.Block{ + Type: "PUBLIC KEY", + Bytes: der, + }) + return &cloudapi.EncryptionKey{ + PEM: string(pemKey), + } +} diff --git a/pkg/streamnativecloud/serviceaccountbinding_client.go b/pkg/streamnativecloud/serviceaccountbinding_client.go index dae641be..9b84e200 100644 --- a/pkg/streamnativecloud/serviceaccountbinding_client.go +++ b/pkg/streamnativecloud/serviceaccountbinding_client.go @@ -76,7 +76,7 @@ func (c *ServiceAccountBindingClient) GetServiceAccountBinding(ctx context.Conte } // convertToCloudServiceAccountBinding converts a local ServiceAccountBinding to a cloud ServiceAccountBinding -func convertToCloudServiceAccountBinding(binding *resourcev1alpha1.ServiceAccountBinding) *cloudapi.ServiceAccountBinding { +func convertToCloudServiceAccountBinding(binding *resourcev1alpha1.ServiceAccountBinding, organization string) *cloudapi.ServiceAccountBinding { // Convert to cloud API type cloudBinding := &cloudapi.ServiceAccountBinding{ ObjectMeta: metav1.ObjectMeta{ @@ -96,12 +96,16 @@ func convertToCloudServiceAccountBinding(binding *resourcev1alpha1.ServiceAccoun Name: binding.Spec.PoolMemberRef.Name, } + if binding.Spec.PoolMemberRef.Namespace == "" { + cloudBinding.Spec.PoolMemberRef.Namespace = organization + } + return cloudBinding } // CreateServiceAccountBinding creates a new ServiceAccountBinding func (c *ServiceAccountBindingClient) CreateServiceAccountBinding(ctx context.Context, binding *resourcev1alpha1.ServiceAccountBinding) (*cloudapi.ServiceAccountBinding, error) { - cloudBinding := convertToCloudServiceAccountBinding(binding) + cloudBinding := convertToCloudServiceAccountBinding(binding, c.organization) // Create ServiceAccountBinding return c.client.CloudV1alpha1().ServiceAccountBindings(c.organization).Create(ctx, cloudBinding, metav1.CreateOptions{}) @@ -116,7 +120,7 @@ func (c *ServiceAccountBindingClient) UpdateServiceAccountBinding(ctx context.Co } // Create updated version - updated := convertToCloudServiceAccountBinding(binding) + updated := convertToCloudServiceAccountBinding(binding, c.organization) // Preserve ResourceVersion for optimistic concurrency updated.ResourceVersion = existing.ResourceVersion diff --git a/pkg/utils/secrets.go b/pkg/utils/secrets.go new file mode 100644 index 00000000..a6fc4e25 --- /dev/null +++ b/pkg/utils/secrets.go @@ -0,0 +1,162 @@ +// Copyright 2025 StreamNative +// +// 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 utils + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +const ( + // PrivateKeySecretSuffix is the suffix for private key secrets + PrivateKeySecretSuffix = "-private-key" + // TokenSecretSuffix is the suffix for token secrets + TokenSecretSuffix = "-token" + // PrivateKeyField is the key for the private key in the secret + PrivateKeyField = "private-key" + // TokenField is the key for the token in the secret + TokenField = "token" + // ServiceAccountCredentialsSecretSuffix is the suffix for service account credentials secrets + ServiceAccountCredentialsSecretSuffix = "-credentials" + // ServiceAccountCredentialsField is the key for the service account credentials in the secret + ServiceAccountCredentialsField = "credentials.json" + // ServiceAccountCredentialsType is the type for service account credentials + ServiceAccountCredentialsType = "TYPE_SN_CREDENTIALS_FILE" +) + +// CreateOrUpdatePrivateKeySecret creates or updates a secret containing a private key +func CreateOrUpdatePrivateKeySecret(ctx context.Context, c client.Client, owner metav1.Object, namespace, name, privateKey string) error { + secretName := name + PrivateKeySecretSuffix + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + }, + } + + _, err := controllerutil.CreateOrUpdate(ctx, c, secret, func() error { + if secret.Data == nil { + secret.Data = make(map[string][]byte) + } + secret.Data[PrivateKeyField] = []byte(privateKey) + secret.Type = corev1.SecretTypeOpaque + + // Set owner reference + return controllerutil.SetControllerReference(owner, secret, c.Scheme()) + }) + + if err != nil { + return fmt.Errorf("failed to create or update private key secret: %w", err) + } + + return nil +} + +// CreateOrUpdateTokenSecret creates or updates a secret containing a token +func CreateOrUpdateTokenSecret(ctx context.Context, c client.Client, owner metav1.Object, namespace, name, token, keyID string) error { + secretName := name + TokenSecretSuffix + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + }, + } + + _, err := controllerutil.CreateOrUpdate(ctx, c, secret, func() error { + if secret.Data == nil { + secret.Data = make(map[string][]byte) + } + secret.Data[TokenField] = []byte(token) + secret.Type = corev1.SecretTypeOpaque + + // Add labels + if secret.Labels == nil { + secret.Labels = make(map[string]string) + } + secret.Labels["resources.streamnative.io/apikey"] = name + if keyID != "" { + secret.Labels["resources.streamnative.io/key-id"] = keyID + } + + // Set owner reference + return controllerutil.SetControllerReference(owner, secret, c.Scheme()) + }) + + if err != nil { + return fmt.Errorf("failed to create or update token secret: %w", err) + } + + return nil +} + +// CreateOrUpdateServiceAccountCredentialsSecret creates or updates a secret containing service account credentials +func CreateOrUpdateServiceAccountCredentialsSecret(ctx context.Context, c client.Client, owner metav1.Object, namespace, name, credentials string) error { + secretName := name + ServiceAccountCredentialsSecretSuffix + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + }, + } + + _, err := controllerutil.CreateOrUpdate(ctx, c, secret, func() error { + if secret.Data == nil { + secret.Data = make(map[string][]byte) + } + secret.Data[ServiceAccountCredentialsField] = []byte(credentials) + secret.Type = corev1.SecretTypeOpaque + + // Add labels + if secret.Labels == nil { + secret.Labels = make(map[string]string) + } + secret.Labels["resources.streamnative.io/serviceaccount"] = name + + // Set owner reference + return controllerutil.SetControllerReference(owner, secret, c.Scheme()) + }) + + if err != nil { + return fmt.Errorf("failed to create or update service account credentials secret: %w", err) + } + + return nil +} + +// GetPrivateKeyFromSecret retrieves the private key from a secret +func GetPrivateKeyFromSecret(ctx context.Context, c client.Client, namespace, name string) (string, error) { + secretName := name + PrivateKeySecretSuffix + var secret corev1.Secret + if err := c.Get(ctx, types.NamespacedName{Namespace: namespace, Name: secretName}, &secret); err != nil { + if errors.IsNotFound(err) { + return "", fmt.Errorf("private key secret not found: %s", secretName) + } + return "", fmt.Errorf("failed to get private key secret: %w", err) + } + + privateKey, ok := secret.Data[PrivateKeyField] + if !ok { + return "", fmt.Errorf("private key not found in secret: %s", secretName) + } + + return string(privateKey), nil +} From 5064fd3c406c02717174b0d54a6c6b25a08fad3d Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Tue, 15 Apr 2025 14:23:28 +0800 Subject: [PATCH 4/6] make apikey and serviceaccount exposes secrets --- README.md | 3 + api/v1alpha1/serviceaccountbinding_types.go | 10 +- api/v1alpha1/zz_generated.deepcopy.go | 13 +- .../resource.streamnative.io_apikeys.yaml | 16 +- ...treamnative.io_serviceaccountbindings.yaml | 40 +++-- ...ource.streamnative.io_serviceaccounts.yaml | 8 - config/crd/kustomization.yaml | 3 + controllers/apikey_controller.go | 100 ++++++++++++- .../serviceaccountbinding_controller.go | 137 ++++++++++++------ docs/serviceaccountbinding.md | 49 +++++-- go.mod | 10 +- go.sum | 10 ++ go.work.sum | 11 +- .../serviceaccountbinding_client.go | 18 ++- 14 files changed, 321 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 4554e751..fb82b0cd 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,9 @@ In this tutorial, a Kubernetes namespace called `test` is used for examples, whi - [ComputeWorkspace](docs/compute_workspace.md) - [ComputeFlinkDeployment](docs/compute_flink_deployment.md) - [StreamNative Cloud Secret](docs/secret.md) +- [StreamNative Cloud APIKey](docs/apikey.md) +- [StreamNative Cloud ServiceAccount](docs/serviceaccount.md) +- [StreamNative Cloud ServiceAccountBinding](docs/serviceaccountbinding.md) # Contributing diff --git a/api/v1alpha1/serviceaccountbinding_types.go b/api/v1alpha1/serviceaccountbinding_types.go index 3eca30ee..49080d0c 100644 --- a/api/v1alpha1/serviceaccountbinding_types.go +++ b/api/v1alpha1/serviceaccountbinding_types.go @@ -15,6 +15,7 @@ package v1alpha1 import ( + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -24,9 +25,14 @@ type ServiceAccountBindingSpec struct { // +required ServiceAccountName string `json:"serviceAccountName"` - // PoolMemberRef refers to a PoolMember in the current namespace or other namespaces + // APIServerRef is the reference to the StreamNativeCloudConnection + // If not provided, it will be retrieved from the referenced ServiceAccount + // +optional + APIServerRef *corev1.LocalObjectReference `json:"apiServerRef,omitempty"` + + // PoolMemberRefs refers to a list of PoolMembers in the current namespace or other namespaces // +required - PoolMemberRef PoolMemberReference `json:"poolMemberRef"` + PoolMemberRefs []PoolMemberReference `json:"poolMemberRefs"` } // ServiceAccountBindingStatus defines the observed state of ServiceAccountBinding diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index c47961af..174f40bc 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -2876,7 +2876,7 @@ func (in *ServiceAccountBinding) DeepCopyInto(out *ServiceAccountBinding) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) } @@ -2933,7 +2933,16 @@ func (in *ServiceAccountBindingList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceAccountBindingSpec) DeepCopyInto(out *ServiceAccountBindingSpec) { *out = *in - out.PoolMemberRef = in.PoolMemberRef + if in.APIServerRef != nil { + in, out := &in.APIServerRef, &out.APIServerRef + *out = new(corev1.LocalObjectReference) + **out = **in + } + if in.PoolMemberRefs != nil { + in, out := &in.PoolMemberRefs, &out.PoolMemberRefs + *out = make([]PoolMemberReference, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountBindingSpec. diff --git a/config/crd/bases/resource.streamnative.io_apikeys.yaml b/config/crd/bases/resource.streamnative.io_apikeys.yaml index 67e740bb..90ec81fe 100644 --- a/config/crd/bases/resource.streamnative.io_apikeys.yaml +++ b/config/crd/bases/resource.streamnative.io_apikeys.yaml @@ -83,18 +83,14 @@ spec: description: Description is a user defined description of the key type: string encryptionKey: - description: |- - EncryptionKey is a public key to encrypt the API Key token - Please provide an RSA key with modulus length of at least 2048 bits + description: EncryptionKey contains the public key used to encrypt + the token properties: - jwk: - description: JWK is a JWK-encoded public key - type: string pem: - description: PEM is a PEM-encoded public key in PKIX, ASN.1 DER - form ("spki" format) + description: PEM is the public key in PEM format type: string type: object + x-kubernetes-map-type: atomic expirationTime: description: |- ExpirationTime is a timestamp that defines when this API key will expire @@ -106,9 +102,7 @@ spec: is for type: string revoke: - default: false - description: Revoke is a boolean that defines if the token of this - API key should be revoked + description: Revoke indicates whether this API key should be revoked type: boolean serviceAccountName: description: ServiceAccountName is the name of the service account diff --git a/config/crd/bases/resource.streamnative.io_serviceaccountbindings.yaml b/config/crd/bases/resource.streamnative.io_serviceaccountbindings.yaml index 4582ee47..b6b52f41 100644 --- a/config/crd/bases/resource.streamnative.io_serviceaccountbindings.yaml +++ b/config/crd/bases/resource.streamnative.io_serviceaccountbindings.yaml @@ -64,24 +64,46 @@ spec: spec: description: ServiceAccountBindingSpec defines the desired state of ServiceAccountBinding properties: - poolMemberRef: - description: PoolMemberRef refers to a PoolMember in the current namespace - or other namespaces + apiServerRef: + description: |- + APIServerRef is the reference to the StreamNativeCloudConnection + If not provided, it will be retrieved from the referenced ServiceAccount properties: name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. type: string - namespace: - type: string - required: - - name - - namespace type: object + x-kubernetes-map-type: atomic + poolMemberRefs: + description: PoolMemberRefs refers to a list of PoolMembers in the + current namespace or other namespaces + items: + description: PoolMemberReference is a reference to a pool member + with a given name. + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array serviceAccountName: description: ServiceAccountName refers to the ServiceAccount under the same namespace as this binding object type: string required: - - poolMemberRef + - poolMemberRefs - serviceAccountName type: object status: diff --git a/config/crd/bases/resource.streamnative.io_serviceaccounts.yaml b/config/crd/bases/resource.streamnative.io_serviceaccounts.yaml index a58232e7..c31df86e 100644 --- a/config/crd/bases/resource.streamnative.io_serviceaccounts.yaml +++ b/config/crd/bases/resource.streamnative.io_serviceaccounts.yaml @@ -79,14 +79,6 @@ spec: type: string type: object x-kubernetes-map-type: atomic - description: - description: Description is a user defined description of the service - account - type: string - instanceName: - description: InstanceName is the name of the instance this service - account is for - type: string required: - apiServerRef type: object diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index b67a5656..7007fbee 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -31,6 +31,9 @@ resources: - bases/resource.streamnative.io_computeworkspaces.yaml - bases/resource.streamnative.io_computeflinkdeployments.yaml - bases/resource.streamnative.io_secrets.yaml +- bases/resource.streamnative.io_serviceaccounts.yaml +- bases/resource.streamnative.io_serviceaccountbindings.yaml +- bases/resource.streamnative.io_apikeys.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: diff --git a/controllers/apikey_controller.go b/controllers/apikey_controller.go index af803037..eb3815cf 100644 --- a/controllers/apikey_controller.go +++ b/controllers/apikey_controller.go @@ -80,6 +80,10 @@ func (r *APIKeyReconciler) handleWatchEvents(ctx context.Context, namespacedName return } + logger.Info("Received watch event", + "apiKey", namespacedName.Name, + "eventType", event.Type) + if event.Type == watch.Modified { // Check if the object is an APIKey cloudAPIKey, ok := event.Object.(*cloudapi.APIKey) @@ -88,6 +92,14 @@ func (r *APIKeyReconciler) handleWatchEvents(ctx context.Context, namespacedName continue } + // Log token and encryption status + hasEncryptedToken := cloudAPIKey.Status.EncryptedToken != nil + hasJWE := hasEncryptedToken && cloudAPIKey.Status.EncryptedToken.JWE != nil + logger.Info("Received APIKey update", + "apiKey", namespacedName.Name, + "hasEncryptedToken", hasEncryptedToken, + "hasJWE", hasJWE) + // Get the local APIKey localAPIKey := &resourcev1alpha1.APIKey{} if err := r.Get(ctx, namespacedName, localAPIKey); err != nil { @@ -95,25 +107,81 @@ func (r *APIKeyReconciler) handleWatchEvents(ctx context.Context, namespacedName continue } - // Update status - r.updateAPIKeyStatus(ctx, localAPIKey, nil, "Ready", "APIKey synced successfully") + // Check remote status and handle abnormal situations + if isRemoteStatusAbnormal(cloudAPIKey) { + logger.Info("Detected abnormal remote APIKey status", + "apiKey", namespacedName.Name, + "hasEncryptedToken", hasEncryptedToken, + "hasJWE", hasJWE) + r.updateAPIKeyStatus(ctx, localAPIKey, + fmt.Errorf("remote API server returned incomplete or invalid status"), + "RemoteStatusAbnormal", + "Remote API server returned incomplete or invalid status for the APIKey") + continue + } // Process encrypted token and update Secret if needed if cloudAPIKey.Status.EncryptedToken != nil && cloudAPIKey.Status.EncryptedToken.JWE != nil { + logger.Info("Found encrypted token in watch event, processing", "apiKey", namespacedName.Name) r.processEncryptedToken(ctx, localAPIKey, cloudAPIKey) + } else { + logger.Info("No encrypted token found in watch event", "apiKey", namespacedName.Name) + // Update status to reflect that we're waiting for token + r.updateAPIKeyStatus(ctx, localAPIKey, nil, "WaitingForToken", + "Waiting for encrypted token from remote API server") } } } } } +// isRemoteStatusAbnormal checks if the remote API key status is incomplete or invalid +func isRemoteStatusAbnormal(cloudAPIKey *cloudapi.APIKey) bool { + // Consider status abnormal if: + // 1. Status is completely empty/nil (very unlikely but possible) + if cloudAPIKey == nil { + return true + } + + // 2. We expect some basic fields to be present in a normal response + // KeyID should be present in a successful API key + if cloudAPIKey.Status.KeyID == nil { + return true + } + + // 3. For an APIKey that's been processed by the server, we should have either: + // - An encrypted token + // - Or explicit conditions indicating why token creation failed + if cloudAPIKey.Status.EncryptedToken == nil && len(cloudAPIKey.Status.Conditions) == 0 { + return true + } + + // 4. If there are conditions, check if they indicate failure + for _, condition := range cloudAPIKey.Status.Conditions { + if condition.Type == "Ready" && condition.Status == metav1.ConditionFalse { + return true + } + } + + return false +} + // processEncryptedToken handles decrypting the token and storing it in a Secret func (r *APIKeyReconciler) processEncryptedToken(ctx context.Context, localAPIKey *resourcev1alpha1.APIKey, cloudAPIKey *cloudapi.APIKey) { logger := log.FromContext(ctx) + logger.Info("Processing encrypted token", + "apiKey", localAPIKey.Name, + "hasEncryptionKey", localAPIKey.Spec.EncryptionKey != nil, + "hasJWE", cloudAPIKey.Status.EncryptedToken != nil && cloudAPIKey.Status.EncryptedToken.JWE != nil) + // Skip if we don't have an encryption key in our local APIKey if localAPIKey.Spec.EncryptionKey == nil || localAPIKey.Spec.EncryptionKey.PEM == "" { logger.Info("No encryption key found in local APIKey, skipping token processing") + r.updateAPIKeyStatus(ctx, localAPIKey, + fmt.Errorf("missing encryption key in local APIKey"), + "MissingEncryptionKey", + "Cannot decrypt token: no encryption key found in local APIKey") return } @@ -121,23 +189,39 @@ func (r *APIKeyReconciler) processEncryptedToken(ctx context.Context, localAPIKe privateKeyStr, err := utils.GetPrivateKeyFromSecret(ctx, r.Client, localAPIKey.Namespace, localAPIKey.Name) if err != nil { logger.Error(err, "Failed to get private key from secret") + r.updateAPIKeyStatus(ctx, localAPIKey, + err, + "PrivateKeyError", + fmt.Sprintf("Failed to get private key from secret: %v", err)) return } + logger.Info("Retrieved private key from secret", "apiKey", localAPIKey.Name) // Import private key privateKey, err := crypto.ImportPrivateKeyFromPEM(privateKeyStr) if err != nil { logger.Error(err, "Failed to import private key") + r.updateAPIKeyStatus(ctx, localAPIKey, + err, + "PrivateKeyImportError", + fmt.Sprintf("Failed to import private key: %v", err)) return } + logger.Info("Successfully imported private key", "apiKey", localAPIKey.Name) // Decrypt token jweToken := *cloudAPIKey.Status.EncryptedToken.JWE + logger.Info("Attempting to decrypt JWE token", "apiKey", localAPIKey.Name, "jweLength", len(jweToken)) token, err := crypto.DecryptJWEToken(jweToken, privateKey) if err != nil { logger.Error(err, "Failed to decrypt token") + r.updateAPIKeyStatus(ctx, localAPIKey, + err, + "TokenDecryptionFailed", + fmt.Sprintf("Failed to decrypt token: %v", err)) return } + logger.Info("Successfully decrypted token", "apiKey", localAPIKey.Name) // Store token in secret var keyID string @@ -147,10 +231,17 @@ func (r *APIKeyReconciler) processEncryptedToken(ctx context.Context, localAPIKe if err := utils.CreateOrUpdateTokenSecret(ctx, r.Client, localAPIKey, localAPIKey.Namespace, localAPIKey.Name, token, keyID); err != nil { logger.Error(err, "Failed to create or update token secret") + r.updateAPIKeyStatus(ctx, localAPIKey, + err, + "SecretUpdateFailed", + fmt.Sprintf("Failed to create or update token secret: %v", err)) return } - logger.Info("Successfully processed encrypted token and stored in secret") + logger.Info("Successfully processed encrypted token and stored in secret", "apiKey", localAPIKey.Name) + + // Update status to indicate successful token processing + r.updateAPIKeyStatus(ctx, localAPIKey, nil, "Ready", "APIKey token successfully decrypted and stored in secret") } // setupWatch creates a new watcher for an APIKey @@ -340,6 +431,9 @@ func (r *APIKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr if resultAPIKey.Status.EncryptedToken != nil && resultAPIKey.Status.EncryptedToken.JWE != nil { // Process the encrypted token r.processEncryptedToken(ctx, apiKey, resultAPIKey) + } else { + r.updateAPIKeyStatus(ctx, apiKey, nil, "WaitingForToken", + "Waiting for encrypted token from remote API server") } // Set up watch for APIKey diff --git a/controllers/serviceaccountbinding_controller.go b/controllers/serviceaccountbinding_controller.go index 4bdce6de..40818114 100644 --- a/controllers/serviceaccountbinding_controller.go +++ b/controllers/serviceaccountbinding_controller.go @@ -162,11 +162,20 @@ func (r *ServiceAccountBindingReconciler) Reconcile(ctx context.Context, req ctr return ctrl.Result{}, err } - // Get the APIServerConnection from the ServiceAccount + // Determine which APIServerRef to use + // If APIServerRef is specified in binding, use that; otherwise use the one from ServiceAccount + apiServerRefName := "" + if binding.Spec.APIServerRef != nil && binding.Spec.APIServerRef.Name != "" { + apiServerRefName = binding.Spec.APIServerRef.Name + } else { + apiServerRefName = serviceAccount.Spec.APIServerRef.Name + } + + // Get the APIServerConnection connection := &resourcev1alpha1.StreamNativeCloudConnection{} if err := r.Get(ctx, types.NamespacedName{ Namespace: req.Namespace, - Name: serviceAccount.Spec.APIServerRef.Name, + Name: apiServerRefName, }, connection); err != nil { r.updateServiceAccountBindingStatus(ctx, binding, err, "ConnectionNotFound", fmt.Sprintf("Failed to get APIServerConnection: %v", err)) @@ -202,22 +211,39 @@ func (r *ServiceAccountBindingReconciler) Reconcile(ctx context.Context, req ctr return ctrl.Result{}, err } + // Validate PoolMemberRefs + if len(binding.Spec.PoolMemberRefs) == 0 { + err := fmt.Errorf("at least one poolMemberRef is required") + r.updateServiceAccountBindingStatus(ctx, binding, err, "ValidationFailed", err.Error()) + return ctrl.Result{}, err + } + // Handle deletion if !binding.DeletionTimestamp.IsZero() { if controllers2.ContainsString(binding.Finalizers, ServiceAccountBindingFinalizer) { - // Try to delete remote ServiceAccountBinding - if err := bindingClient.DeleteServiceAccountBinding(ctx, binding); err != nil { - if !apierrors.IsNotFound(err) { - r.updateServiceAccountBindingStatus(ctx, binding, err, "DeleteFailed", - fmt.Sprintf("Failed to delete external resources: %v", err)) - return ctrl.Result{}, err + // Try to delete remote ServiceAccountBinding for each PoolMemberRef + for i, poolMemberRef := range binding.Spec.PoolMemberRefs { + // Create a unique name for each remote ServiceAccountBinding based on the local binding name and index + remoteName := fmt.Sprintf("%s.%s.%s", binding.Spec.ServiceAccountName, poolMemberRef.Namespace, poolMemberRef.Name) + + // Delete the remote ServiceAccountBinding + if err := bindingClient.DeleteServiceAccountBinding(ctx, &resourcev1alpha1.ServiceAccountBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: remoteName, + }, + }); err != nil { + if !apierrors.IsNotFound(err) { + r.updateServiceAccountBindingStatus(ctx, binding, err, "DeleteFailed", + fmt.Sprintf("Failed to delete remote ServiceAccountBinding for PoolMemberRef %d: %v", i, err)) + return ctrl.Result{}, err + } + // If the resource is already gone, that's fine + logger.Info("Remote ServiceAccountBinding already deleted or not found", + "binding", remoteName, "poolMemberRef", poolMemberRef) } - // If the resource is already gone, that's fine - logger.Info("Remote ServiceAccountBinding already deleted or not found", - "binding", binding.Name) } - // Remove finalizer after successful deletion + // Remove finalizer after successful deletion of all remote ServiceAccountBindings binding.Finalizers = controllers2.RemoveString(binding.Finalizers, ServiceAccountBindingFinalizer) if err := r.Update(ctx, binding); err != nil { return ctrl.Result{}, err @@ -234,48 +260,73 @@ func (r *ServiceAccountBindingReconciler) Reconcile(ctx context.Context, req ctr } } - // Check if ServiceAccountBinding exists - existingBinding, err := bindingClient.GetServiceAccountBinding(ctx, binding.Name) - if err != nil { - logger.Info("Failed to get ServiceAccountBinding", "error", err, "existingBinding", existingBinding) - if !apierrors.IsNotFound(err) { - r.updateServiceAccountBindingStatus(ctx, binding, err, "GetServiceAccountBindingFailed", - fmt.Sprintf("Failed to get ServiceAccountBinding: %v", err)) - return ctrl.Result{}, client.IgnoreNotFound(err) + // Process each PoolMemberRef + var errs []error + for i, poolMemberRef := range binding.Spec.PoolMemberRefs { + // Create a unique name for each remote ServiceAccountBinding based on the local binding name and index + remoteName := fmt.Sprintf("%s.%s.%s", binding.Spec.ServiceAccountName, poolMemberRef.Namespace, poolMemberRef.Name) + + // Create a ServiceAccountBinding for this PoolMemberRef + remoteBinding := &resourcev1alpha1.ServiceAccountBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: remoteName, + }, + Spec: resourcev1alpha1.ServiceAccountBindingSpec{ + ServiceAccountName: binding.Spec.ServiceAccountName, + PoolMemberRefs: []resourcev1alpha1.PoolMemberReference{poolMemberRef}, + }, } - existingBinding = nil - } - if existingBinding == nil { - // Create ServiceAccountBinding - _, err := bindingClient.CreateServiceAccountBinding(ctx, binding) + // Check if ServiceAccountBinding exists + existingBinding, err := bindingClient.GetServiceAccountBinding(ctx, remoteName) if err != nil { - r.updateServiceAccountBindingStatus(ctx, binding, err, "CreateServiceAccountBindingFailed", - fmt.Sprintf("Failed to create ServiceAccountBinding: %v", err)) - return ctrl.Result{}, err + logger.Info("Failed to get ServiceAccountBinding", "error", err, "existingBinding", existingBinding) + if !apierrors.IsNotFound(err) { + errs = append(errs, fmt.Errorf("failed to get ServiceAccountBinding for PoolMemberRef %d: %w", i, err)) + continue + } + existingBinding = nil } - // Update status - r.updateServiceAccountBindingStatus(ctx, binding, nil, "Ready", "ServiceAccountBinding created successfully") - } else { - // Update ServiceAccountBinding - _, err := bindingClient.UpdateServiceAccountBinding(ctx, binding) - if err != nil { - r.updateServiceAccountBindingStatus(ctx, binding, err, "UpdateServiceAccountBindingFailed", - fmt.Sprintf("Failed to update ServiceAccountBinding: %v", err)) - return ctrl.Result{}, err + if existingBinding == nil { + // Create ServiceAccountBinding + _, err := bindingClient.CreateServiceAccountBinding(ctx, remoteBinding) + if err != nil { + errs = append(errs, fmt.Errorf("failed to create ServiceAccountBinding for PoolMemberRef %d: %w", i, err)) + continue + } + logger.Info("Created ServiceAccountBinding", "name", remoteName, "poolMemberRef", poolMemberRef) + } else { + // Update ServiceAccountBinding + _, err := bindingClient.UpdateServiceAccountBinding(ctx, remoteBinding) + if err != nil { + errs = append(errs, fmt.Errorf("failed to update ServiceAccountBinding for PoolMemberRef %d: %w", i, err)) + continue + } + logger.Info("Updated ServiceAccountBinding", "name", remoteName, "poolMemberRef", poolMemberRef) } - // Update status - r.updateServiceAccountBindingStatus(ctx, binding, nil, "Ready", "ServiceAccountBinding updated successfully") + // Setup watch for each remote binding + if err := r.setupWatch(ctx, remoteBinding, bindingClient); err != nil { + logger.Error(err, "Failed to setup watch", "name", remoteName) + // Don't return error, just log it + } } - // Setup watch after ServiceAccountBinding is created/updated - if err := r.setupWatch(ctx, binding, bindingClient); err != nil { - logger.Error(err, "Failed to setup watch") - // Don't return error, just log it + // Update status based on errors + if len(errs) > 0 { + // Combine error messages + errorMsg := "Failed to process some PoolMemberRefs: " + for _, err := range errs { + errorMsg += err.Error() + "; " + } + r.updateServiceAccountBindingStatus(ctx, binding, fmt.Errorf(errorMsg), "ProcessingFailed", errorMsg) + return ctrl.Result{}, nil } + // Update status + r.updateServiceAccountBindingStatus(ctx, binding, nil, "Ready", "All ServiceAccountBindings processed successfully") + return ctrl.Result{RequeueAfter: requeueInterval}, nil } diff --git a/docs/serviceaccountbinding.md b/docs/serviceaccountbinding.md index af24321f..ce3572fd 100644 --- a/docs/serviceaccountbinding.md +++ b/docs/serviceaccountbinding.md @@ -1,6 +1,6 @@ # ServiceAccountBinding -The `ServiceAccountBinding` resource allows you to bind a service account to a pool member in the StreamNative Cloud, granting the service account access to the resources managed by that pool member, such as Pulsar Functions, Pulsar Connectors, Kafka Connect Connectors, Flink Jobs, etc. +The `ServiceAccountBinding` resource allows you to bind a service account to one or more pool members in the StreamNative Cloud, granting the service account access to the resources managed by those pool members, such as Pulsar Functions, Pulsar Connectors, Kafka Connect Connectors, Flink Jobs, etc. ## Example @@ -12,9 +12,14 @@ metadata: namespace: default spec: serviceAccountName: my-service-account - poolMemberRef: - namespace: default - name: my-pool-member + # Optional: Override APIServer connection from ServiceAccount + # apiServerRef: + # name: custom-api-connection + poolMemberRefs: + - namespace: default + name: pool-member-1 + - namespace: production + name: pool-member-2 ``` ## Specification @@ -22,8 +27,10 @@ spec: | Field | Type | Description | Required | | --- | --- | --- | --- | | `spec.serviceAccountName` | string | Reference to a ServiceAccount in the same namespace as this binding object | Yes | -| `spec.poolMemberRef.name` | string | Name of the pool member this service account will be bound to | Yes | -| `spec.poolMemberRef.namespace` | string | Namespace of the pool member | Yes | +| `spec.apiServerRef.name` | string | Optional reference to a StreamNativeCloudConnection. If not provided, the connection from the ServiceAccount will be used | No | +| `spec.poolMemberRefs` | []PoolMemberReference | List of pool members this service account will be bound to | Yes | +| `spec.poolMemberRefs[].name` | string | Name of the pool member | Yes | +| `spec.poolMemberRefs[].namespace` | string | Namespace of the pool member | Yes | ## Status @@ -41,7 +48,9 @@ Service account bindings provide a way to grant service accounts access to speci To create a service account binding, you need: 1. An existing ServiceAccount resource in the same namespace -2. A pool member to which the service account will be bound +2. One or more pool members to which the service account will be bound + +#### Basic Example ```yaml apiVersion: resource.streamnative.io/v1alpha1 @@ -51,8 +60,30 @@ metadata: namespace: default spec: serviceAccountName: app-service-account - poolMemberRef: - namespace: default + poolMemberRefs: + - namespace: default + name: production-pool-member +``` + +#### Advanced Example with Multiple Pool Members + +```yaml +apiVersion: resource.streamnative.io/v1alpha1 +kind: ServiceAccountBinding +metadata: + name: multi-cluster-binding + namespace: default +spec: + serviceAccountName: app-service-account + # Optional: Use a different API connection than the one in ServiceAccount + apiServerRef: + name: custom-api-connection + poolMemberRefs: + - namespace: dev + name: dev-pool-member + - namespace: staging + name: staging-pool-member + - namespace: production name: production-pool-member ``` diff --git a/go.mod b/go.mod index 7fa57b96..08c51a49 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,11 @@ toolchain go1.24.1 require ( github.com/apache/pulsar-client-go v0.14.0 github.com/go-logr/logr v1.4.2 + github.com/lestrrat-go/jwx/v2 v2.1.4 github.com/onsi/ginkgo v1.16.5 github.com/onsi/ginkgo/v2 v2.21.0 github.com/onsi/gomega v1.35.1 + github.com/pkg/errors v0.9.1 github.com/xhit/go-str2duration/v2 v2.1.0 go.uber.org/zap v1.27.0 gocloud.dev v0.40.0 @@ -65,6 +67,7 @@ require ( github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/evanphx/json-patch v5.9.0+incompatible // indirect @@ -98,7 +101,10 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/lestrrat-go/jwx/v2 v2.1.4 // indirect + github.com/lestrrat-go/blackmagic v1.0.2 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc v1.0.6 // indirect + github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect @@ -109,7 +115,6 @@ require ( github.com/nxadm/tail v1.4.8 // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.5.0 // indirect @@ -117,7 +122,6 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.10.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect diff --git a/go.sum b/go.sum index b47f6f87..1a4be1c0 100644 --- a/go.sum +++ b/go.sum @@ -1454,6 +1454,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= @@ -1744,6 +1746,14 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= +github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= +github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= github.com/lestrrat-go/jwx/v2 v2.1.4 h1:uBCMmJX8oRZStmKuMMOFb0Yh9xmEMgNJLgjuKKt4/qc= github.com/lestrrat-go/jwx/v2 v2.1.4/go.mod h1:nWRbDFR1ALG2Z6GJbBXzfQaYyvn751KuuyySN2yR6is= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= diff --git a/go.work.sum b/go.work.sum index b4326b07..0d210025 100644 --- a/go.work.sum +++ b/go.work.sum @@ -924,11 +924,12 @@ github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/daviddengcn/go-colortext v1.0.0 h1:ANqDyC0ys6qCSvuEK7l3g5RaehL/Xck9EX8ATG8oKsE= github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c= +github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= +github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/dgraph-io/ristretto/v2 v2.0.1 h1:7W0LfEP+USCmtrUjJsk+Jv2jbhJmb72N4yRI7GrLdMI= github.com/dgraph-io/ristretto/v2 v2.0.1/go.mod h1:K7caLeufSdxm+ITp1n/73U+VbFVAHrexfLbz4n14hpo= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= @@ -1242,18 +1243,10 @@ github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RC github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80= github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= -github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= -github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= -github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= -github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= -github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= -github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A= github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= -github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= -github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= github.com/lestrrat-go/jwx v1.2.25 h1:tAx93jN2SdPvFn08fHNAhqFJazn5mBBOB8Zli0g0otA= github.com/lestrrat-go/jwx v1.2.25/go.mod h1:zoNuZymNl5lgdcu6P7K6ie2QRll5HVfF4xwxBBK1NxY= github.com/lestrrat-go/jwx/v2 v2.0.11 h1:ViHMnaMeaO0qV16RZWBHM7GTrAnX2aFLVKofc7FuKLQ= diff --git a/pkg/streamnativecloud/serviceaccountbinding_client.go b/pkg/streamnativecloud/serviceaccountbinding_client.go index 9b84e200..c97dcb22 100644 --- a/pkg/streamnativecloud/serviceaccountbinding_client.go +++ b/pkg/streamnativecloud/serviceaccountbinding_client.go @@ -90,14 +90,16 @@ func convertToCloudServiceAccountBinding(binding *resourcev1alpha1.ServiceAccoun cloudBinding.Spec.ServiceAccountName = binding.Spec.ServiceAccountName } - // Convert PoolMemberRef - cloudBinding.Spec.PoolMemberRef = cloudapi.PoolMemberReference{ - Namespace: binding.Spec.PoolMemberRef.Namespace, - Name: binding.Spec.PoolMemberRef.Name, - } - - if binding.Spec.PoolMemberRef.Namespace == "" { - cloudBinding.Spec.PoolMemberRef.Namespace = organization + // Convert PoolMemberRef - use the first one if multiple are provided + if len(binding.Spec.PoolMemberRefs) > 0 { + cloudBinding.Spec.PoolMemberRef = cloudapi.PoolMemberReference{ + Namespace: binding.Spec.PoolMemberRefs[0].Namespace, + Name: binding.Spec.PoolMemberRefs[0].Name, + } + + if binding.Spec.PoolMemberRefs[0].Namespace == "" { + cloudBinding.Spec.PoolMemberRef.Namespace = organization + } } return cloudBinding From c2475426768577ac983f1200b59e0f3a8cbb6c33 Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Tue, 15 Apr 2025 14:53:55 +0800 Subject: [PATCH 5/6] fix lint --- controllers/serviceaccountbinding_controller.go | 3 ++- pkg/crypto/jwe.go | 1 + pkg/crypto/rsa.go | 1 + pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go | 2 +- .../apis/cloud/v1alpha1/serviceaccount_types.go | 2 +- .../apis/cloud/v1alpha1/serviceaccountbinding_types.go | 2 +- pkg/utils/secrets.go | 5 +++-- 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/controllers/serviceaccountbinding_controller.go b/controllers/serviceaccountbinding_controller.go index 40818114..7a4e068c 100644 --- a/controllers/serviceaccountbinding_controller.go +++ b/controllers/serviceaccountbinding_controller.go @@ -20,6 +20,7 @@ import ( "sync" "time" + "github.com/pkg/errors" resourcev1alpha1 "github.com/streamnative/pulsar-resources-operator/api/v1alpha1" controllers2 "github.com/streamnative/pulsar-resources-operator/pkg/streamnativecloud" @@ -320,7 +321,7 @@ func (r *ServiceAccountBindingReconciler) Reconcile(ctx context.Context, req ctr for _, err := range errs { errorMsg += err.Error() + "; " } - r.updateServiceAccountBindingStatus(ctx, binding, fmt.Errorf(errorMsg), "ProcessingFailed", errorMsg) + r.updateServiceAccountBindingStatus(ctx, binding, errors.New(errorMsg), "ProcessingFailed", errorMsg) return ctrl.Result{}, nil } diff --git a/pkg/crypto/jwe.go b/pkg/crypto/jwe.go index d87b71a5..20515093 100644 --- a/pkg/crypto/jwe.go +++ b/pkg/crypto/jwe.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Package crypto implements the crypto functions package crypto import ( diff --git a/pkg/crypto/rsa.go b/pkg/crypto/rsa.go index 48f4f03b..a8d5469a 100644 --- a/pkg/crypto/rsa.go +++ b/pkg/crypto/rsa.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Package crypto implements the crypto functions package crypto import ( diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go index 6aba1cee..2787bd37 100644 --- a/pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/apikey_types.go @@ -21,7 +21,7 @@ import ( // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// APIKey +// APIKey is a resource that represents an API key for a service account // +k8s:openapi-gen=true // +resource:path=apikeys,strategy=APIKeyStrategy // +kubebuilder:categories=all diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go index e56eb312..22220350 100644 --- a/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccount_types.go @@ -21,7 +21,7 @@ import ( // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// ServiceAccount +// ServiceAccount is a resource that represents a service account for a cluster // +k8s:openapi-gen=true // +resource:path=serviceaccounts,strategy=ServiceAccountStrategy // +kubebuilder:categories=all diff --git a/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go index 401ed60b..79e9fe4f 100644 --- a/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go +++ b/pkg/streamnativecloud/apis/cloud/v1alpha1/serviceaccountbinding_types.go @@ -21,7 +21,7 @@ import ( // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// ServiceAccountBinding +// ServiceAccountBinding is a resource that represents a binding between a service account and a pool member // +k8s:openapi-gen=true // +resource:path=serviceaccountbindings,strategy=ServiceAccountBindingStrategy // +kubebuilder:categories=all diff --git a/pkg/utils/secrets.go b/pkg/utils/secrets.go index a6fc4e25..b0c82e15 100644 --- a/pkg/utils/secrets.go +++ b/pkg/utils/secrets.go @@ -19,7 +19,7 @@ import ( "fmt" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -40,6 +40,7 @@ const ( // ServiceAccountCredentialsField is the key for the service account credentials in the secret ServiceAccountCredentialsField = "credentials.json" // ServiceAccountCredentialsType is the type for service account credentials + //nolint:gosec ServiceAccountCredentialsType = "TYPE_SN_CREDENTIALS_FILE" ) @@ -147,7 +148,7 @@ func GetPrivateKeyFromSecret(ctx context.Context, c client.Client, namespace, na secretName := name + PrivateKeySecretSuffix var secret corev1.Secret if err := c.Get(ctx, types.NamespacedName{Namespace: namespace, Name: secretName}, &secret); err != nil { - if errors.IsNotFound(err) { + if apierrors.IsNotFound(err) { return "", fmt.Errorf("private key secret not found: %s", secretName) } return "", fmt.Errorf("failed to get private key secret: %w", err) From 545708b037588c8e9772da18133cdddd2dc539c2 Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Tue, 6 May 2025 23:10:17 +0800 Subject: [PATCH 6/6] add spec.exportPlaintextToken --- api/v1alpha1/apikey_types.go | 4 +++ api/v1alpha1/zz_generated.deepcopy.go | 5 ++++ controllers/apikey_controller.go | 37 +++++++++++++++++---------- docs/apikey.md | 5 ++-- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/api/v1alpha1/apikey_types.go b/api/v1alpha1/apikey_types.go index 3ac00928..a0e975e2 100644 --- a/api/v1alpha1/apikey_types.go +++ b/api/v1alpha1/apikey_types.go @@ -49,6 +49,10 @@ type APIKeySpec struct { // EncryptionKey contains the public key used to encrypt the token // +optional EncryptionKey *EncryptionKey `json:"encryptionKey,omitempty"` + + // ExportPlaintextToken indicates whether the token should be exported in plaintext + // +optional + ExportPlaintextToken *bool `json:"exportPlaintextToken,omitempty"` } // EncryptionKey contains a public key used for encryption diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 633b2a6c..7a0dfdb0 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -98,6 +98,11 @@ func (in *APIKeySpec) DeepCopyInto(out *APIKeySpec) { *out = new(EncryptionKey) **out = **in } + if in.ExportPlaintextToken != nil { + in, out := &in.ExportPlaintextToken, &out.ExportPlaintextToken + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIKeySpec. diff --git a/controllers/apikey_controller.go b/controllers/apikey_controller.go index eb3815cf..f7c18334 100644 --- a/controllers/apikey_controller.go +++ b/controllers/apikey_controller.go @@ -120,15 +120,19 @@ func (r *APIKeyReconciler) handleWatchEvents(ctx context.Context, namespacedName continue } - // Process encrypted token and update Secret if needed - if cloudAPIKey.Status.EncryptedToken != nil && cloudAPIKey.Status.EncryptedToken.JWE != nil { - logger.Info("Found encrypted token in watch event, processing", "apiKey", namespacedName.Name) - r.processEncryptedToken(ctx, localAPIKey, cloudAPIKey) + if localAPIKey.Spec.ExportPlaintextToken != nil && *localAPIKey.Spec.ExportPlaintextToken { + // Process encrypted token and update Secret if needed + if cloudAPIKey.Status.EncryptedToken != nil && cloudAPIKey.Status.EncryptedToken.JWE != nil { + logger.Info("Found encrypted token in watch event, processing", "apiKey", namespacedName.Name) + r.processEncryptedToken(ctx, localAPIKey, cloudAPIKey) + } else { + logger.Info("No encrypted token found in watch event", "apiKey", namespacedName.Name) + // Update status to reflect that we're waiting for token + r.updateAPIKeyStatus(ctx, localAPIKey, nil, "WaitingForToken", + "Waiting for encrypted token from remote API server") + } } else { - logger.Info("No encrypted token found in watch event", "apiKey", namespacedName.Name) - // Update status to reflect that we're waiting for token - r.updateAPIKeyStatus(ctx, localAPIKey, nil, "WaitingForToken", - "Waiting for encrypted token from remote API server") + r.updateAPIKeyStatus(ctx, localAPIKey, nil, "Ready", "APIKey created successfully") } } } @@ -427,13 +431,18 @@ func (r *APIKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, err } - // Update status with token information - if resultAPIKey.Status.EncryptedToken != nil && resultAPIKey.Status.EncryptedToken.JWE != nil { - // Process the encrypted token - r.processEncryptedToken(ctx, apiKey, resultAPIKey) + if apiKey.Spec.ExportPlaintextToken != nil && *apiKey.Spec.ExportPlaintextToken { + // Update status with token information + if resultAPIKey.Status.EncryptedToken != nil && resultAPIKey.Status.EncryptedToken.JWE != nil { + // Process the encrypted token + r.processEncryptedToken(ctx, apiKey, resultAPIKey) + } else { + r.updateAPIKeyStatus(ctx, apiKey, nil, "WaitingForToken", + "Waiting for encrypted token from remote API server") + } } else { - r.updateAPIKeyStatus(ctx, apiKey, nil, "WaitingForToken", - "Waiting for encrypted token from remote API server") + // Update status with token information + r.updateAPIKeyStatus(ctx, apiKey, nil, "Ready", "APIKey created successfully") } // Set up watch for APIKey diff --git a/docs/apikey.md b/docs/apikey.md index 99b20eb8..16bb92b0 100644 --- a/docs/apikey.md +++ b/docs/apikey.md @@ -30,6 +30,7 @@ spec: | `spec.expirationTime` | string | Timestamp defining when this API key will expire | No | | `spec.revoke` | boolean | Indicates whether this API key should be revoked | No | | `spec.encryptionKey` | object | Contains the public key used to encrypt the token | No | +| `spec.exportPlaintextToken` | boolean | Indicates whether the plaintext token should be exported as Kubernetes secret if the Pulsar Resources Operator managed the private key. (Default: false) | No | ## Status @@ -48,11 +49,11 @@ spec: For each APIKey resource, the operator creates and manages two types of Secrets: -1. **Private Key Secret**: Contains the RSA private key used for decrypting tokens +1. **Private Key Secret**: Contains the RSA private key used for decrypting tokens, only if no `encryptionKey` provided - Name format: `-private-key` - Contains key: `private-key` -2. **Token Secret**: Contains the decrypted API token for authentication +2. **Token Secret**: Contains the decrypted API token for authentication, only if no `encryptionKey` provided, and `exportPlaintextToken` to `true` - Name format: `-token` - Contains key: `token` - Includes labels: