Skip to content

Commit

Permalink
feat: add type to policy CRD
Browse files Browse the repository at this point in the history
  • Loading branch information
binbin-li committed Sep 13, 2023
1 parent 1cae9e1 commit 55b774d
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 78 deletions.
3 changes: 3 additions & 0 deletions api/unversioned/policy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import (
type PolicySpec struct {
// Important: Run "make" to regenerate code after modifying this file

// Type of the polocy
Type string `json:"type,omitempty"`

// Parameters for this policy
Parameters runtime.RawExtension `json:"parameters,omitempty"`
}
Expand Down
3 changes: 3 additions & 0 deletions api/v1alpha1/policy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ import (
type PolicySpec struct {
// Important: Run "make" to regenerate code after modifying this file

// Type of the polocy
Type string `json:"type,omitempty"`

// +kubebuilder:pruning:PreserveUnknownFields
// Parameters for this policy
Parameters runtime.RawExtension `json:"parameters,omitempty"`
Expand Down
2 changes: 2 additions & 0 deletions api/v1alpha1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 35 additions & 33 deletions charts/ratify/crds/policy-customresourcedefinition.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
Expand All @@ -14,36 +15,37 @@ spec:
singular: policy
scope: Cluster
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: Policy is the Schema for the policies 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: PolicySpec defines the desired state of Policy
properties:
parameters:
description: Parameters for this policy
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
status:
description: PolicyStatus defines the observed state of Policy
type: object
type: object
served: true
storage: true
- name: v1alpha1
schema:
openAPIV3Schema:
description: Policy is the Schema for the policies 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: PolicySpec defines the desired state of Policy
properties:
parameters:
description: Parameters for this policy
type: object
x-kubernetes-preserve-unknown-fields: true
type:
description: Type of the polocy
type: string
type: object
status:
description: PolicyStatus defines the observed state of Policy
type: object
type: object
served: true
storage: true
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ spec:
format: date-time
type: string
properties:
description: provider specific parameters of the each individual certificate
description: provider specific properties of the each individual certificate
type: object
x-kubernetes-preserve-unknown-fields: true
required:
Expand Down
3 changes: 3 additions & 0 deletions config/crd/bases/config.ratify.deislabs.io_policies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ spec:
description: Parameters for this policy
type: object
x-kubernetes-preserve-unknown-fields: true
type:
description: Type of the polocy
type: string
type: object
status:
description: PolicyStatus defines the observed state of Policy
Expand Down
3 changes: 2 additions & 1 deletion config/samples/policy/config_v1alpha1_policy_json.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
apiVersion: config.ratify.deislabs.io/v1alpha1
kind: Policy
metadata:
name: "configpolicy"
name: "ratify-policy"
spec:
type: "configpolicy"
parameters:
artifactVerificationPolicies:
default: "all"
3 changes: 2 additions & 1 deletion config/samples/policy/config_v1alpha1_policy_rego.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
apiVersion: config.ratify.deislabs.io/v1alpha1
kind: Policy
metadata:
name: "regopolicy"
name: "ratify-policy"
spec:
type: "regopolicy"
parameters:
passthroughEnabled: false
policy: |
Expand Down
13 changes: 8 additions & 5 deletions docs/reference/crds/policies.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,24 @@ View more CRD samples [here](../../../config/samples/policy). The `metadata.name
apiVersion: config.ratify.deislabs.io/v1alpha1
kind: Policy
metadata:
name: "configpolicy"
name: "ratify-policy"
spec:
type: required. Type of the policy.
parameters: required. Parameters specific to this policy
```
There would be exactly one CR existing in the cluster. That means if users apply a new CR, the old one would be replaced.
Ratify would only accept a policy setting `metadata.name` as `ratify-policy` since there is at most one active policy for Ratify to use. That means if users apply a new CR with different `metadata.name`, Ratify will not update the existing policy.

Note: `metadata.name` MUST be `configpolicy` or `regopolicy` per the usage.
Note: `spec.name` MUST be `configpolicy` or `regopolicy` per the usage.

## configpolicy
Sample spec:
```yml
apiVersion: config.ratify.deislabs.io/v1alpha1
kind: Policy
metadata:
name: "configpolicy"
name: "ratify-policy"
spec:
type: "configpolicy"
parameters:
artifactVerificationPolicies:
"application/vnd.cncf.notary.signature": "any"
Expand All @@ -38,8 +40,9 @@ Sample spec:
apiVersion: config.ratify.deislabs.io/v1alpha1
kind: Policy
metadata:
name: "regopolicy"
name: "ratify-policy"
spec:
type: "regopolicy"
parameters:
passthroughEnabled: false
policy: |
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The Rego Policy Provider is a built-in policy provider that uses [Open Policy Ag
Ratify embeds OPA engine inside the executor to provide a built-in policy provider. There are 2 approaches to enable this feature as an add-on service.

1. Set the helm chart value of `policy.useRego` to `true` while deploying Ratify.
2. Apply a Policy Custom Resource with Rego Policy if the service is up. e.g.
2. Apply a Policy Custom Resource with Rego Policy after the service is up. e.g.
```bash
kubectl apply -f ./config/samples/policy/config_v1alpha1_policy_rego.yaml
```
Expand Down
39 changes: 11 additions & 28 deletions pkg/controllers/policy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)

const ratifyPolicy = "ratify-policy"

// PolicyReconciler reconciles a Policy object
type PolicyReconciler struct {
client.Client
Expand Down Expand Up @@ -74,33 +76,15 @@ func (r *PolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
return ctrl.Result{}, client.IgnoreNotFound(err)
}

if err := policyAddOrReplace(policy.Spec, resource); err != nil {
policyLogger.Error("unable to create policy from policy crd: ", err)
return ctrl.Result{}, err
if resource != ratifyPolicy {
return ctrl.Result{}, nil
}

// List all policies in the same namespace.
policyList := &configv1alpha1.PolicyList{}
if err := r.List(ctx, policyList, client.InNamespace(req.Namespace)); err != nil {
policyLogger.Error("failed to list Policies: ", err)
if err := policyAddOrReplace(policy.Spec); err != nil {
policyLogger.Error("unable to create policy from policy crd: ", err)
return ctrl.Result{}, err
}

// Delete all policies except the current one.
for _, item := range policyList.Items {
item := item
if item.Name != resource {
policyLogger.Infof("Deleting policy %s", item.Name)
err := r.Delete(ctx, &item)
if err != nil {
policyLogger.Error("failed to delete Policy: ", err)
return ctrl.Result{}, err
}
policyLogger.Info("Deleted policy", "name", item.Name)
}
}

// returning empty result and no error to indicate we’ve successfully reconciled this object
return ctrl.Result{}, nil
}

Expand All @@ -111,19 +95,19 @@ func (r *PolicyReconciler) SetupWithManager(mgr ctrl.Manager) error {
Complete(r)
}

func policyAddOrReplace(spec configv1alpha1.PolicySpec, policyName string) error {
policyEnforcer, err := specToPolicyEnforcer(spec, policyName)
func policyAddOrReplace(spec configv1alpha1.PolicySpec) error {
policyEnforcer, err := specToPolicyEnforcer(spec)
if err != nil {
return fmt.Errorf("failed to create policy enforcer: %w", err)
}

ActivePolicy.Name = policyName
ActivePolicy.Name = spec.Type
ActivePolicy.Enforcer = policyEnforcer
return nil
}

func specToPolicyEnforcer(spec configv1alpha1.PolicySpec, policyName string) (policyprovider.PolicyProvider, error) {
policyConfig, err := rawToPolicyConfig(spec.Parameters.Raw, policyName)
func specToPolicyEnforcer(spec configv1alpha1.PolicySpec) (policyprovider.PolicyProvider, error) {
policyConfig, err := rawToPolicyConfig(spec.Parameters.Raw, spec.Type)
if err != nil {
return nil, fmt.Errorf("failed to parse policy config: %w", err)
}
Expand Down Expand Up @@ -160,7 +144,6 @@ func (p *policy) deletePolicy(resource string) {
}
}

// IsEmpty returns true if there is no policy set up.
func (p *policy) IsEmpty() bool {
return p.Name == "" && p.Enforcer == nil
}
19 changes: 11 additions & 8 deletions pkg/controllers/policy_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,28 +147,30 @@ func TestSpecToPolicyEnforcer(t *testing.T) {
{
name: "invalid spec",
policyName: policyName1,
spec: configv1alpha1.PolicySpec{},
spec: configv1alpha1.PolicySpec{
Type: policyName1,
},
expectErr: true,
expectProvider: false,
},
{
name: "non-supported policy",
policyName: policyName1,
spec: configv1alpha1.PolicySpec{
Parameters: runtime.RawExtension{
Raw: []byte("{\"name\": \"policy1\"}"),
},
Type: policyName1,
},
expectErr: true,
expectProvider: false,
},
{
name: "valid spec",
policyName: "configpolicy",
spec: configv1alpha1.PolicySpec{
Parameters: runtime.RawExtension{
Raw: []byte("{\"name\": \"configpolicy\"}"),
},
Type: "configpolicy",
},
expectErr: false,
expectProvider: true,
Expand All @@ -177,7 +179,7 @@ func TestSpecToPolicyEnforcer(t *testing.T) {

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
provider, err := specToPolicyEnforcer(tc.spec, tc.policyName)
provider, err := specToPolicyEnforcer(tc.spec)

if tc.expectErr != (err != nil) {
t.Fatalf("Expected error to be %t, got %t", tc.expectErr, err != nil)
Expand All @@ -198,8 +200,9 @@ func TestPolicyAddOrReplace(t *testing.T) {
}{
{
name: "invalid spec",
spec: configv1alpha1.PolicySpec{},
policyName: policyName1,
spec: configv1alpha1.PolicySpec{
Type: policyName1,
},
expectErr: true,
},
{
Expand All @@ -208,15 +211,15 @@ func TestPolicyAddOrReplace(t *testing.T) {
Parameters: runtime.RawExtension{
Raw: []byte("{\"name\": \"configpolicy\"}"),
},
Type: "configpolicy",
},
policyName: "configpolicy",
expectErr: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := policyAddOrReplace(tc.spec, tc.policyName)
err := policyAddOrReplace(tc.spec)

if tc.expectErr != (err != nil) {
t.Fatalf("Expected error to be %t, got %t", tc.expectErr, err != nil)
Expand Down

0 comments on commit 55b774d

Please sign in to comment.