Auto restart one password custom resource will be be added to converted kubernetes secret

This commit is contained in:
jillianwilson
2021-03-02 16:59:21 -04:00
parent 8635be0cab
commit d98f9172a0
8 changed files with 59 additions and 26 deletions

View File

@@ -176,6 +176,18 @@ metadata:
``` ```
If the value is not set, the auto reset settings on the namespace will be used. If the value is not set, the auto reset settings on the namespace will be used.
**Per OnePasswordItem Custom Resource**
This method allows for managing auto restarts on a given OnePasswordItem custom resource. Auto restarts can by managed by setting the annotation `onepasswordoperator/auto_restart` to either `true` or `false` on the desired OnePasswordItem. An example of this is shown below:
```yaml
# enabled auto restarts for the OnePasswordItem
apiVersion: onepassword.com/v1
kind: OnePasswordItem
metadata:
name: example
onepasswordoperator/auto_restart: "true"
```
If the value is not set, the auto reset settings on the deployment will be used.
## Development ## Development
### Creating a Docker image ### Creating a Docker image

View File

@@ -2,6 +2,5 @@ apiVersion: onepassword.com/v1
kind: OnePasswordItem kind: OnePasswordItem
metadata: metadata:
name: example name: example
onepasswordoperator/auto_restart: "true"
spec: spec:
itemPath: "vaults/<vault_id>/items/<item_id>" itemPath: "vaults/<vault_id>/items/<item_id>"

View File

@@ -201,5 +201,5 @@ func (r *ReconcileDeployment) HandleApplyingDeployment(namespace string, annotat
return fmt.Errorf("Failed to retrieve item: %v", err) return fmt.Errorf("Failed to retrieve item: %v", err)
} }
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, namespace, item) return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, namespace, item, annotations[op.RestartDeploymentsAnnotation])
} }

View File

@@ -7,6 +7,7 @@ import (
onepasswordv1 "github.com/1Password/onepassword-operator/pkg/apis/onepassword/v1" onepasswordv1 "github.com/1Password/onepassword-operator/pkg/apis/onepassword/v1"
kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets" kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets"
"github.com/1Password/onepassword-operator/pkg/onepassword" "github.com/1Password/onepassword-operator/pkg/onepassword"
op "github.com/1Password/onepassword-operator/pkg/onepassword"
"github.com/1Password/onepassword-operator/pkg/utils" "github.com/1Password/onepassword-operator/pkg/utils"
"github.com/1Password/connect-sdk-go/connect" "github.com/1Password/connect-sdk-go/connect"
@@ -143,11 +144,12 @@ func (r *ReconcileOnePasswordItem) removeOnePasswordFinalizerFromOnePasswordItem
func (r *ReconcileOnePasswordItem) HandleOnePasswordItem(resource *onepasswordv1.OnePasswordItem, request reconcile.Request) error { func (r *ReconcileOnePasswordItem) HandleOnePasswordItem(resource *onepasswordv1.OnePasswordItem, request reconcile.Request) error {
secretName := resource.GetName() secretName := resource.GetName()
autoRestart := resource.Annotations[op.RestartDeploymentsAnnotation]
item, err := onepassword.GetOnePasswordItemByPath(r.opConnectClient, resource.Spec.ItemPath) item, err := onepassword.GetOnePasswordItemByPath(r.opConnectClient, resource.Spec.ItemPath)
if err != nil { if err != nil {
return fmt.Errorf("Failed to retrieve item: %v", err) return fmt.Errorf("Failed to retrieve item: %v", err)
} }
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item) return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item, autoRestart)
} }

View File

