Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: toSelectorMatchFields #376

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func main() {

{
secretExports := sharing.NewSecretExportsWarmedUp(
sharing.NewSecretExports(log.WithName("secretexports")))
sharing.NewSecretExports(mgr.GetClient(), log.WithName("secretexports")))

secretExportReconciler := sharing.NewSecretExportReconciler(
mgr.GetClient(), secretExports, log.WithName("secexp"))
Expand Down
24 changes: 24 additions & 0 deletions config/package-bundle/config/crds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,30 @@ spec:
items:
type: string
type: array
dangerousToNamespacesSelector:
type: array
items:
properties:
key:
type: string
description: Property to target the resource for the match. It supports dot notation.
operator:
type: string
description: Type of comparison.
enum:
- In
- NotIn
- Exists
- DoesNotExist
values:
description: Values to match on the resource key using the comparison operator.
type: array
items:
type: string
type: object
required:
- key
- operator
type: object
status:
properties:
Expand Down
34 changes: 33 additions & 1 deletion docs/secret-export.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ apiVersion: v1
kind: Namespace
metadata:
name: user2
---
apiVersion: v1
kind: Namespace
metadata:
name: user3
annotations:
field.cattle.io/projectId: "cluster1:project1"

#! generate user-password secret upon creation
---
Expand All @@ -29,7 +36,7 @@ metadata:
name: user-password
namespace: user1

#! offer user-password to user2 namespace
#! offer user-password to user2 namespace and namespace with specified annotations (in this case user3)
---
apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretExport
Expand All @@ -38,6 +45,10 @@ metadata:
namespace: user1
spec:
toNamespace: user2
dangerousToNamespacesSelector:
- key: "metadata.annotations['field\\.cattle\\.io/projectId']"
operator: In
value: "cluster1:project1"

#! allow user-password to be created in user2 namespace
---
Expand All @@ -48,6 +59,17 @@ metadata:
namespace: user2
spec:
fromNamespace: user1

#! allow user-password to be created in namespace user3
---
apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretImport
metadata:
name: user-password
namespace: user3
spec:
fromNamespace: user1

```

Above configuration results in a `user-password` Secret created within `user2` namespace:
Expand Down Expand Up @@ -75,6 +97,16 @@ SecretExport CRD allows to "offer" secrets for export.

- `toNamespace` (optional; string) Destination namespace for offer. Use `*` to indicate all namespaces.
- `toNamespaces` (optional; array of strings) List of destination namespaces for offer.
- `dangerousToNamespacesSelector` (optional; array of selector objects) List of matchers for destination namespaces. If multiple expressions are specified, all those expressions must evaluate to true for the selector to match a namespace. The selector object is composed as follows:
- `key` (required; string) Property to target on the resource for the match. Based on kubernetes JSONPath syntax.
- `operator` (required; enum string) Type of comparison. Must be one of `In`, `NotIn`, `Exists`, `DoesNotExist`.
Operator explanations:
- In: Label's value must match one of the specified values.
- NotIn: Label's value must not match any of the specified values.
- Exists: Namespace must include a label with the specified key (the value isn't relevant). When using this operator, the values field should not be specified.
- DoesNotExist: Namespace must not include a label with the specified key. The values property must not be specified.

- `values` (optional; array if string) Values to match on the resource key using the comparison operator.

### SecretImport

Expand Down
30 changes: 30 additions & 0 deletions examples/secret-export.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,33 @@ metadata:
namespace: user3
spec:
fromNamespace: user1

#! export user-password-multi secret from user1 to namespaces containing specific annotations
---
apiVersion: secretgen.k14s.io/v1alpha1
kind: Password
metadata:
name: scoped-user-password-multi
namespace: user1
---
apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretExport
metadata:
name: scoped-user-password-multi
namespace: user1
spec:
dangerousToNamespacesSelector:
- key: "metadata.annotations['field\\.cattle\\.io/projectId']"
operator: In
value: "cluster1:project1"
---
apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretImport
metadata:
name: scoped-user-password-multi
namespace: user2
annotations:
field.cattle.io/projectId: "cluster1:project1"
spec:
fromNamespace: user1

37 changes: 35 additions & 2 deletions pkg/apis/secretgen2/v1alpha1/secret_export.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,31 @@ type SecretExportList struct {
Items []SecretExport `json:"items"`
}

// SelectorOperator is a part of SelectorMatchField
type SelectorOperator string

// SelectorOperator values
const (
SelectorOperatorIn SelectorOperator = "In"
SelectorOperatorNotIn = "NotIn"
SelectorOperatorExists = "Exists"
SelectorOperatorDoesNotExist = "DoesNotExist"
)

// SelectorMatchField is a selector field to match against namespace definition
type SelectorMatchField struct {
Key string `json:"key,omitempty"`
Operator SelectorOperator `json:"operator,omitempty"`
Values []string `json:"values,omitempty"`
}

type SecretExportSpec struct {
// +optional
ToNamespace string `json:"toNamespace,omitempty"`
// +optional
ToNamespaces []string `json:"toNamespaces,omitempty"`
// +optional
ToNamespacesSelector []SelectorMatchField `json:"dangerousToNamespacesSelector,omitempty"`
}

type SecretExportStatus struct {
Expand All @@ -68,15 +88,28 @@ func (e SecretExport) Validate() error {
var errs []error

toNses := e.StaticToNamespaces()
toSmf := e.Spec.ToNamespacesSelector

if len(toNses) == 0 {
errs = append(errs, fmt.Errorf("Expected to have at least one non-empty to namespace"))
if len(toNses) == 0 && len(toSmf) == 0 {
errs = append(errs, fmt.Errorf("Expected to have at least one non-empty to namespace or to namespace annotation"))
}
for _, ns := range toNses {
if len(ns) == 0 {
errs = append(errs, fmt.Errorf("Expected to namespace to be non-empty"))
}
}
for _, s := range toSmf {
devthejo marked this conversation as resolved.
Show resolved Hide resolved
switch s.Operator {
case SelectorOperatorIn, SelectorOperatorNotIn:
if len(s.Values) == 0 {
errs = append(errs, fmt.Errorf("Values must be specified when `operator` is 'In' or 'NotIn'"))
}
case SelectorOperatorExists, SelectorOperatorDoesNotExist:
if len(s.Values) > 0 {
errs = append(errs, fmt.Errorf("Values may not be specified when `operator` is 'Exists' or 'DoesNotExist'"))
}
}
}

return combinedErrs("Validation errors", errs)
}
2 changes: 1 addition & 1 deletion pkg/sharing/import_secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func secretImportFor(sourceSecret corev1.Secret) sg2v1alpha1.SecretImport {

func importReconcilers(objects ...runtime.Object) (secretExportReconciler *sharing.SecretExportReconciler, secretImportReconciler *sharing.SecretImportReconciler, k8sClient client.Client) {
k8sClient = fakeClient.NewFakeClient(objects...)
secretExports := sharing.NewSecretExportsWarmedUp(sharing.NewSecretExports(testLogr))
secretExports := sharing.NewSecretExportsWarmedUp(sharing.NewSecretExports(k8sClient, testLogr))
secretExportReconciler = sharing.NewSecretExportReconciler(k8sClient, secretExports, testLogr)
secretExports.WarmUpFunc = secretExportReconciler.WarmUp
secretImportReconciler = sharing.NewSecretImportReconciler(k8sClient, secretExports, testLogr)
Expand Down
2 changes: 1 addition & 1 deletion pkg/sharing/placeholder_secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func Test_SecretReconciler_updatesStatus(t *testing.T) {
}
func placeholderReconcilers(objects ...runtime.Object) (secretExportReconciler *sharing.SecretExportReconciler, secretReconciler *sharing.SecretReconciler, k8sClient client.Client) {
k8sClient = fakeClient.NewFakeClient(objects...)
secretExports := sharing.NewSecretExportsWarmedUp(sharing.NewSecretExports(testLogr))
secretExports := sharing.NewSecretExportsWarmedUp(sharing.NewSecretExports(k8sClient, testLogr))
secretExportReconciler = sharing.NewSecretExportReconciler(k8sClient, secretExports, testLogr)
secretExports.WarmUpFunc = secretExportReconciler.WarmUp
secretReconciler = sharing.NewSecretReconciler(k8sClient, secretExports, testLogr)
Expand Down
Loading
Loading