mirror of
https://github.com/1Password/onepassword-operator.git
synced 2025-10-22 23:48:05 +00:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
69857c3d47 | ||
![]() |
ad276cb296 | ||
![]() |
eab5a4ad92 | ||
![]() |
128b9b2eb3 | ||
![]() |
867e699030 | ||
![]() |
ffab2cfdab | ||
![]() |
00436b4aee | ||
![]() |
0ca3415a47 | ||
![]() |
4aa1f7a669 | ||
![]() |
6c20db47d6 | ||
![]() |
874d5c57f9 | ||
![]() |
123cfa2c86 |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -12,6 +12,18 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
[//]: # (START/v1.5.0)
|
||||||
|
# v1.5.0
|
||||||
|
|
||||||
|
## Features
|
||||||
|
* `OnePasswordItem` now contains a `status` which contains the status of creating the kubernetes secret for a OnePasswordItem. {#52}
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
* The operator no longer logs an error about changing the secret type if the secret type is not actually being changed.
|
||||||
|
* Annotations on a deployment are no longer removed when the operator triggers a restart. {#112}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
[//]: # "START/v1.4.1"
|
[//]: # "START/v1.4.1"
|
||||||
|
|
||||||
# v1.4.1
|
# v1.4.1
|
||||||
|
@@ -12,8 +12,6 @@ spec:
|
|||||||
scope: Namespaced
|
scope: Namespaced
|
||||||
versions:
|
versions:
|
||||||
- name: v1
|
- name: v1
|
||||||
served: true
|
|
||||||
storage: true
|
|
||||||
schema:
|
schema:
|
||||||
openAPIV3Schema:
|
openAPIV3Schema:
|
||||||
description: OnePasswordItem is the Schema for the onepassworditems API
|
description: OnePasswordItem is the Schema for the onepassworditems API
|
||||||
@@ -38,8 +36,41 @@ spec:
|
|||||||
type: object
|
type: object
|
||||||
status:
|
status:
|
||||||
description: OnePasswordItemStatus defines the observed state of OnePasswordItem
|
description: OnePasswordItemStatus defines the observed state of OnePasswordItem
|
||||||
|
properties:
|
||||||
|
conditions:
|
||||||
|
description: 'Important: Run "operator-sdk generate k8s" to regenerate
|
||||||
|
code after modifying this file Add custom validation using kubebuilder
|
||||||
|
tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html'
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
lastTransitionTime:
|
||||||
|
description: Last time the condition transit from one status
|
||||||
|
to another.
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
description: Human-readable message indicating details about
|
||||||
|
last transition.
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
description: Status of the condition, one of True, False, Unknown.
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
description: Type of job condition, Completed.
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
- type
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- conditions
|
||||||
type: object
|
type: object
|
||||||
type:
|
type:
|
||||||
description: 'Kubernetes secret type. More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
description: 'Kubernetes secret type. More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
|
@@ -11,11 +11,31 @@ type OnePasswordItemSpec struct {
|
|||||||
ItemPath string `json:"itemPath,omitempty"`
|
ItemPath string `json:"itemPath,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OnePasswordItemConditionType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// OnePasswordItemReady means the Kubernetes secret is ready for use.
|
||||||
|
OnePasswordItemReady OnePasswordItemConditionType = "Ready"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OnePasswordItemCondition struct {
|
||||||
|
// Type of job condition, Completed.
|
||||||
|
Type OnePasswordItemConditionType `json:"type"`
|
||||||
|
// Status of the condition, one of True, False, Unknown.
|
||||||
|
Status metav1.ConditionStatus `json:"status"`
|
||||||
|
// Last time the condition transit from one status to another.
|
||||||
|
// +optional
|
||||||
|
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
|
||||||
|
// Human-readable message indicating details about last transition.
|
||||||
|
// +optional
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// OnePasswordItemStatus defines the observed state of OnePasswordItem
|
// OnePasswordItemStatus defines the observed state of OnePasswordItem
|
||||||
type OnePasswordItemStatus struct {
|
type OnePasswordItemStatus struct {
|
||||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
|
||||||
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
|
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
|
||||||
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
|
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
|
||||||
|
Conditions []OnePasswordItemCondition `json:"conditions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
@@ -26,7 +46,9 @@ type OnePasswordItemStatus struct {
|
|||||||
type OnePasswordItem struct {
|
type OnePasswordItem struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
|
// Kubernetes secret type. More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
|
||||||
Spec OnePasswordItemSpec `json:"spec,omitempty"`
|
Spec OnePasswordItemSpec `json:"spec,omitempty"`
|
||||||
Status OnePasswordItemStatus `json:"status,omitempty"`
|
Status OnePasswordItemStatus `json:"status,omitempty"`
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build !ignore_autogenerated
|
||||||
// +build !ignore_autogenerated
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
// Code generated by operator-sdk. DO NOT EDIT.
|
// Code generated by operator-sdk. DO NOT EDIT.
|
||||||
@@ -14,7 +15,7 @@ func (in *OnePasswordItem) DeepCopyInto(out *OnePasswordItem) {
|
|||||||
out.TypeMeta = in.TypeMeta
|
out.TypeMeta = in.TypeMeta
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
out.Spec = in.Spec
|
out.Spec = in.Spec
|
||||||
out.Status = in.Status
|
in.Status.DeepCopyInto(&out.Status)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +37,23 @@ func (in *OnePasswordItem) DeepCopyObject() runtime.Object {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *OnePasswordItemCondition) DeepCopyInto(out *OnePasswordItemCondition) {
|
||||||
|
*out = *in
|
||||||
|
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OnePasswordItemCondition.
|
||||||
|
func (in *OnePasswordItemCondition) DeepCopy() *OnePasswordItemCondition {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(OnePasswordItemCondition)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *OnePasswordItemList) DeepCopyInto(out *OnePasswordItemList) {
|
func (in *OnePasswordItemList) DeepCopyInto(out *OnePasswordItemList) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -88,6 +106,13 @@ func (in *OnePasswordItemSpec) DeepCopy() *OnePasswordItemSpec {
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *OnePasswordItemStatus) DeepCopyInto(out *OnePasswordItemStatus) {
|
func (in *OnePasswordItemStatus) DeepCopyInto(out *OnePasswordItemStatus) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.Conditions != nil {
|
||||||
|
in, out := &in.Conditions, &out.Conditions
|
||||||
|
*out = make([]OnePasswordItemCondition, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -96,10 +96,11 @@ func (r *ReconcileOnePasswordItem) Reconcile(request reconcile.Request) (reconci
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handles creation or updating secrets for deployment if needed
|
// Handles creation or updating secrets for deployment if needed
|
||||||
if err := r.HandleOnePasswordItem(onepassworditem, request); err != nil {
|
err := r.HandleOnePasswordItem(onepassworditem, request)
|
||||||
return reconcile.Result{}, err
|
if updateStatusErr := r.updateStatus(onepassworditem, err); updateStatusErr != nil {
|
||||||
|
return reconcile.Result{}, fmt.Errorf("cannot update status: %s", updateStatusErr)
|
||||||
}
|
}
|
||||||
return reconcile.Result{}, nil
|
return reconcile.Result{}, err
|
||||||
}
|
}
|
||||||
// If one password finalizer exists then we must cleanup associated secrets
|
// If one password finalizer exists then we must cleanup associated secrets
|
||||||
if utils.ContainsString(onepassworditem.ObjectMeta.Finalizers, finalizer) {
|
if utils.ContainsString(onepassworditem.ObjectMeta.Finalizers, finalizer) {
|
||||||
@@ -169,3 +170,34 @@ func (r *ReconcileOnePasswordItem) HandleOnePasswordItem(resource *onepasswordv1
|
|||||||
|
|
||||||
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item, autoRestart, labels, secretType, ownerRef)
|
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item, autoRestart, labels, secretType, ownerRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ReconcileOnePasswordItem) updateStatus(resource *onepasswordv1.OnePasswordItem, err error) error {
|
||||||
|
existingCondition := findCondition(resource.Status.Conditions, onepasswordv1.OnePasswordItemReady)
|
||||||
|
updatedCondition := existingCondition
|
||||||
|
if err != nil {
|
||||||
|
updatedCondition.Message = err.Error()
|
||||||
|
updatedCondition.Status = metav1.ConditionFalse
|
||||||
|
} else {
|
||||||
|
updatedCondition.Message = ""
|
||||||
|
updatedCondition.Status = metav1.ConditionTrue
|
||||||
|
}
|
||||||
|
|
||||||
|
if existingCondition.Status != updatedCondition.Status {
|
||||||
|
updatedCondition.LastTransitionTime = metav1.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.Status.Conditions = []onepasswordv1.OnePasswordItemCondition{updatedCondition}
|
||||||
|
return r.kubeClient.Status().Update(context.Background(), resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findCondition(conditions []onepasswordv1.OnePasswordItemCondition, t onepasswordv1.OnePasswordItemConditionType) onepasswordv1.OnePasswordItemCondition {
|
||||||
|
for _, c := range conditions {
|
||||||
|
if c.Type == t {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return onepasswordv1.OnePasswordItemCondition{
|
||||||
|
Type: t,
|
||||||
|
Status: metav1.ConditionUnknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -27,7 +27,6 @@ import (
|
|||||||
const OnepasswordPrefix = "operator.1password.io"
|
const OnepasswordPrefix = "operator.1password.io"
|
||||||
const NameAnnotation = OnepasswordPrefix + "/item-name"
|
const NameAnnotation = OnepasswordPrefix + "/item-name"
|
||||||
const VersionAnnotation = OnepasswordPrefix + "/item-version"
|
const VersionAnnotation = OnepasswordPrefix + "/item-version"
|
||||||
const restartAnnotation = OnepasswordPrefix + "/last-restarted"
|
|
||||||
const ItemPathAnnotation = OnepasswordPrefix + "/item-path"
|
const ItemPathAnnotation = OnepasswordPrefix + "/item-path"
|
||||||
const RestartDeploymentsAnnotation = OnepasswordPrefix + "/auto-restart"
|
const RestartDeploymentsAnnotation = OnepasswordPrefix + "/auto-restart"
|
||||||
|
|
||||||
@@ -45,8 +44,7 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
|
|||||||
if autoRestart != "" {
|
if autoRestart != "" {
|
||||||
_, err := utils.StringToBool(autoRestart)
|
_, err := utils.StringToBool(autoRestart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "Error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secretName)
|
return fmt.Errorf("Error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secretName)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
secretAnnotations[RestartDeploymentsAnnotation] = autoRestart
|
secretAnnotations[RestartDeploymentsAnnotation] = autoRestart
|
||||||
}
|
}
|
||||||
@@ -63,19 +61,31 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
currentAnnotations := currentSecret.Annotations
|
// Check if the secret types are being changed on the update.
|
||||||
currentLabels := currentSecret.Labels
|
// Avoid Opaque and "" are treated as different on check.
|
||||||
|
wantSecretType := secretType
|
||||||
|
if wantSecretType == "" {
|
||||||
|
wantSecretType = string(corev1.SecretTypeOpaque)
|
||||||
|
}
|
||||||
currentSecretType := string(currentSecret.Type)
|
currentSecretType := string(currentSecret.Type)
|
||||||
if !reflect.DeepEqual(currentSecretType, secretType) {
|
if currentSecretType == "" {
|
||||||
|
currentSecretType = string(corev1.SecretTypeOpaque)
|
||||||
|
}
|
||||||
|
if currentSecretType != wantSecretType {
|
||||||
return ErrCannotUpdateSecretType
|
return ErrCannotUpdateSecretType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentAnnotations := currentSecret.Annotations
|
||||||
|
currentLabels := currentSecret.Labels
|
||||||
if !reflect.DeepEqual(currentAnnotations, secretAnnotations) || !reflect.DeepEqual(currentLabels, labels) {
|
if !reflect.DeepEqual(currentAnnotations, secretAnnotations) || !reflect.DeepEqual(currentLabels, labels) {
|
||||||
log.Info(fmt.Sprintf("Updating Secret %v at namespace '%v'", secret.Name, secret.Namespace))
|
log.Info(fmt.Sprintf("Updating Secret %v at namespace '%v'", secret.Name, secret.Namespace))
|
||||||
currentSecret.ObjectMeta.Annotations = secretAnnotations
|
currentSecret.ObjectMeta.Annotations = secretAnnotations
|
||||||
currentSecret.ObjectMeta.Labels = labels
|
currentSecret.ObjectMeta.Labels = labels
|
||||||
currentSecret.Data = secret.Data
|
currentSecret.Data = secret.Data
|
||||||
return kubeClient.Update(context.Background(), currentSecret)
|
if err := kubeClient.Update(context.Background(), currentSecret); err != nil {
|
||||||
|
return fmt.Errorf("Kubernetes secret update failed: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info(fmt.Sprintf("Secret with name %v and version %v already exists", secret.Name, secret.Annotations[VersionAnnotation]))
|
log.Info(fmt.Sprintf("Secret with name %v and version %v already exists", secret.Name, secret.Annotations[VersionAnnotation]))
|
||||||
|
@@ -91,9 +91,10 @@ func (h *SecretUpdateHandler) restartDeploymentsWithUpdatedSecrets(updatedSecret
|
|||||||
|
|
||||||
func (h *SecretUpdateHandler) restartDeployment(deployment *appsv1.Deployment) {
|
func (h *SecretUpdateHandler) restartDeployment(deployment *appsv1.Deployment) {
|
||||||
log.Info(fmt.Sprintf("Deployment %q at namespace %q references an updated secret. Restarting", deployment.GetName(), deployment.Namespace))
|
log.Info(fmt.Sprintf("Deployment %q at namespace %q references an updated secret. Restarting", deployment.GetName(), deployment.Namespace))
|
||||||
deployment.Spec.Template.Annotations = map[string]string{
|
if deployment.Spec.Template.Annotations == nil {
|
||||||
RestartAnnotation: time.Now().String(),
|
deployment.Spec.Template.Annotations = map[string]string{}
|
||||||
}
|
}
|
||||||
|
deployment.Spec.Template.Annotations[RestartAnnotation] = time.Now().String()
|
||||||
err := h.client.Update(context.Background(), deployment)
|
err := h.client.Update(context.Background(), deployment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "Problem restarting deployment")
|
log.Error(err, "Problem restarting deployment")
|
||||||
|
@@ -122,6 +122,9 @@ var tests = []testUpdateSecretTask{
|
|||||||
},
|
},
|
||||||
Spec: appsv1.DeploymentSpec{
|
Spec: appsv1.DeploymentSpec{
|
||||||
Template: corev1.PodTemplateSpec{
|
Template: corev1.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{"external-annotation": "some-value"},
|
||||||
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
Containers: []corev1.Container{
|
Containers: []corev1.Container{
|
||||||
{
|
{
|
||||||
@@ -235,6 +238,9 @@ var tests = []testUpdateSecretTask{
|
|||||||
},
|
},
|
||||||
Spec: appsv1.DeploymentSpec{
|
Spec: appsv1.DeploymentSpec{
|
||||||
Template: corev1.PodTemplateSpec{
|
Template: corev1.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{"external-annotation": "some-value"},
|
||||||
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
Volumes: []corev1.Volume{
|
Volumes: []corev1.Volume{
|
||||||
{
|
{
|
||||||
@@ -342,6 +348,9 @@ var tests = []testUpdateSecretTask{
|
|||||||
},
|
},
|
||||||
Spec: appsv1.DeploymentSpec{
|
Spec: appsv1.DeploymentSpec{
|
||||||
Template: corev1.PodTemplateSpec{
|
Template: corev1.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{"external-annotation": "some-value"},
|
||||||
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
Containers: []corev1.Container{
|
Containers: []corev1.Container{
|
||||||
{
|
{
|
||||||
@@ -411,6 +420,9 @@ var tests = []testUpdateSecretTask{
|
|||||||
},
|
},
|
||||||
Spec: appsv1.DeploymentSpec{
|
Spec: appsv1.DeploymentSpec{
|
||||||
Template: corev1.PodTemplateSpec{
|
Template: corev1.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{"external-annotation": "some-value"},
|
||||||
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
Containers: []corev1.Container{
|
Containers: []corev1.Container{
|
||||||
{
|
{
|
||||||
@@ -482,6 +494,9 @@ var tests = []testUpdateSecretTask{
|
|||||||
},
|
},
|
||||||
Spec: appsv1.DeploymentSpec{
|
Spec: appsv1.DeploymentSpec{
|
||||||
Template: corev1.PodTemplateSpec{
|
Template: corev1.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{"external-annotation": "some-value"},
|
||||||
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
Containers: []corev1.Container{
|
Containers: []corev1.Container{
|
||||||
{
|
{
|
||||||
@@ -553,6 +568,9 @@ var tests = []testUpdateSecretTask{
|
|||||||
},
|
},
|
||||||
Spec: appsv1.DeploymentSpec{
|
Spec: appsv1.DeploymentSpec{
|
||||||
Template: corev1.PodTemplateSpec{
|
Template: corev1.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{"external-annotation": "some-value"},
|
||||||
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
Containers: []corev1.Container{
|
Containers: []corev1.Container{
|
||||||
{
|
{
|
||||||
@@ -630,6 +648,9 @@ var tests = []testUpdateSecretTask{
|
|||||||
},
|
},
|
||||||
Spec: appsv1.DeploymentSpec{
|
Spec: appsv1.DeploymentSpec{
|
||||||
Template: corev1.PodTemplateSpec{
|
Template: corev1.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{"external-annotation": "some-value"},
|
||||||
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
Containers: []corev1.Container{
|
Containers: []corev1.Container{
|
||||||
{
|
{
|
||||||
@@ -703,6 +724,9 @@ var tests = []testUpdateSecretTask{
|
|||||||
},
|
},
|
||||||
Spec: appsv1.DeploymentSpec{
|
Spec: appsv1.DeploymentSpec{
|
||||||
Template: corev1.PodTemplateSpec{
|
Template: corev1.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{"external-annotation": "some-value"},
|
||||||
|
},
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
Containers: []corev1.Container{
|
Containers: []corev1.Container{
|
||||||
{
|
{
|
||||||
@@ -829,6 +853,16 @@ func TestUpdateSecretHandler(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
assert.False(t, testData.expectedRestart, "Deployment was restarted but should not have been.")
|
assert.False(t, testData.expectedRestart, "Deployment was restarted but should not have been.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldPodTemplateAnnotations := testData.existingDeployment.Spec.Template.ObjectMeta.Annotations
|
||||||
|
newPodTemplateAnnotations := deployment.Spec.Template.Annotations
|
||||||
|
for name, expected := range oldPodTemplateAnnotations {
|
||||||
|
actual, ok := newPodTemplateAnnotations[name]
|
||||||
|
if assert.Truef(t, ok, "Annotation %s was present in original pod template but was dropped after update", name) {
|
||||||
|
assert.Equalf(t, expected, actual, "Annotation value for %s original pod template has changed", name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user