mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-10 07:58:15 -05:00
feat(k8-operator): dynamic secrets
This commit is contained in:
@@ -26,4 +26,13 @@ resources:
|
||||
kind: InfisicalPushSecretSecret
|
||||
path: github.com/Infisical/infisical/k8-operator/api/v1alpha1
|
||||
version: v1alpha1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: true
|
||||
controller: true
|
||||
domain: infisical.com
|
||||
group: secrets
|
||||
kind: InfisicalDynamicSecret
|
||||
path: github.com/Infisical/infisical/k8-operator/api/v1alpha1
|
||||
version: v1alpha1
|
||||
version: "3"
|
||||
|
||||
105
k8-operator/api/v1alpha1/common.go
Normal file
105
k8-operator/api/v1alpha1/common.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package v1alpha1
|
||||
|
||||
type GenericInfisicalAuthentication struct {
|
||||
// +kubebuilder:validation:Optional
|
||||
UniversalAuth GenericUniversalAuth `json:"universalAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
KubernetesAuth GenericKubernetesAuth `json:"kubernetesAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
AwsIamAuth GenericAwsIamAuth `json:"awsIamAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
AzureAuth GenericAzureAuth `json:"azureAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
GcpIdTokenAuth GenericGcpIdTokenAuth `json:"gcpIdTokenAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
GcpIamAuth GenericGcpIamAuth `json:"gcpIamAuth,omitempty"`
|
||||
}
|
||||
|
||||
type GenericUniversalAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
CredentialsRef KubeSecretReference `json:"credentialsRef"`
|
||||
}
|
||||
|
||||
type GenericAwsIamAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
}
|
||||
|
||||
type GenericAzureAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Optional
|
||||
Resource string `json:"resource,omitempty"`
|
||||
}
|
||||
|
||||
type GenericGcpIdTokenAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
}
|
||||
|
||||
type GenericGcpIamAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Required
|
||||
ServiceAccountKeyFilePath string `json:"serviceAccountKeyFilePath"`
|
||||
}
|
||||
|
||||
type GenericKubernetesAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Required
|
||||
ServiceAccountRef KubernetesServiceAccountRef `json:"serviceAccountRef"`
|
||||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
// Reference to secret containing CA cert
|
||||
// +kubebuilder:validation:Optional
|
||||
CaRef CaReference `json:"caRef,omitempty"`
|
||||
}
|
||||
|
||||
type CaReference struct {
|
||||
// The name of the Kubernetes Secret
|
||||
// +kubebuilder:validation:Required
|
||||
SecretName string `json:"secretName"`
|
||||
|
||||
// The namespace where the Kubernetes Secret is located
|
||||
// +kubebuilder:validation:Required
|
||||
SecretNamespace string `json:"secretNamespace"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
// The name of the secret property with the CA certificate value
|
||||
SecretKey string `json:"key"`
|
||||
}
|
||||
|
||||
type KubeSecretReference struct {
|
||||
// The name of the Kubernetes Secret
|
||||
// +kubebuilder:validation:Required
|
||||
SecretName string `json:"secretName"`
|
||||
|
||||
// The name space where the Kubernetes Secret is located
|
||||
// +kubebuilder:validation:Required
|
||||
SecretNamespace string `json:"secretNamespace"`
|
||||
}
|
||||
|
||||
type ManagedKubeSecretConfig struct {
|
||||
// The name of the Kubernetes Secret
|
||||
// +kubebuilder:validation:Required
|
||||
SecretName string `json:"secretName"`
|
||||
|
||||
// The name space where the Kubernetes Secret is located
|
||||
// +kubebuilder:validation:Required
|
||||
SecretNamespace string `json:"secretNamespace"`
|
||||
|
||||
// The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types
|
||||
// +kubebuilder:validation:Optional
|
||||
// +kubebuilder:default:=Opaque
|
||||
SecretType string `json:"secretType"`
|
||||
|
||||
// The Kubernetes Secret creation policy.
|
||||
// Enum with values: 'Owner', 'Orphan'.
|
||||
// Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it.
|
||||
// Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.
|
||||
// +kubebuilder:validation:Optional
|
||||
// +kubebuilder:default:=Orphan
|
||||
CreationPolicy string `json:"creationPolicy"`
|
||||
}
|
||||
99
k8-operator/api/v1alpha1/infisicaldynamicsecret_types.go
Normal file
99
k8-operator/api/v1alpha1/infisicaldynamicsecret_types.go
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2022.
|
||||
|
||||
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 InfisicalDynamicSecretLease struct {
|
||||
ID string `json:"id"`
|
||||
Version int64 `json:"version"`
|
||||
CreationTimestamp metav1.Time `json:"creationTimestamp"`
|
||||
ExpiresAt metav1.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type DynamicSecretDetails struct {
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
SecretName string `json:"secretName"`
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
SecretPath string `json:"secretsPath"`
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
EnvironmentSlug string `json:"environmentSlug"`
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
ProjectID string `json:"projectId"`
|
||||
}
|
||||
|
||||
// InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret.
|
||||
type InfisicalDynamicSecretSpec struct {
|
||||
// +kubebuilder:validation:Required
|
||||
ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"` // The destination to store the lease in.
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
Authentication GenericInfisicalAuthentication `json:"authentication"` // The authentication to use for authenticating with Infisical.
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
DynamicSecret DynamicSecretDetails `json:"dynamicSecret"` // The dynamic secret to create the lease for. Required.
|
||||
|
||||
LeaseRevocationPolicy string `json:"leaseRevocationPolicy"` // Revoke will revoke the lease when the resource is deleted. Optional, will default to no revocation.
|
||||
LeaseTTL string `json:"leaseTTL"` // The TTL of the lease in seconds. Optional, will default to the dynamic secret default TTL.
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
HostAPI string `json:"hostAPI"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
TLS TLSConfig `json:"tls"`
|
||||
}
|
||||
|
||||
// InfisicalDynamicSecretStatus defines the observed state of InfisicalDynamicSecret.
|
||||
type InfisicalDynamicSecretStatus struct {
|
||||
Lease *InfisicalDynamicSecretLease `json:"lease,omitempty"`
|
||||
|
||||
DynamicSecretID string `json:"dynamicSecretId,omitempty"`
|
||||
|
||||
// The MaxTTL can be null, if it's null, there's no max TTL and we should never have to renew.
|
||||
MaxTTL string `json:"maxTTL,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
|
||||
// InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets API.
|
||||
type InfisicalDynamicSecret struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec InfisicalDynamicSecretSpec `json:"spec,omitempty"`
|
||||
Status InfisicalDynamicSecretStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// InfisicalDynamicSecretList contains a list of InfisicalDynamicSecret.
|
||||
type InfisicalDynamicSecretList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []InfisicalDynamicSecret `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&InfisicalDynamicSecret{}, &InfisicalDynamicSecretList{})
|
||||
}
|
||||
@@ -16,64 +16,6 @@ type InfisicalPushSecretDestination struct {
|
||||
ProjectID string `json:"projectId"`
|
||||
}
|
||||
|
||||
type PushSecretTlsConfig struct {
|
||||
// Reference to secret containing CA cert
|
||||
// +kubebuilder:validation:Optional
|
||||
CaRef CaReference `json:"caRef,omitempty"`
|
||||
}
|
||||
|
||||
// PushSecretUniversalAuth defines universal authentication
|
||||
type PushSecretUniversalAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
CredentialsRef KubeSecretReference `json:"credentialsRef"`
|
||||
}
|
||||
|
||||
type PushSecretAwsIamAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
}
|
||||
|
||||
type PushSecretAzureAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Optional
|
||||
Resource string `json:"resource,omitempty"`
|
||||
}
|
||||
|
||||
type PushSecretGcpIdTokenAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
}
|
||||
|
||||
type PushSecretGcpIamAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Required
|
||||
ServiceAccountKeyFilePath string `json:"serviceAccountKeyFilePath"`
|
||||
}
|
||||
|
||||
type PushSecretKubernetesAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Required
|
||||
ServiceAccountRef KubernetesServiceAccountRef `json:"serviceAccountRef"`
|
||||
}
|
||||
|
||||
type PushSecretAuthentication struct {
|
||||
// +kubebuilder:validation:Optional
|
||||
UniversalAuth PushSecretUniversalAuth `json:"universalAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
KubernetesAuth PushSecretKubernetesAuth `json:"kubernetesAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
AwsIamAuth PushSecretAwsIamAuth `json:"awsIamAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
AzureAuth PushSecretAzureAuth `json:"azureAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
GcpIdTokenAuth PushSecretGcpIdTokenAuth `json:"gcpIdTokenAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
GcpIamAuth PushSecretGcpIamAuth `json:"gcpIamAuth,omitempty"`
|
||||
}
|
||||
|
||||
type SecretPush struct {
|
||||
// +kubebuilder:validation:Required
|
||||
Secret KubeSecretReference `json:"secret"`
|
||||
@@ -92,7 +34,7 @@ type InfisicalPushSecretSpec struct {
|
||||
Destination InfisicalPushSecretDestination `json:"destination"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
Authentication PushSecretAuthentication `json:"authentication"`
|
||||
Authentication GenericInfisicalAuthentication `json:"authentication"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
Push SecretPush `json:"push"`
|
||||
@@ -104,7 +46,7 @@ type InfisicalPushSecretSpec struct {
|
||||
HostAPI string `json:"hostAPI"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
TLS PushSecretTlsConfig `json:"tls"`
|
||||
TLS TLSConfig `json:"tls"`
|
||||
}
|
||||
|
||||
// InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret
|
||||
|
||||
@@ -116,43 +116,6 @@ type MachineIdentityScopeInWorkspace struct {
|
||||
Recursive bool `json:"recursive"`
|
||||
}
|
||||
|
||||
type KubeSecretReference struct {
|
||||
// The name of the Kubernetes Secret
|
||||
// +kubebuilder:validation:Required
|
||||
SecretName string `json:"secretName"`
|
||||
|
||||
// The name space where the Kubernetes Secret is located
|
||||
// +kubebuilder:validation:Required
|
||||
SecretNamespace string `json:"secretNamespace"`
|
||||
}
|
||||
|
||||
type MangedKubeSecretConfig struct {
|
||||
// The name of the Kubernetes Secret
|
||||
// +kubebuilder:validation:Required
|
||||
SecretName string `json:"secretName"`
|
||||
|
||||
// The name space where the Kubernetes Secret is located
|
||||
// +kubebuilder:validation:Required
|
||||
SecretNamespace string `json:"secretNamespace"`
|
||||
|
||||
// The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types
|
||||
// +kubebuilder:validation:Optional
|
||||
// +kubebuilder:default:=Opaque
|
||||
SecretType string `json:"secretType"`
|
||||
|
||||
// The Kubernetes Secret creation policy.
|
||||
// Enum with values: 'Owner', 'Orphan'.
|
||||
// Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it.
|
||||
// Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.
|
||||
// +kubebuilder:validation:Optional
|
||||
// +kubebuilder:default:=Orphan
|
||||
CreationPolicy string `json:"creationPolicy"`
|
||||
|
||||
// The template to transform the secret data
|
||||
// +kubebuilder:validation:Optional
|
||||
Template *InfisicalSecretTemplate `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
type InfisicalSecretTemplate struct {
|
||||
// This injects all retrieved secrets into the top level of your template.
|
||||
// Secrets defined in the template will take precedence over the injected ones.
|
||||
@@ -163,26 +126,6 @@ type InfisicalSecretTemplate struct {
|
||||
Data map[string]string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type CaReference struct {
|
||||
// The name of the Kubernetes Secret
|
||||
// +kubebuilder:validation:Required
|
||||
SecretName string `json:"secretName"`
|
||||
|
||||
// The namespace where the Kubernetes Secret is located
|
||||
// +kubebuilder:validation:Required
|
||||
SecretNamespace string `json:"secretNamespace"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
// The name of the secret property with the CA certificate value
|
||||
SecretKey string `json:"key"`
|
||||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
// Reference to secret containing CA cert
|
||||
// +kubebuilder:validation:Optional
|
||||
CaRef CaReference `json:"caRef,omitempty"`
|
||||
}
|
||||
|
||||
// InfisicalSecretSpec defines the desired state of InfisicalSecret
|
||||
type InfisicalSecretSpec struct {
|
||||
// +kubebuilder:validation:Optional
|
||||
@@ -192,7 +135,7 @@ type InfisicalSecretSpec struct {
|
||||
Authentication Authentication `json:"authentication"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
ManagedSecretReference MangedKubeSecretConfig `json:"managedSecretReference"`
|
||||
ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"`
|
||||
|
||||
// +kubebuilder:default:=60
|
||||
ResyncInterval int `json:"resyncInterval"`
|
||||
|
||||
@@ -96,6 +96,21 @@ func (in *CaReference) DeepCopy() *CaReference {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DynamicSecretDetails) DeepCopyInto(out *DynamicSecretDetails) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DynamicSecretDetails.
|
||||
func (in *DynamicSecretDetails) DeepCopy() *DynamicSecretDetails {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DynamicSecretDetails)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GCPIdTokenAuthDetails) DeepCopyInto(out *GCPIdTokenAuthDetails) {
|
||||
*out = *in
|
||||
@@ -128,6 +143,234 @@ func (in *GcpIamAuthDetails) DeepCopy() *GcpIamAuthDetails {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GenericAwsIamAuth) DeepCopyInto(out *GenericAwsIamAuth) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericAwsIamAuth.
|
||||
func (in *GenericAwsIamAuth) DeepCopy() *GenericAwsIamAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GenericAwsIamAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GenericAzureAuth) DeepCopyInto(out *GenericAzureAuth) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericAzureAuth.
|
||||
func (in *GenericAzureAuth) DeepCopy() *GenericAzureAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GenericAzureAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GenericGcpIamAuth) DeepCopyInto(out *GenericGcpIamAuth) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericGcpIamAuth.
|
||||
func (in *GenericGcpIamAuth) DeepCopy() *GenericGcpIamAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GenericGcpIamAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GenericGcpIdTokenAuth) DeepCopyInto(out *GenericGcpIdTokenAuth) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericGcpIdTokenAuth.
|
||||
func (in *GenericGcpIdTokenAuth) DeepCopy() *GenericGcpIdTokenAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GenericGcpIdTokenAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GenericInfisicalAuthentication) DeepCopyInto(out *GenericInfisicalAuthentication) {
|
||||
*out = *in
|
||||
out.UniversalAuth = in.UniversalAuth
|
||||
out.KubernetesAuth = in.KubernetesAuth
|
||||
out.AwsIamAuth = in.AwsIamAuth
|
||||
out.AzureAuth = in.AzureAuth
|
||||
out.GcpIdTokenAuth = in.GcpIdTokenAuth
|
||||
out.GcpIamAuth = in.GcpIamAuth
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericInfisicalAuthentication.
|
||||
func (in *GenericInfisicalAuthentication) DeepCopy() *GenericInfisicalAuthentication {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GenericInfisicalAuthentication)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GenericKubernetesAuth) DeepCopyInto(out *GenericKubernetesAuth) {
|
||||
*out = *in
|
||||
out.ServiceAccountRef = in.ServiceAccountRef
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericKubernetesAuth.
|
||||
func (in *GenericKubernetesAuth) DeepCopy() *GenericKubernetesAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GenericKubernetesAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GenericUniversalAuth) DeepCopyInto(out *GenericUniversalAuth) {
|
||||
*out = *in
|
||||
out.CredentialsRef = in.CredentialsRef
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericUniversalAuth.
|
||||
func (in *GenericUniversalAuth) DeepCopy() *GenericUniversalAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GenericUniversalAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *InfisicalDynamicSecret) DeepCopyInto(out *InfisicalDynamicSecret) {
|
||||
*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 InfisicalDynamicSecret.
|
||||
func (in *InfisicalDynamicSecret) DeepCopy() *InfisicalDynamicSecret {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(InfisicalDynamicSecret)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *InfisicalDynamicSecret) 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 *InfisicalDynamicSecretLease) DeepCopyInto(out *InfisicalDynamicSecretLease) {
|
||||
*out = *in
|
||||
in.CreationTimestamp.DeepCopyInto(&out.CreationTimestamp)
|
||||
in.ExpiresAt.DeepCopyInto(&out.ExpiresAt)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretLease.
|
||||
func (in *InfisicalDynamicSecretLease) DeepCopy() *InfisicalDynamicSecretLease {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(InfisicalDynamicSecretLease)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *InfisicalDynamicSecretList) DeepCopyInto(out *InfisicalDynamicSecretList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]InfisicalDynamicSecret, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretList.
|
||||
func (in *InfisicalDynamicSecretList) DeepCopy() *InfisicalDynamicSecretList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(InfisicalDynamicSecretList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *InfisicalDynamicSecretList) 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 *InfisicalDynamicSecretSpec) DeepCopyInto(out *InfisicalDynamicSecretSpec) {
|
||||
*out = *in
|
||||
out.ManagedSecretReference = in.ManagedSecretReference
|
||||
out.Authentication = in.Authentication
|
||||
out.DynamicSecret = in.DynamicSecret
|
||||
out.TLS = in.TLS
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretSpec.
|
||||
func (in *InfisicalDynamicSecretSpec) DeepCopy() *InfisicalDynamicSecretSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(InfisicalDynamicSecretSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *InfisicalDynamicSecretStatus) DeepCopyInto(out *InfisicalDynamicSecretStatus) {
|
||||
*out = *in
|
||||
if in.Lease != nil {
|
||||
in, out := &in.Lease, &out.Lease
|
||||
*out = new(InfisicalDynamicSecretLease)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretStatus.
|
||||
func (in *InfisicalDynamicSecretStatus) DeepCopy() *InfisicalDynamicSecretStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(InfisicalDynamicSecretStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *InfisicalPushSecret) DeepCopyInto(out *InfisicalPushSecret) {
|
||||
*out = *in
|
||||
@@ -435,7 +678,7 @@ func (in *MachineIdentityScopeInWorkspace) DeepCopy() *MachineIdentityScopeInWor
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MangedKubeSecretConfig) DeepCopyInto(out *MangedKubeSecretConfig) {
|
||||
func (in *ManagedKubeSecretConfig) DeepCopyInto(out *ManagedKubeSecretConfig) {
|
||||
*out = *in
|
||||
if in.Template != nil {
|
||||
in, out := &in.Template, &out.Template
|
||||
@@ -444,141 +687,12 @@ func (in *MangedKubeSecretConfig) DeepCopyInto(out *MangedKubeSecretConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MangedKubeSecretConfig.
|
||||
func (in *MangedKubeSecretConfig) DeepCopy() *MangedKubeSecretConfig {
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedKubeSecretConfig.
|
||||
func (in *ManagedKubeSecretConfig) DeepCopy() *ManagedKubeSecretConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MangedKubeSecretConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PushSecretAuthentication) DeepCopyInto(out *PushSecretAuthentication) {
|
||||
*out = *in
|
||||
out.UniversalAuth = in.UniversalAuth
|
||||
out.KubernetesAuth = in.KubernetesAuth
|
||||
out.AwsIamAuth = in.AwsIamAuth
|
||||
out.AzureAuth = in.AzureAuth
|
||||
out.GcpIdTokenAuth = in.GcpIdTokenAuth
|
||||
out.GcpIamAuth = in.GcpIamAuth
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretAuthentication.
|
||||
func (in *PushSecretAuthentication) DeepCopy() *PushSecretAuthentication {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PushSecretAuthentication)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PushSecretAwsIamAuth) DeepCopyInto(out *PushSecretAwsIamAuth) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretAwsIamAuth.
|
||||
func (in *PushSecretAwsIamAuth) DeepCopy() *PushSecretAwsIamAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PushSecretAwsIamAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PushSecretAzureAuth) DeepCopyInto(out *PushSecretAzureAuth) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretAzureAuth.
|
||||
func (in *PushSecretAzureAuth) DeepCopy() *PushSecretAzureAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PushSecretAzureAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PushSecretGcpIamAuth) DeepCopyInto(out *PushSecretGcpIamAuth) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretGcpIamAuth.
|
||||
func (in *PushSecretGcpIamAuth) DeepCopy() *PushSecretGcpIamAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PushSecretGcpIamAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PushSecretGcpIdTokenAuth) DeepCopyInto(out *PushSecretGcpIdTokenAuth) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretGcpIdTokenAuth.
|
||||
func (in *PushSecretGcpIdTokenAuth) DeepCopy() *PushSecretGcpIdTokenAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PushSecretGcpIdTokenAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PushSecretKubernetesAuth) DeepCopyInto(out *PushSecretKubernetesAuth) {
|
||||
*out = *in
|
||||
out.ServiceAccountRef = in.ServiceAccountRef
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretKubernetesAuth.
|
||||
func (in *PushSecretKubernetesAuth) DeepCopy() *PushSecretKubernetesAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PushSecretKubernetesAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PushSecretTlsConfig) DeepCopyInto(out *PushSecretTlsConfig) {
|
||||
*out = *in
|
||||
out.CaRef = in.CaRef
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretTlsConfig.
|
||||
func (in *PushSecretTlsConfig) DeepCopy() *PushSecretTlsConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PushSecretTlsConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PushSecretUniversalAuth) DeepCopyInto(out *PushSecretUniversalAuth) {
|
||||
*out = *in
|
||||
out.CredentialsRef = in.CredentialsRef
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretUniversalAuth.
|
||||
func (in *PushSecretUniversalAuth) DeepCopy() *PushSecretUniversalAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PushSecretUniversalAuth)
|
||||
out := new(ManagedKubeSecretConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.10.0
|
||||
creationTimestamp: null
|
||||
name: infisicaldynamicsecrets.secrets.infisical.com
|
||||
spec:
|
||||
group: secrets.infisical.com
|
||||
names:
|
||||
kind: InfisicalDynamicSecret
|
||||
listKind: InfisicalDynamicSecretList
|
||||
plural: infisicaldynamicsecrets
|
||||
singular: infisicaldynamicsecret
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets
|
||||
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: InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret.
|
||||
properties:
|
||||
authentication:
|
||||
properties:
|
||||
awsIamAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
type: object
|
||||
azureAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
resource:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
type: object
|
||||
gcpIamAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
serviceAccountKeyFilePath:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
- serviceAccountKeyFilePath
|
||||
type: object
|
||||
gcpIdTokenAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
type: object
|
||||
kubernetesAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
serviceAccountRef:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- namespace
|
||||
type: object
|
||||
required:
|
||||
- identityId
|
||||
- serviceAccountRef
|
||||
type: object
|
||||
universalAuth:
|
||||
properties:
|
||||
credentialsRef:
|
||||
properties:
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret
|
||||
is located
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
required:
|
||||
- credentialsRef
|
||||
type: object
|
||||
type: object
|
||||
dynamicSecret:
|
||||
properties:
|
||||
environmentSlug:
|
||||
type: string
|
||||
projectId:
|
||||
type: string
|
||||
secretName:
|
||||
type: string
|
||||
secretsPath:
|
||||
type: string
|
||||
required:
|
||||
- environmentSlug
|
||||
- projectId
|
||||
- secretName
|
||||
- secretsPath
|
||||
type: object
|
||||
hostAPI:
|
||||
type: string
|
||||
leaseRevocationPolicy:
|
||||
type: string
|
||||
leaseTTL:
|
||||
type: string
|
||||
managedSecretReference:
|
||||
properties:
|
||||
creationPolicy:
|
||||
default: Orphan
|
||||
description: 'The Kubernetes Secret creation policy. Enum with
|
||||
values: ''Owner'', ''Orphan''. Owner creates the secret and
|
||||
sets .metadata.ownerReferences of the InfisicalSecret CRD that
|
||||
created it. Orphan will not set the secret owner. This will
|
||||
result in the secret being orphaned and not deleted when the
|
||||
resource is deleted.'
|
||||
type: string
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret is located
|
||||
type: string
|
||||
secretType:
|
||||
default: Opaque
|
||||
description: 'The Kubernetes Secret type (experimental feature).
|
||||
More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
tls:
|
||||
properties:
|
||||
caRef:
|
||||
description: Reference to secret containing CA cert
|
||||
properties:
|
||||
key:
|
||||
description: The name of the secret property with the CA certificate
|
||||
value
|
||||
type: string
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The namespace where the Kubernetes Secret is
|
||||
located
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- authentication
|
||||
- dynamicSecret
|
||||
- leaseRevocationPolicy
|
||||
- leaseTTL
|
||||
- managedSecretReference
|
||||
type: object
|
||||
status:
|
||||
description: InfisicalDynamicSecretStatus defines the observed state of
|
||||
InfisicalDynamicSecret.
|
||||
properties:
|
||||
dynamicSecretId:
|
||||
type: string
|
||||
lease:
|
||||
properties:
|
||||
creationTimestamp:
|
||||
format: date-time
|
||||
type: string
|
||||
expiresAt:
|
||||
format: date-time
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
version:
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- creationTimestamp
|
||||
- expiresAt
|
||||
- id
|
||||
- version
|
||||
type: object
|
||||
maxTTL:
|
||||
description: The MaxTTL can be null, if it's null, there's no max
|
||||
TTL and we should never have to renew.
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -4,6 +4,7 @@
|
||||
resources:
|
||||
- bases/secrets.infisical.com_infisicalsecrets.yaml
|
||||
- bases/secrets.infisical.com_infisicalpushsecrets.yaml
|
||||
- bases/secrets.infisical.com_infisicaldynamicsecrets.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizeresource
|
||||
|
||||
patchesStrategicMerge:
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# permissions for end users to edit infisicaldynamicsecrets.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: infisicaldynamicsecret-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -0,0 +1,23 @@
|
||||
# permissions for end users to view infisicaldynamicsecrets.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: infisicaldynamicsecret-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -44,6 +44,32 @@ rules:
|
||||
- list
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
apiVersion: secrets.infisical.com/v1alpha1
|
||||
kind: InfisicalPushSecret
|
||||
metadata:
|
||||
name: infisical-push-secret-demo
|
||||
name: infisical-api-secret-sample-push
|
||||
spec:
|
||||
resyncInterval: 1m
|
||||
hostAPI: https://app.infisical.com/api
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/constants"
|
||||
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerhelpers"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/util"
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
// InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object
|
||||
type InfisicalDynamicSecretReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
BaseLogger logr.Logger
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/finalizers,verbs=update
|
||||
|
||||
var infisicalDynamicSecretsResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables)
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) GetLogger(req ctrl.Request) logr.Logger {
|
||||
return r.BaseLogger.WithValues("infisicaldynamicsecret", req.NamespacedName)
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
|
||||
logger := r.GetLogger(req)
|
||||
|
||||
var infisicalDynamicSecretCRD secretsv1alpha1.InfisicalDynamicSecret
|
||||
requeueTime := time.Second * 5
|
||||
|
||||
err := r.Get(ctx, req.NamespacedName, &infisicalDynamicSecretCRD)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
logger.Info("Infisical Dynamic Secret CRD not found")
|
||||
return ctrl.Result{
|
||||
Requeue: false,
|
||||
}, nil
|
||||
} else {
|
||||
logger.Error(err, "Unable to fetch Infisical Dynamic Secret CRD from cluster")
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Add finalizer if it doesn't exist
|
||||
if !controllerutil.ContainsFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) {
|
||||
controllerutil.AddFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME)
|
||||
if err := r.Update(ctx, &infisicalDynamicSecretCRD); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it's being deleted
|
||||
if !infisicalDynamicSecretCRD.DeletionTimestamp.IsZero() {
|
||||
logger.Info("Handling deletion of InfisicalDynamicSecret")
|
||||
if controllerutil.ContainsFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) {
|
||||
// We remove finalizers before running deletion logic to be completely safe from stuck resources
|
||||
infisicalDynamicSecretCRD.ObjectMeta.Finalizers = []string{}
|
||||
if err := r.Update(ctx, &infisicalDynamicSecretCRD); err != nil {
|
||||
logger.Error(err, fmt.Sprintf("Error removing finalizers from InfisicalDynamicSecret %s", infisicalDynamicSecretCRD.Name))
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
err := r.HandleLeaseRevocation(ctx, logger, infisicalDynamicSecretCRD)
|
||||
|
||||
if infisicalDynamicSecretsResourceVariablesMap != nil {
|
||||
if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecretCRD.GetUID())]; ok {
|
||||
rv.CancelCtx()
|
||||
delete(infisicalDynamicSecretsResourceVariablesMap, string(infisicalDynamicSecretCRD.GetUID()))
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err // Even if this fails, we still want to delete the CRD
|
||||
}
|
||||
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// Get modified/default config
|
||||
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client)
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if infisicalDynamicSecretCRD.Spec.HostAPI == "" {
|
||||
api.API_HOST_URL = infisicalConfig["hostAPI"]
|
||||
} else {
|
||||
api.API_HOST_URL = util.AppendAPIEndpoint(infisicalDynamicSecretCRD.Spec.HostAPI)
|
||||
}
|
||||
|
||||
if infisicalDynamicSecretCRD.Spec.TLS.CaRef.SecretName != "" {
|
||||
api.API_CA_CERTIFICATE, err = r.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalDynamicSecretCRD)
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to fetch CA certificate. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
logger.Info("Using custom CA certificate...")
|
||||
} else {
|
||||
api.API_CA_CERTIFICATE = ""
|
||||
}
|
||||
|
||||
nextReconcile, err := r.ReconcileInfisicalDynamicSecret(ctx, logger, infisicalDynamicSecretCRD)
|
||||
// r.SetSuccessfullyReconciledConditions(ctx, &infisicalDynamicSecretCRD, err)
|
||||
|
||||
if err == nil && nextReconcile.Seconds() >= 5 {
|
||||
requeueTime = nextReconcile
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile Infisical Push Secret. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
_, err = controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalDynamicSecretCRD.Spec.ManagedSecretReference)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Sync again after the specified time
|
||||
logger.Info(fmt.Sprintf("Next reconciliation in [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
|
||||
// Custom predicate that allows both spec changes and deletions
|
||||
specChangeOrDelete := predicate.Funcs{
|
||||
UpdateFunc: func(e event.UpdateEvent) bool {
|
||||
// Only reconcile if spec/generation changed
|
||||
|
||||
isSpecOrGenerationChange := e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration()
|
||||
|
||||
if isSpecOrGenerationChange {
|
||||
if infisicalDynamicSecretsResourceVariablesMap != nil {
|
||||
if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(e.ObjectNew.GetUID())]; ok {
|
||||
rv.CancelCtx()
|
||||
delete(infisicalDynamicSecretsResourceVariablesMap, string(e.ObjectNew.GetUID()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isSpecOrGenerationChange
|
||||
},
|
||||
DeleteFunc: func(e event.DeleteEvent) bool {
|
||||
// Always reconcile on deletion
|
||||
|
||||
if infisicalDynamicSecretsResourceVariablesMap != nil {
|
||||
if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(e.Object.GetUID())]; ok {
|
||||
rv.CancelCtx()
|
||||
delete(infisicalDynamicSecretsResourceVariablesMap, string(e.Object.GetUID()))
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
CreateFunc: func(e event.CreateEvent) bool {
|
||||
// Reconcile on creation
|
||||
return true
|
||||
},
|
||||
GenericFunc: func(e event.GenericEvent) bool {
|
||||
// Ignore generic events
|
||||
return false
|
||||
},
|
||||
}
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&secretsv1alpha1.InfisicalDynamicSecret{}, builder.WithPredicates(
|
||||
specChangeOrDelete,
|
||||
)).
|
||||
Complete(r)
|
||||
}
|
||||
@@ -0,0 +1,446 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/constants"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/util"
|
||||
"github.com/go-logr/logr"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
infisicalSdk "github.com/infisical/go-sdk"
|
||||
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) createInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, versionAnnotationValue string) error {
|
||||
secretType := infisicalDynamicSecret.Spec.ManagedSecretReference.SecretType
|
||||
|
||||
// copy labels and annotations from InfisicalSecret CRD
|
||||
labels := map[string]string{}
|
||||
for k, v := range infisicalDynamicSecret.Labels {
|
||||
labels[k] = v
|
||||
}
|
||||
|
||||
annotations := map[string]string{}
|
||||
systemPrefixes := []string{"kubectl.kubernetes.io/", "kubernetes.io/", "k8s.io/", "helm.sh/"}
|
||||
for k, v := range infisicalDynamicSecret.Annotations {
|
||||
isSystem := false
|
||||
for _, prefix := range systemPrefixes {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
isSystem = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isSystem {
|
||||
annotations[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
annotations[constants.SECRET_VERSION_ANNOTATION] = versionAnnotationValue
|
||||
|
||||
// create a new secret as specified by the managed secret spec of CRD
|
||||
newKubeSecretInstance := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretName,
|
||||
Namespace: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretNamespace,
|
||||
Annotations: annotations,
|
||||
Labels: labels,
|
||||
},
|
||||
Type: corev1.SecretType(secretType),
|
||||
}
|
||||
|
||||
if infisicalDynamicSecret.Spec.ManagedSecretReference.CreationPolicy == "Owner" {
|
||||
// Set InfisicalSecret instance as the owner and controller of the managed secret
|
||||
err := ctrl.SetControllerReference(&infisicalDynamicSecret, newKubeSecretInstance, r.Scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := r.Client.Create(ctx, newKubeSecretInstance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create the managed Kubernetes secret : %w", err)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Successfully created a managed Kubernetes secret. [type: %s]", secretType))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) handleAuthentication(ctx context.Context, infisicalSecret v1alpha1.InfisicalDynamicSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error) {
|
||||
authStrategies := map[util.AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd util.SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error){
|
||||
util.AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: util.HandleUniversalAuth,
|
||||
util.AuthStrategy.KUBERNETES_MACHINE_IDENTITY: util.HandleKubernetesAuth,
|
||||
util.AuthStrategy.AWS_IAM_MACHINE_IDENTITY: util.HandleAwsIamAuth,
|
||||
util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth,
|
||||
util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth,
|
||||
util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth,
|
||||
}
|
||||
|
||||
for authStrategy, authHandler := range authStrategies {
|
||||
authDetails, err := authHandler(ctx, r.Client, util.SecretAuthInput{
|
||||
Secret: infisicalSecret,
|
||||
Type: util.SecretCrd.INFISICAL_DYNAMIC_SECRET,
|
||||
}, infisicalClient)
|
||||
|
||||
if err == nil {
|
||||
return authDetails, nil
|
||||
}
|
||||
|
||||
if !errors.Is(err, util.ErrAuthNotApplicable) {
|
||||
return util.AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err)
|
||||
}
|
||||
}
|
||||
|
||||
return util.AuthenticationDetails{}, fmt.Errorf("no authentication method provided")
|
||||
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalDynamicSecret) (caCertificate string, err error) {
|
||||
|
||||
caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
||||
Namespace: infisicalSecret.Spec.TLS.CaRef.SecretNamespace,
|
||||
Name: infisicalSecret.Spec.TLS.CaRef.SecretName,
|
||||
})
|
||||
|
||||
if k8Errors.IsNotFound(err) {
|
||||
return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err)
|
||||
}
|
||||
|
||||
caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalSecret.Spec.TLS.CaRef.SecretKey])
|
||||
|
||||
return caCertificateFromSecret, nil
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) getResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret) util.ResourceVariables {
|
||||
|
||||
var resourceVariables util.ResourceVariables
|
||||
|
||||
if _, ok := infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)]; !ok {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
client := infisicalSdk.NewInfisicalClient(ctx, infisicalSdk.Config{
|
||||
SiteUrl: api.API_HOST_URL,
|
||||
CaCertificate: api.API_CA_CERTIFICATE,
|
||||
UserAgent: api.USER_AGENT_NAME,
|
||||
})
|
||||
|
||||
infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)] = util.ResourceVariables{
|
||||
InfisicalClient: client,
|
||||
CancelCtx: cancel,
|
||||
AuthDetails: util.AuthenticationDetails{},
|
||||
}
|
||||
|
||||
resourceVariables = infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)]
|
||||
|
||||
} else {
|
||||
resourceVariables = infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)]
|
||||
}
|
||||
|
||||
return resourceVariables
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) CreateDynamicSecretLease(ctx context.Context, logger logr.Logger, infisicalClient infisicalSdk.InfisicalClientInterface, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, destination *corev1.Secret) error {
|
||||
project, err := util.GetProjectByID(infisicalClient.Auth().GetAccessToken(), infisicalDynamicSecret.Spec.DynamicSecret.ProjectID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := infisicalSdk.CreateDynamicSecretLeaseOptions{
|
||||
DynamicSecretName: infisicalDynamicSecret.Spec.DynamicSecret.SecretName,
|
||||
ProjectSlug: project.Slug,
|
||||
SecretPath: infisicalDynamicSecret.Spec.DynamicSecret.SecretPath,
|
||||
EnvironmentSlug: infisicalDynamicSecret.Spec.DynamicSecret.EnvironmentSlug,
|
||||
}
|
||||
|
||||
if infisicalDynamicSecret.Spec.LeaseTTL != "" {
|
||||
request.TTL = infisicalDynamicSecret.Spec.LeaseTTL
|
||||
}
|
||||
|
||||
leaseData, dynamicSecret, lease, err := infisicalClient.DynamicSecrets().Leases().Create(request)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create lease [err=%s]", err)
|
||||
}
|
||||
|
||||
newLeaseStatus := &v1alpha1.InfisicalDynamicSecretLease{
|
||||
ID: lease.Id,
|
||||
ExpiresAt: metav1.NewTime(lease.ExpireAt),
|
||||
CreationTimestamp: metav1.NewTime(time.Now()),
|
||||
Version: int64(lease.Version),
|
||||
}
|
||||
|
||||
infisicalDynamicSecret.Status.DynamicSecretID = dynamicSecret.Id
|
||||
infisicalDynamicSecret.Status.MaxTTL = dynamicSecret.MaxTTL
|
||||
infisicalDynamicSecret.Status.Lease = newLeaseStatus
|
||||
|
||||
// write the leaseData to the destination secret
|
||||
destinationData := map[string]string{}
|
||||
|
||||
for key, value := range leaseData {
|
||||
if strValue, ok := value.(string); ok {
|
||||
destinationData[key] = strValue
|
||||
} else {
|
||||
return fmt.Errorf("unable to convert value to string for key %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
destination.StringData = destinationData
|
||||
destination.Annotations[constants.SECRET_VERSION_ANNOTATION] = fmt.Sprintf("%s-%d", lease.Id, lease.Version)
|
||||
|
||||
if err := r.Client.Update(ctx, destination); err != nil {
|
||||
return fmt.Errorf("unable to update destination secret [err=%s]", err)
|
||||
}
|
||||
|
||||
if err := r.Client.Status().Update(ctx, infisicalDynamicSecret); err != nil {
|
||||
return fmt.Errorf("unable to update InfisicalDynamicSecret status [err=%s]", err)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("New lease successfully created [leaseId=%s]", lease.Id))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) RenewDynamicSecretLease(ctx context.Context, logger logr.Logger, infisicalClient infisicalSdk.InfisicalClientInterface, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, destination *corev1.Secret) error {
|
||||
project, err := util.GetProjectByID(infisicalClient.Auth().GetAccessToken(), infisicalDynamicSecret.Spec.DynamicSecret.ProjectID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := infisicalSdk.RenewDynamicSecretLeaseOptions{
|
||||
LeaseId: infisicalDynamicSecret.Status.Lease.ID,
|
||||
ProjectSlug: project.Slug,
|
||||
SecretPath: infisicalDynamicSecret.Spec.DynamicSecret.SecretPath,
|
||||
EnvironmentSlug: infisicalDynamicSecret.Spec.DynamicSecret.EnvironmentSlug,
|
||||
}
|
||||
|
||||
if infisicalDynamicSecret.Spec.LeaseTTL != "" {
|
||||
request.TTL = infisicalDynamicSecret.Spec.LeaseTTL
|
||||
}
|
||||
|
||||
lease, err := infisicalClient.DynamicSecrets().Leases().RenewById(request)
|
||||
|
||||
if err != nil {
|
||||
|
||||
if strings.Contains(err.Error(), "TTL cannot be larger than max ttl") || // Case 1: TTL is larger than the max TTL
|
||||
strings.Contains(err.Error(), "Dynamic secret lease with ID") { // Case 2: The lease has already expired and has been deleted
|
||||
return constants.ErrInvalidLease
|
||||
}
|
||||
|
||||
return fmt.Errorf("unable to renew lease [err=%s]", err)
|
||||
}
|
||||
|
||||
infisicalDynamicSecret.Status.Lease.ExpiresAt = metav1.NewTime(lease.ExpireAt)
|
||||
|
||||
// update the infisicalDynamicSecret status
|
||||
if err := r.Client.Status().Update(ctx, infisicalDynamicSecret); err != nil {
|
||||
return fmt.Errorf("unable to update InfisicalDynamicSecret status [err=%s]", err)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Lease successfully renewed [leaseId=%s]", lease.Id))
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) updateResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, resourceVariables util.ResourceVariables) {
|
||||
infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)] = resourceVariables
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Context, logger logr.Logger, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret) error {
|
||||
if infisicalDynamicSecret.Spec.LeaseRevocationPolicy != string(constants.DYNAMIC_SECRET_LEASE_REVOCATION_POLICY_ENABLED) {
|
||||
return nil
|
||||
}
|
||||
|
||||
resourceVariables := r.getResourceVariables(infisicalDynamicSecret)
|
||||
infisicalClient := resourceVariables.InfisicalClient
|
||||
|
||||
logger.Info("Authenticating for lease revocation")
|
||||
authDetails, err := r.handleAuthentication(ctx, infisicalDynamicSecret, infisicalClient)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to authenticate for lease revocation [err=%s]", err)
|
||||
}
|
||||
|
||||
r.updateResourceVariables(infisicalDynamicSecret, util.ResourceVariables{
|
||||
InfisicalClient: infisicalClient,
|
||||
CancelCtx: resourceVariables.CancelCtx,
|
||||
AuthDetails: authDetails,
|
||||
})
|
||||
|
||||
if infisicalDynamicSecret.Status.Lease == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
project, err := util.GetProjectByID(infisicalClient.Auth().GetAccessToken(), infisicalDynamicSecret.Spec.DynamicSecret.ProjectID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
infisicalClient.DynamicSecrets().Leases().DeleteById(infisicalSdk.DeleteDynamicSecretLeaseOptions{
|
||||
LeaseId: infisicalDynamicSecret.Status.Lease.ID,
|
||||
ProjectSlug: project.Slug,
|
||||
SecretPath: infisicalDynamicSecret.Spec.DynamicSecret.SecretPath,
|
||||
EnvironmentSlug: infisicalDynamicSecret.Spec.DynamicSecret.EnvironmentSlug,
|
||||
})
|
||||
|
||||
// update the destination data to remove the lease data
|
||||
destination, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
||||
Name: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretName,
|
||||
Namespace: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretNamespace,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to fetch destination secret [err=%s]", err)
|
||||
}
|
||||
|
||||
destination.Data = map[string][]byte{}
|
||||
|
||||
if err := r.Client.Update(ctx, destination); err != nil {
|
||||
return fmt.Errorf("unable to update destination secret [err=%s]", err)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Lease successfully revoked [leaseId=%s]", infisicalDynamicSecret.Status.Lease.ID))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret) (time.Duration, error) {
|
||||
|
||||
resourceVariables := r.getResourceVariables(infisicalDynamicSecret)
|
||||
infisicalClient := resourceVariables.InfisicalClient
|
||||
cancelCtx := resourceVariables.CancelCtx
|
||||
authDetails := resourceVariables.AuthDetails
|
||||
|
||||
defaultNextReconcile := 5 * time.Second
|
||||
nextReconcile := defaultNextReconcile
|
||||
|
||||
var err error
|
||||
|
||||
if authDetails.AuthStrategy == "" {
|
||||
logger.Info("No authentication strategy found. Attempting to authenticate")
|
||||
authDetails, err = r.handleAuthentication(ctx, infisicalDynamicSecret, infisicalClient)
|
||||
|
||||
if err != nil {
|
||||
return nextReconcile, fmt.Errorf("unable to authenticate [err=%s]", err)
|
||||
}
|
||||
|
||||
r.updateResourceVariables(infisicalDynamicSecret, util.ResourceVariables{
|
||||
InfisicalClient: infisicalClient,
|
||||
CancelCtx: cancelCtx,
|
||||
AuthDetails: authDetails,
|
||||
})
|
||||
}
|
||||
|
||||
destination, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
||||
Name: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretName,
|
||||
Namespace: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretNamespace,
|
||||
})
|
||||
|
||||
if err != nil && !k8Errors.IsNotFound(err) {
|
||||
annotationValue := ""
|
||||
if infisicalDynamicSecret.Status.Lease != nil {
|
||||
annotationValue = fmt.Sprintf("%s-%d", infisicalDynamicSecret.Status.Lease.ID, infisicalDynamicSecret.Status.Lease.Version)
|
||||
}
|
||||
r.createInfisicalManagedKubeSecret(ctx, logger, infisicalDynamicSecret, annotationValue)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if k8Errors.IsNotFound(err) {
|
||||
return nextReconcile, fmt.Errorf("destination secret not found")
|
||||
}
|
||||
|
||||
return nextReconcile, fmt.Errorf("unable to fetch destination secret")
|
||||
}
|
||||
|
||||
if infisicalDynamicSecret.Status.Lease == nil {
|
||||
r.CreateDynamicSecretLease(ctx, logger, infisicalClient, &infisicalDynamicSecret, destination)
|
||||
} else {
|
||||
now := time.Now()
|
||||
leaseExpiresAt := infisicalDynamicSecret.Status.Lease.ExpiresAt.Time
|
||||
|
||||
// Calculate from creation to expiration
|
||||
originalLeaseDuration := leaseExpiresAt.Sub(infisicalDynamicSecret.Status.Lease.CreationTimestamp.Time)
|
||||
|
||||
// 30% of the original duration (if the TTL has 30% or less of its time left, renew)
|
||||
renewalThreshold := originalLeaseDuration * 30 / 100
|
||||
timeUntilExpiration := time.Until(leaseExpiresAt)
|
||||
|
||||
nextReconcile = timeUntilExpiration / 2
|
||||
|
||||
// Max TTL
|
||||
if infisicalDynamicSecret.Status.MaxTTL != "" {
|
||||
maxTTLDuration, err := util.ConvertIntervalToDuration(infisicalDynamicSecret.Status.MaxTTL)
|
||||
if err != nil {
|
||||
return defaultNextReconcile, fmt.Errorf("unable to parse MaxTTL duration: %w", err)
|
||||
}
|
||||
|
||||
// Calculate when this dynamic secret will hit its max TTL
|
||||
maxTTLExpirationTime := infisicalDynamicSecret.Status.Lease.CreationTimestamp.Add(maxTTLDuration)
|
||||
|
||||
// Calculate remaining time until max TTL
|
||||
timeUntilMaxTTL := maxTTLExpirationTime.Sub(now)
|
||||
maxTTLThreshold := maxTTLDuration * 40 / 100
|
||||
|
||||
// If we have less than 40% of max TTL remaining or have exceeded it, create new lease
|
||||
if timeUntilMaxTTL <= maxTTLThreshold || now.After(maxTTLExpirationTime) {
|
||||
logger.Info(fmt.Sprintf("Approaching or exceeded max TTL [timeUntilMaxTTL=%v] [maxTTLThreshold=%v], creating new lease...",
|
||||
timeUntilMaxTTL,
|
||||
maxTTLThreshold))
|
||||
|
||||
err := r.CreateDynamicSecretLease(ctx, logger, infisicalClient, &infisicalDynamicSecret, destination)
|
||||
return defaultNextReconcile, err // Short requeue after creation
|
||||
}
|
||||
}
|
||||
|
||||
// Fail-safe: If the lease has expired we create a new dynamic secret directly.
|
||||
if now.After(leaseExpiresAt) {
|
||||
logger.Info("Lease has expired, creating new lease...")
|
||||
err = r.CreateDynamicSecretLease(ctx, logger, infisicalClient, &infisicalDynamicSecret, destination)
|
||||
return defaultNextReconcile, err // Short requeue after creation
|
||||
}
|
||||
|
||||
if timeUntilExpiration < renewalThreshold {
|
||||
logger.Info(fmt.Sprintf("Lease renewal needed [leaseId=%s] [timeUntilExpiration=%v] [threshold=%v]",
|
||||
infisicalDynamicSecret.Status.Lease.ID,
|
||||
timeUntilExpiration,
|
||||
renewalThreshold))
|
||||
|
||||
err = r.RenewDynamicSecretLease(ctx, logger, infisicalClient, &infisicalDynamicSecret, destination)
|
||||
|
||||
if err == constants.ErrInvalidLease {
|
||||
logger.Info("Failed to renew expired lease, creating new lease...")
|
||||
err = r.CreateDynamicSecretLease(ctx, logger, infisicalClient, &infisicalDynamicSecret, destination)
|
||||
}
|
||||
return defaultNextReconcile, err // Short requeue after renewal/creation
|
||||
|
||||
} else {
|
||||
logger.Info(fmt.Sprintf("Lease renewal not needed yet [leaseId=%s] [timeUntilExpiration=%v] [threshold=%v]",
|
||||
infisicalDynamicSecret.Status.Lease.ID,
|
||||
timeUntilExpiration,
|
||||
renewalThreshold))
|
||||
}
|
||||
|
||||
// Small buffer (20% of the calculated time) to ensure we don't cut it too close
|
||||
nextReconcile = nextReconcile * 8 / 10
|
||||
|
||||
// Minimum and maximum bounds for the reconcile interval (5 min max, 5 min minimum)
|
||||
nextReconcile = max(5*time.Second, min(nextReconcile, 5*time.Minute))
|
||||
}
|
||||
|
||||
return nextReconcile, nil
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/constants"
|
||||
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerutil"
|
||||
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerhelpers"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/util"
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
@@ -105,7 +105,7 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.
|
||||
|
||||
if infisicalPushSecretCRD.Spec.ResyncInterval != "" {
|
||||
|
||||
duration, err := util.ConvertResyncIntervalToDuration(infisicalPushSecretCRD.Spec.ResyncInterval)
|
||||
duration, err := util.ConvertIntervalToDuration(infisicalPushSecretCRD.Spec.ResyncInterval)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to convert resync interval to duration. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
@@ -141,7 +141,7 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.
|
||||
if infisicalPushSecretCRD.Spec.HostAPI == "" {
|
||||
api.API_HOST_URL = infisicalConfig["hostAPI"]
|
||||
} else {
|
||||
api.API_HOST_URL = infisicalPushSecretCRD.Spec.HostAPI
|
||||
api.API_HOST_URL = util.AppendAPIEndpoint(infisicalPushSecretCRD.Spec.HostAPI)
|
||||
}
|
||||
|
||||
if infisicalPushSecretCRD.Spec.TLS.CaRef.SecretName != "" {
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerutil"
|
||||
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerhelpers"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/util"
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
@@ -110,7 +110,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
||||
if infisicalSecretCRD.Spec.HostAPI == "" {
|
||||
api.API_HOST_URL = infisicalConfig["hostAPI"]
|
||||
} else {
|
||||
api.API_HOST_URL = infisicalSecretCRD.Spec.HostAPI
|
||||
api.API_HOST_URL = util.AppendAPIEndpoint(infisicalSecretCRD.Spec.HostAPI)
|
||||
}
|
||||
|
||||
if infisicalSecretCRD.Spec.TLS.CaRef.SecretName != "" {
|
||||
@@ -138,7 +138,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
||||
}, nil
|
||||
}
|
||||
|
||||
numDeployments, err := r.ReconcileDeploymentsWithManagedSecrets(ctx, logger, infisicalSecretCRD)
|
||||
numDeployments, err := controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalSecretCRD.Spec.ManagedSecretReference)
|
||||
r.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err)
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
|
||||
@@ -3,7 +3,7 @@ module github.com/Infisical/infisical/k8-operator
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/infisical/go-sdk v0.4.1
|
||||
github.com/infisical/go-sdk v0.4.4
|
||||
github.com/onsi/ginkgo/v2 v2.6.0
|
||||
github.com/onsi/gomega v1.24.1
|
||||
k8s.io/apimachinery v0.26.1
|
||||
@@ -54,7 +54,7 @@ require (
|
||||
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/logr v1.4.2
|
||||
github.com/go-logr/zapr v1.2.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
|
||||
@@ -219,6 +219,10 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/infisical/go-sdk v0.4.1 h1:ZeLyc2+2TeIaw9odjxR3ipQqYzVSMOnd8/RaqyUNvBg=
|
||||
github.com/infisical/go-sdk v0.4.1/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M=
|
||||
github.com/infisical/go-sdk v0.4.3 h1:O5ZJ2eCBAZDE9PIAfBPq9Utb2CgQKrhmj9R0oFTRu4U=
|
||||
github.com/infisical/go-sdk v0.4.3/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M=
|
||||
github.com/infisical/go-sdk v0.4.4 h1:Z4CBzxfhiY6ikjRimOEeyEEnb3QT/BKw3OzNFH7Pe+U=
|
||||
github.com/infisical/go-sdk v0.4.4/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright 2022.
|
||||
|
||||
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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
)
|
||||
|
||||
// InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object
|
||||
type InfisicalDynamicSecretReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/finalizers,verbs=update
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
// TODO(user): Modify the Reconcile function to compare the state specified by
|
||||
// the InfisicalDynamicSecret object against the actual cluster state, and then
|
||||
// perform operations to make the cluster state reflect the state specified by
|
||||
// the user.
|
||||
//
|
||||
// For more details, check Reconcile and its Result here:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/reconcile
|
||||
func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
_ = log.FromContext(ctx)
|
||||
|
||||
// TODO(user): your logic here
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *InfisicalDynamicSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&secretsv1alpha1.InfisicalDynamicSecret{}).
|
||||
Named("infisicaldynamicsecret").
|
||||
Complete(r)
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
infisicalDynamicSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicaldynamicsecret"
|
||||
infisicalPushSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicalpushsecret"
|
||||
infisicalSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicalsecret"
|
||||
//+kubebuilder:scaffold:imports
|
||||
@@ -92,6 +93,15 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&infisicalDynamicSecretController.InfisicalDynamicSecretReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
BaseLogger: ctrl.Log,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "InfisicalDynamicSecret")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
//+kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
|
||||
@@ -125,3 +125,24 @@ func CallGetServiceAccountKeysV2(httpClient *resty.Client, request GetServiceAcc
|
||||
|
||||
return serviceAccountKeysResponse, nil
|
||||
}
|
||||
|
||||
func CallGetProjectByID(httpClient *resty.Client, request GetProjectByIDRequest) (GetProjectByIDResponse, error) {
|
||||
|
||||
var projectResponse GetProjectByIDResponse
|
||||
|
||||
response, err := httpClient.
|
||||
R().SetResult(&projectResponse).
|
||||
SetHeader("User-Agent", USER_AGENT_NAME).
|
||||
Get(fmt.Sprintf("%s/v1/workspace/%s", API_HOST_URL, request.ProjectID))
|
||||
|
||||
if err != nil {
|
||||
return GetProjectByIDResponse{}, fmt.Errorf("CallGetProject: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return GetProjectByIDResponse{}, fmt.Errorf("CallGetProject: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return projectResponse, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package api
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/packages/model"
|
||||
)
|
||||
|
||||
type GetEncryptedWorkspaceKeyRequest struct {
|
||||
WorkspaceId string `json:"workspaceId"`
|
||||
@@ -194,3 +198,11 @@ type ServiceAccountKey struct {
|
||||
type GetServiceAccountKeysResponse struct {
|
||||
ServiceAccountKeys []ServiceAccountKey `json:"serviceAccountKeys"`
|
||||
}
|
||||
|
||||
type GetProjectByIDRequest struct {
|
||||
ProjectID string
|
||||
}
|
||||
|
||||
type GetProjectByIDResponse struct {
|
||||
Project model.Project `json:"workspace"`
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package constants
|
||||
|
||||
import "errors"
|
||||
|
||||
const SERVICE_ACCOUNT_ACCESS_KEY = "serviceAccountAccessKey"
|
||||
const SERVICE_ACCOUNT_PUBLIC_KEY = "serviceAccountPublicKey"
|
||||
const SERVICE_ACCOUNT_PRIVATE_KEY = "serviceAccountPrivateKey"
|
||||
@@ -14,6 +16,7 @@ const OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE = "infisical-operator-system"
|
||||
const INFISICAL_DOMAIN = "https://app.infisical.com/api"
|
||||
|
||||
const INFISICAL_PUSH_SECRET_FINALIZER_NAME = "pushsecret.secrets.infisical.com/finalizer"
|
||||
const INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME = "dynamicsecret.secrets.infisical.com/finalizer"
|
||||
|
||||
type PushSecretReplacePolicy string
|
||||
type PushSecretDeletionPolicy string
|
||||
@@ -22,3 +25,11 @@ const (
|
||||
PUSH_SECRET_REPLACE_POLICY_ENABLED PushSecretReplacePolicy = "Replace"
|
||||
PUSH_SECRET_DELETE_POLICY_ENABLED PushSecretDeletionPolicy = "Delete"
|
||||
)
|
||||
|
||||
type DynamicSecretLeaseRevocationPolicy string
|
||||
|
||||
const (
|
||||
DYNAMIC_SECRET_LEASE_REVOCATION_POLICY_ENABLED DynamicSecretLeaseRevocationPolicy = "Revoke"
|
||||
)
|
||||
|
||||
var ErrInvalidLease = errors.New("invalid dynamic secret lease")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package controllers
|
||||
package controllerhelpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -10,27 +10,30 @@ import (
|
||||
"github.com/go-logr/logr"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
controllerClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
const DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX = "secrets.infisical.com/managed-secret"
|
||||
const AUTO_RELOAD_DEPLOYMENT_ANNOTATION = "secrets.infisical.com/auto-reload" // needs to be set to true for a deployment to start auto redeploying
|
||||
|
||||
func (r *InfisicalSecretReconciler) ReconcileDeploymentsWithManagedSecrets(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret) (int, error) {
|
||||
func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecret v1alpha1.ManagedKubeSecretConfig) (int, error) {
|
||||
listOfDeployments := &v1.DeploymentList{}
|
||||
err := r.Client.List(ctx, listOfDeployments, &client.ListOptions{Namespace: infisicalSecret.Spec.ManagedSecretReference.SecretNamespace})
|
||||
|
||||
err := client.List(ctx, listOfDeployments, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unable to get deployments in the [namespace=%v] [err=%v]", infisicalSecret.Spec.ManagedSecretReference.SecretNamespace, err)
|
||||
return 0, fmt.Errorf("unable to get deployments in the [namespace=%v] [err=%v]", managedSecret.SecretNamespace, err)
|
||||
}
|
||||
|
||||
managedKubeSecretNameAndNamespace := types.NamespacedName{
|
||||
Namespace: infisicalSecret.Spec.ManagedSecretReference.SecretNamespace,
|
||||
Name: infisicalSecret.Spec.ManagedSecretReference.SecretName,
|
||||
Namespace: managedSecret.SecretNamespace,
|
||||
Name: managedSecret.SecretName,
|
||||
}
|
||||
|
||||
managedKubeSecret := &corev1.Secret{}
|
||||
err = r.Client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret)
|
||||
err = client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment: %v", err)
|
||||
}
|
||||
@@ -39,12 +42,12 @@ func (r *InfisicalSecretReconciler) ReconcileDeploymentsWithManagedSecrets(ctx c
|
||||
// Iterate over the deployments and check if they use the managed secret
|
||||
for _, deployment := range listOfDeployments.Items {
|
||||
deployment := deployment
|
||||
if deployment.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && r.IsDeploymentUsingManagedSecret(deployment, infisicalSecret) {
|
||||
if deployment.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && IsDeploymentUsingManagedSecret(deployment, managedSecret) {
|
||||
// Start a goroutine to reconcile the deployment
|
||||
wg.Add(1)
|
||||
go func(d v1.Deployment, s corev1.Secret) {
|
||||
go func(deployment v1.Deployment, managedSecret corev1.Secret) {
|
||||
defer wg.Done()
|
||||
if err := r.ReconcileDeployment(ctx, logger, d, s); err != nil {
|
||||
if err := ReconcileDeployment(ctx, client, logger, deployment, managedSecret); err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile deployment with [name=%v]. Will try next requeue", deployment.ObjectMeta.Name))
|
||||
}
|
||||
}(deployment, *managedKubeSecret)
|
||||
@@ -57,8 +60,8 @@ func (r *InfisicalSecretReconciler) ReconcileDeploymentsWithManagedSecrets(ctx c
|
||||
}
|
||||
|
||||
// Check if the deployment uses managed secrets
|
||||
func (r *InfisicalSecretReconciler) IsDeploymentUsingManagedSecret(deployment v1.Deployment, infisicalSecret v1alpha1.InfisicalSecret) bool {
|
||||
managedSecretName := infisicalSecret.Spec.ManagedSecretReference.SecretName
|
||||
func IsDeploymentUsingManagedSecret(deployment v1.Deployment, managedSecret v1alpha1.ManagedKubeSecretConfig) bool {
|
||||
managedSecretName := managedSecret.SecretName
|
||||
for _, container := range deployment.Spec.Template.Spec.Containers {
|
||||
for _, envFrom := range container.EnvFrom {
|
||||
if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName {
|
||||
@@ -82,7 +85,7 @@ func (r *InfisicalSecretReconciler) IsDeploymentUsingManagedSecret(deployment v1
|
||||
|
||||
// This function ensures that a deployment is in sync with a Kubernetes secret by comparing their versions.
|
||||
// If the version of the secret is different from the version annotation on the deployment, the annotation is updated to trigger a restart of the deployment.
|
||||
func (r *InfisicalSecretReconciler) ReconcileDeployment(ctx context.Context, logger logr.Logger, deployment v1.Deployment, secret corev1.Secret) error {
|
||||
func ReconcileDeployment(ctx context.Context, client controllerClient.Client, logger logr.Logger, deployment v1.Deployment, secret corev1.Secret) error {
|
||||
annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name)
|
||||
annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION]
|
||||
|
||||
@@ -101,8 +104,41 @@ func (r *InfisicalSecretReconciler) ReconcileDeployment(ctx context.Context, log
|
||||
deployment.Annotations[annotationKey] = annotationValue
|
||||
deployment.Spec.Template.Annotations[annotationKey] = annotationValue
|
||||
|
||||
if err := r.Client.Update(ctx, &deployment); err != nil {
|
||||
if err := client.Update(ctx, &deployment); err != nil {
|
||||
return fmt.Errorf("failed to update deployment annotation: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) {
|
||||
// default key values
|
||||
defaultConfigMapData := make(map[string]string)
|
||||
defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN
|
||||
|
||||
kubeConfigMap := &corev1.ConfigMap{}
|
||||
err := client.Get(ctx, types.NamespacedName{
|
||||
Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE,
|
||||
Name: constants.OPERATOR_SETTINGS_CONFIGMAP_NAME,
|
||||
}, kubeConfigMap)
|
||||
|
||||
if err != nil {
|
||||
if k8Errors.IsNotFound(err) {
|
||||
kubeConfigMap = nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("GetConfigMapByNamespacedName: unable to fetch config map in [namespacedName=%s] [err=%s]", constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, err)
|
||||
}
|
||||
}
|
||||
|
||||
if kubeConfigMap == nil {
|
||||
return defaultConfigMapData, nil
|
||||
} else {
|
||||
for key, value := range defaultConfigMapData {
|
||||
_, exists := kubeConfigMap.Data[key]
|
||||
if !exists {
|
||||
kubeConfigMap.Data[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return kubeConfigMap.Data, nil
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package controllerhelpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/packages/constants"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) {
|
||||
// default key values
|
||||
defaultConfigMapData := make(map[string]string)
|
||||
defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN
|
||||
|
||||
kubeConfigMap := &corev1.ConfigMap{}
|
||||
err := client.Get(ctx, types.NamespacedName{
|
||||
Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE,
|
||||
Name: constants.OPERATOR_SETTINGS_CONFIGMAP_NAME,
|
||||
}, kubeConfigMap)
|
||||
|
||||
if err != nil {
|
||||
if k8Errors.IsNotFound(err) {
|
||||
kubeConfigMap = nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("GetConfigMapByNamespacedName: unable to fetch config map in [namespacedName=%s] [err=%s]", constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, err)
|
||||
}
|
||||
}
|
||||
|
||||
if kubeConfigMap == nil {
|
||||
return defaultConfigMapData, nil
|
||||
} else {
|
||||
for key, value := range defaultConfigMapData {
|
||||
_, exists := kubeConfigMap.Data[key]
|
||||
if !exists {
|
||||
kubeConfigMap.Data[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return kubeConfigMap.Data, nil
|
||||
}
|
||||
}
|
||||
@@ -28,3 +28,15 @@ type SecretTemplateOptions struct {
|
||||
Value string `json:"value"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
OrgID string `json:"orgId"`
|
||||
Environments []struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,11 +63,13 @@ var AuthStrategy = struct {
|
||||
type SecretCrdType string
|
||||
|
||||
var SecretCrd = struct {
|
||||
INFISICAL_SECRET SecretCrdType
|
||||
INFISICAL_PUSH_SECRET SecretCrdType
|
||||
INFISICAL_SECRET SecretCrdType
|
||||
INFISICAL_PUSH_SECRET SecretCrdType
|
||||
INFISICAL_DYNAMIC_SECRET SecretCrdType
|
||||
}{
|
||||
INFISICAL_SECRET: "INFISICAL_SECRET",
|
||||
INFISICAL_PUSH_SECRET: "INFISICAL_PUSH_SECRET",
|
||||
INFISICAL_SECRET: "INFISICAL_SECRET",
|
||||
INFISICAL_PUSH_SECRET: "INFISICAL_PUSH_SECRET",
|
||||
INFISICAL_DYNAMIC_SECRET: "INFISICAL_DYNAMIC_SECRET",
|
||||
}
|
||||
|
||||
type SecretAuthInput struct {
|
||||
@@ -107,6 +109,18 @@ func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, se
|
||||
CredentialsRef: infisicalPushSecret.Spec.Authentication.UniversalAuth.CredentialsRef,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
universalAuthSpec = v1alpha1.UniversalAuthDetails{
|
||||
CredentialsRef: infisicalDynamicSecret.Spec.Authentication.UniversalAuth.CredentialsRef,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
}
|
||||
|
||||
universalAuthKubeSecret, err := GetInfisicalUniversalAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{
|
||||
@@ -160,6 +174,22 @@ func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, s
|
||||
},
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
kubernetesAuthSpec = v1alpha1.KubernetesAuthDetails{
|
||||
IdentityID: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.IdentityID,
|
||||
ServiceAccountRef: v1alpha1.KubernetesServiceAccountRef{
|
||||
Namespace: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Namespace,
|
||||
Name: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Name,
|
||||
},
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
}
|
||||
|
||||
if kubernetesAuthSpec.IdentityID == "" {
|
||||
@@ -208,6 +238,18 @@ func HandleAwsIamAuth(ctx context.Context, reconcilerClient client.Client, secre
|
||||
IdentityID: infisicalPushSecret.Spec.Authentication.AwsIamAuth.IdentityID,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
awsIamAuthSpec = v1alpha1.AWSIamAuthDetails{
|
||||
IdentityID: infisicalDynamicSecret.Spec.Authentication.AwsIamAuth.IdentityID,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
}
|
||||
|
||||
if awsIamAuthSpec.IdentityID == "" {
|
||||
@@ -253,6 +295,19 @@ func HandleAzureAuth(ctx context.Context, reconcilerClient client.Client, secret
|
||||
Resource: infisicalPushSecret.Spec.Authentication.AzureAuth.Resource,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
azureAuthSpec = v1alpha1.AzureAuthDetails{
|
||||
IdentityID: infisicalDynamicSecret.Spec.Authentication.AzureAuth.IdentityID,
|
||||
Resource: infisicalDynamicSecret.Spec.Authentication.AzureAuth.Resource,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
}
|
||||
|
||||
if azureAuthSpec.IdentityID == "" {
|
||||
@@ -296,6 +351,18 @@ func HandleGcpIdTokenAuth(ctx context.Context, reconcilerClient client.Client, s
|
||||
IdentityID: infisicalPushSecret.Spec.Authentication.GcpIdTokenAuth.IdentityID,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
gcpIdTokenSpec = v1alpha1.GCPIdTokenAuthDetails{
|
||||
IdentityID: infisicalDynamicSecret.Spec.Authentication.GcpIdTokenAuth.IdentityID,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
}
|
||||
|
||||
if gcpIdTokenSpec.IdentityID == "" {
|
||||
@@ -340,6 +407,19 @@ func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secre
|
||||
ServiceAccountKeyFilePath: infisicalPushSecret.Spec.Authentication.GcpIamAuth.ServiceAccountKeyFilePath,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
|
||||
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||
|
||||
if !ok {
|
||||
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||
}
|
||||
|
||||
gcpIamSpec = v1alpha1.GcpIamAuthDetails{
|
||||
IdentityID: infisicalDynamicSecret.Spec.Authentication.GcpIamAuth.IdentityID,
|
||||
ServiceAccountKeyFilePath: infisicalDynamicSecret.Spec.Authentication.GcpIamAuth.ServiceAccountKeyFilePath,
|
||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||
}
|
||||
}
|
||||
|
||||
if gcpIamSpec.IdentityID == "" && gcpIamSpec.ServiceAccountKeyFilePath == "" {
|
||||
|
||||
@@ -3,10 +3,11 @@ package util
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ConvertResyncIntervalToDuration(resyncInterval string) (time.Duration, error) {
|
||||
func ConvertIntervalToDuration(resyncInterval string) (time.Duration, error) {
|
||||
length := len(resyncInterval)
|
||||
if length < 2 {
|
||||
return 0, fmt.Errorf("invalid format")
|
||||
@@ -38,3 +39,23 @@ func ConvertResyncIntervalToDuration(resyncInterval string) (time.Duration, erro
|
||||
return 0, fmt.Errorf("invalid time unit")
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertIntervalToTime(resyncInterval string) (time.Time, error) {
|
||||
duration, err := ConvertIntervalToDuration(resyncInterval)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
// Add duration to current time
|
||||
return time.Now().Add(duration), nil
|
||||
}
|
||||
|
||||
func AppendAPIEndpoint(address string) string {
|
||||
if strings.HasSuffix(address, "/api") {
|
||||
return address
|
||||
}
|
||||
if address[len(address)-1] == '/' {
|
||||
return address + "api"
|
||||
}
|
||||
return address + "/api"
|
||||
}
|
||||
27
k8-operator/packages/util/workspace.go
Normal file
27
k8-operator/packages/util/workspace.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||
"github.com/Infisical/infisical/k8-operator/packages/model"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
func GetProjectByID(accessToken string, projectId string) (model.Project, error) {
|
||||
|
||||
httpClient := resty.New()
|
||||
httpClient.
|
||||
SetAuthScheme("Bearer").
|
||||
SetAuthToken(accessToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
projectDetails, err := api.CallGetProjectByID(httpClient, api.GetProjectByIDRequest{
|
||||
ProjectID: projectId,
|
||||
})
|
||||
if err != nil {
|
||||
return model.Project{}, fmt.Errorf("unable to get project by slug. [err=%v]", err)
|
||||
}
|
||||
|
||||
return projectDetails.Project, nil
|
||||
}
|
||||
Reference in New Issue
Block a user