@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/1Password/connect-sdk-go/onepassword" "github.com/1Password/connect-sdk-go/onepassword"
"github.com/1Password/onepassword-operator/pkg/utils"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -13,21 +14,30 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/log" logf "sigs.k8s.io/controller-runtime/pkg/log"
) )
const onepasswordPrefix = "onepasswordoperator" const OnepasswordPrefix = "onepasswordoperator"
const NameAnnotation = onepasswordPrefix + "/item-name" const NameAnnotation = OnepasswordPrefix + "/item-name"
const VersionAnnotation = onepasswordPrefix + "/item-version" const VersionAnnotation = OnepasswordPrefix + "/item-version"
const restartAnnotation = onepasswordPrefix + "/lastRestarted" const restartAnnotation = OnepasswordPrefix + "/lastRestarted"
const ItemPathAnnotation = onepasswordPrefix + "/item-path" const ItemPathAnnotation = OnepasswordPrefix + "/item-path"
const RestartDeploymentsAnnotation = OnepasswordPrefix + "/auto_restart"
var log = logf.Log var log = logf.Log
func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretName, namespace string, item *onepassword.Item) error { func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretName, namespace string, item *onepassword.Item, autoRestart string) error {
itemVersion := fmt.Sprint(item.Version) itemVersion := fmt.Sprint(item.Version)
annotations := map[string]string{ annotations := map[string]string{
VersionAnnotation: itemVersion, VersionAnnotation: itemVersion,
ItemPathAnnotation: fmt.Sprintf("vaults/%v/items/%v", item.Vault.ID, item.ID), ItemPathAnnotation: fmt.Sprintf("vaults/%v/items/%v", item.Vault.ID, item.ID),
} }
if autoRestart != "" {
_, err := utils.StringToBool(autoRestart)
if err != nil {
log.Error(err, "Error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secretName)
return err
}
annotations[RestartDeploymentsAnnotation] = autoRestart
}
secret := BuildKubernetesSecretFromOnePasswordItem(secretName, namespace, annotations, *item) secret := BuildKubernetesSecretFromOnePasswordItem(secretName, namespace, annotations, *item)
currentSecret := &corev1.Secret{} currentSecret := &corev1.Secret{}

View File

@@ -13,6 +13,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/client/fake"
) )
const restartDeploymentAnnotation = "false"
type k8s struct { type k8s struct {
clientset kubernetes.Interface clientset kubernetes.Interface
} }
@@ -28,7 +30,7 @@ func TestCreateKubernetesSecretFromOnePasswordItem(t *testing.T) {
item.ID = "h46bb3jddvay7nxopfhvlwg35q" item.ID = "h46bb3jddvay7nxopfhvlwg35q"
kubeClient := fake.NewFakeClient() kubeClient := fake.NewFakeClient()
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item) err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation)
if err != nil { if err != nil {
t.Errorf("Unexpected error: %v", err) t.Errorf("Unexpected error: %v", err)
} }
@@ -53,7 +55,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) {
item.ID = "h46bb3jddvay7nxopfhvlwg35q" item.ID = "h46bb3jddvay7nxopfhvlwg35q"
kubeClient := fake.NewFakeClient() kubeClient := fake.NewFakeClient()
err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item) err := CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &item, restartDeploymentAnnotation)
if err != nil { if err != nil {
t.Errorf("Unexpected error: %v", err) t.Errorf("Unexpected error: %v", err)
} }
@@ -64,7 +66,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) {
newItem.Version = 456 newItem.Version = 456
newItem.Vault.ID = "hfnjvi6aymbsnfc2xeeoheizda" newItem.Vault.ID = "hfnjvi6aymbsnfc2xeeoheizda"
newItem.ID = "h46bb3jddvay7nxopfhvlwg35q" newItem.ID = "h46bb3jddvay7nxopfhvlwg35q"
err = CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &newItem) err = CreateKubernetesSecretFromItem(kubeClient, secretName, namespace, &newItem, restartDeploymentAnnotation)
if err != nil { if err != nil {
t.Errorf("Unexpected error: %v", err) t.Errorf("Unexpected error: %v", err)
} }
@@ -125,6 +127,10 @@ func compareAnnotationsToItem(annotations map[string]string, item onepassword.It
if annotations[VersionAnnotation] != fmt.Sprint(item.Version) { if annotations[VersionAnnotation] != fmt.Sprint(item.Version) {
t.Errorf("Expected annotation version to be %v but was %v", item.Version, annotations[VersionAnnotation]) t.Errorf("Expected annotation version to be %v but was %v", item.Version, annotations[VersionAnnotation])
} }
if annotations[RestartDeploymentsAnnotation] != "false" {
t.Errorf("Expected restart deployments annotation to be %v but was %v", restartDeploymentAnnotation, RestartDeploymentsAnnotation)
}
} }
func compareFields(actualFields []*onepassword.ItemField, secretData map[string][]byte, t *testing.T) { func compareFields(actualFields []*onepassword.ItemField, secretData map[string][]byte, t *testing.T) {

View File

@@ -3,11 +3,10 @@ package onepassword
import ( import (
"context" "context"
"fmt" "fmt"
"strconv"
"strings"
"time" "time"
kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets" kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets"
"github.com/1Password/onepassword-operator/pkg/utils"
"github.com/1Password/connect-sdk-go/connect" "github.com/1Password/connect-sdk-go/connect"
"github.com/1Password/connect-sdk-go/onepassword" "github.com/1Password/connect-sdk-go/onepassword"
@@ -184,9 +183,9 @@ func isSecretSetForAutoRestart(secret *corev1.Secret, deployment *appsv1.Deploym
return isDeploymentSetForAutoRestart(deployment, setForAutoRestartByNamespace) return isDeploymentSetForAutoRestart(deployment, setForAutoRestartByNamespace)
} }
restartDeploymentBool, err := stringToBool(restartDeployment) restartDeploymentBool, err := utils.StringToBool(restartDeployment)
if err != nil { if err != nil {
log.Error(err, "Error parsing %v annotation on Deployment %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, deployment.Name) log.Error(err, "Error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secret.Name)
return false return false
} }
return restartDeploymentBool return restartDeploymentBool
@@ -199,7 +198,7 @@ func isDeploymentSetForAutoRestart(deployment *appsv1.Deployment, setForAutoRest
return setForAutoRestartByNamespace[deployment.Namespace] return setForAutoRestartByNamespace[deployment.Namespace]
} }
restartDeploymentBool, err := stringToBool(restartDeployment) restartDeploymentBool, err := utils.StringToBool(restartDeployment)
if err != nil { if err != nil {
log.Error(err, "Error parsing %v annotation on Deployment %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, deployment.Name) log.Error(err, "Error parsing %v annotation on Deployment %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, deployment.Name)
return false return false
@@ -214,18 +213,10 @@ func (h *SecretUpdateHandler) isNamespaceSetToAutoRestart(namespace *corev1.Name
return h.shouldAutoRestartDeploymentsGlobal return h.shouldAutoRestartDeploymentsGlobal
} }
restartDeploymentBool, err := stringToBool(restartDeployment) restartDeploymentBool, err := utils.StringToBool(restartDeployment)
if err != nil { if err != nil {
log.Error(err, "Error parsing %v annotation on Namespace %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, namespace.Name) log.Error(err, "Error parsing %v annotation on Namespace %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, namespace.Name)
return false return false
} }
return restartDeploymentBool return restartDeploymentBool
} }
func stringToBool(str string) (bool, error) {
restartDeploymentBool, err := strconv.ParseBool(strings.ToLower(str))
if err != nil {
return false, err
}
return restartDeploymentBool, nil
}

View File

@@ -1,5 +1,10 @@
package utils package utils
import (
"strconv"
"strings"
)
func ContainsString(slice []string, s string) bool { func ContainsString(slice []string, s string) bool {
for _, item := range slice { for _, item := range slice {
if item == s { if item == s {
@@ -18,3 +23,11 @@ func RemoveString(slice []string, s string) (result []string) {
} }
return return
} }
func StringToBool(str string) (bool, error) {
restartDeploymentBool, err := strconv.ParseBool(strings.ToLower(str))
if err != nil {
return false, err
}
return restartDeploymentBool, nil
}