Skip to content

Commit

Permalink
feat: Implement config pod status (#3544)
Browse files Browse the repository at this point in the history
Signed-off-by: Avinash Patnala <[email protected]>
Co-authored-by: Avinash Patnala <[email protected]>
Co-authored-by: Rita Zhang <[email protected]>
  • Loading branch information
3 people authored Oct 11, 2024
1 parent d1081fd commit 7d71ba2
Show file tree
Hide file tree
Showing 24 changed files with 1,063 additions and 43 deletions.
4 changes: 4 additions & 0 deletions apis/config/v1alpha1/config_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ limitations under the License.
package v1alpha1

import (
status "github.com/open-policy-agent/gatekeeper/v3/apis/status/v1beta1"
"github.com/open-policy-agent/gatekeeper/v3/pkg/wildcard"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand Down Expand Up @@ -82,6 +83,7 @@ type ReadinessSpec struct {

// ConfigStatus defines the observed state of Config.
type ConfigStatus struct { // Important: Run "make" to regenerate code after modifying this file
ByPod []status.ConfigPodStatusStatus `json:"byPod,omitempty"`
}

type GVK struct {
Expand All @@ -92,6 +94,8 @@ type GVK struct {

// +kubebuilder:resource:scope=Namespaced
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:storageversion

// Config is the Schema for the configs API.
type Config struct {
Expand Down
10 changes: 9 additions & 1 deletion apis/config/v1alpha1/zz_generated.deepcopy.go

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

94 changes: 94 additions & 0 deletions apis/status/v1beta1/configpodstatus_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package v1beta1

import (
"github.com/open-policy-agent/gatekeeper/v3/pkg/operations"
"github.com/open-policy-agent/gatekeeper/v3/pkg/util"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

// ConfigPodStatusStatus defines the observed state of ConfigPodStatus.

// +kubebuilder:object:generate=true

type ConfigPodStatusStatus struct {
ID string `json:"id,omitempty"`
ConfigUID types.UID `json:"configUID,omitempty"`
Operations []string `json:"operations,omitempty"`
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
Errors []*ConfigError `json:"errors,omitempty"`
}

// +kubebuilder:object:generate=true

type ConfigError struct {
Type string `json:"type,omitempty"`
Message string `json:"message"`
}

// ConfigPodStatus is the Schema for the configpodstatuses API.

// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Namespaced

type ConfigPodStatus struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Status ConfigPodStatusStatus `json:"status,omitempty"`
}

// ConfigPodStatusList contains a list of ConfigPodStatus.

// +kubebuilder:object:root=true
type ConfigPodStatusList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ConfigPodStatus `json:"items"`
}

func init() {
SchemeBuilder.Register(&ConfigPodStatus{}, &ConfigPodStatusList{})
}

// NewConfigStatusForPod returns an config status object
// that has been initialized with the bare minimum of fields to make it functional
// with the config status controller.
func NewConfigStatusForPod(pod *corev1.Pod, configNamespace string, configName string, scheme *runtime.Scheme) (*ConfigPodStatus, error) {
obj := &ConfigPodStatus{}
name, err := KeyForConfig(pod.Name, configNamespace, configName)
if err != nil {
return nil, err
}
obj.SetName(name)
obj.SetNamespace(util.GetNamespace())
obj.Status.ID = pod.Name
obj.Status.Operations = operations.AssignedStringList()
obj.SetLabels(map[string]string{
ConfigNameLabel: configName,
PodLabel: pod.Name,
})

if err := controllerutil.SetOwnerReference(pod, obj, scheme); err != nil {
return nil, err
}

return obj, nil
}

// KeyForConfig returns a unique status object name given the Pod ID and
// a config object.
// The object name must satisfy RFC 1123 Label Names spec
// (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/)
// and Kubernetes validation rules for object names.
//
// It's possible that dash packing/unpacking would result in a name
// that exceeds the maximum length allowed, but for Config resources,
// the configName should always be "config", and namespace would be "gatekeeper-system",
// so this validation will hold.
func KeyForConfig(id string, configNamespace string, configName string) (string, error) {
return DashPacker(id, configNamespace, configName)
}
69 changes: 69 additions & 0 deletions apis/status/v1beta1/configpodstatus_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package v1beta1_test

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/open-policy-agent/gatekeeper/v3/apis/status/v1beta1"
"github.com/open-policy-agent/gatekeeper/v3/pkg/fakes"
"github.com/open-policy-agent/gatekeeper/v3/pkg/operations"
"github.com/open-policy-agent/gatekeeper/v3/test/testutils"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

func TestNewConfigStatusForPod(t *testing.T) {
const podName = "some-gk-pod"
const podNS = "a-gk-namespace"
const configName = "a-config"
const configNameSpace = "a-gk-ns"

testutils.Setenv(t, "POD_NAMESPACE", podNS)

scheme := runtime.NewScheme()
err := v1beta1.AddToScheme(scheme)
if err != nil {
t.Fatal(err)
}

err = corev1.AddToScheme(scheme)
if err != nil {
t.Fatal(err)
}

pod := fakes.Pod(
fakes.WithNamespace(podNS),
fakes.WithName(podName),
)

expectedStatus := &v1beta1.ConfigPodStatus{}
expectedStatus.SetName("some--gk--pod-a--gk--ns-a--config")
expectedStatus.SetNamespace(podNS)
expectedStatus.Status.ID = podName
expectedStatus.Status.Operations = operations.AssignedStringList()
expectedStatus.SetLabels(map[string]string{
v1beta1.ConfigNameLabel: configName,
v1beta1.PodLabel: podName,
})

err = controllerutil.SetOwnerReference(pod, expectedStatus, scheme)
if err != nil {
t.Fatal(err)
}

status, err := v1beta1.NewConfigStatusForPod(pod, configNameSpace, configName, scheme)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(expectedStatus, status); diff != "" {
t.Fatal(diff)
}
n, err := v1beta1.KeyForConfig(podName, configNameSpace, configName)
if err != nil {
t.Fatal(err)
}
if status.Name != n {
t.Fatal("got status.Name != n, want equal")
}
}
1 change: 1 addition & 0 deletions apis/status/v1beta1/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1beta1

// Label keys used for internal gatekeeper operations.
const (
ConfigNameLabel = "internal.gatekeeper.sh/config-name"
ExpansionTemplateNameLabel = "internal.gatekeeper.sh/expansiontemplate-name"
ConstraintNameLabel = "internal.gatekeeper.sh/constraint-name"
ConstraintKindLabel = "internal.gatekeeper.sh/constraint-kind"
Expand Down
104 changes: 104 additions & 0 deletions apis/status/v1beta1/zz_generated.deepcopy.go

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

6 changes: 6 additions & 0 deletions cmd/build/helmify/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ patchesJson6902:
kind: CustomResourceDefinition
name: expansiontemplatepodstatuses.status.gatekeeper.sh
path: labels_patch.yaml
- target:
group: apiextensions.k8s.io
version: v1
kind: CustomResourceDefinition
name: configpodstatuses.status.gatekeeper.sh
path: labels_patch.yaml
- target:
group: apiextensions.k8s.io
version: v1
Expand Down
34 changes: 34 additions & 0 deletions config/crd/bases/config.gatekeeper.sh_configs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,41 @@ spec:
type: object
status:
description: ConfigStatus defines the observed state of Config.
properties:
byPod:
items:
properties:
configUID:
description: |-
UID is a type that holds unique ID values, including UUIDs. Because we
don't ONLY use UUIDs, this is an alias to string. Being a type captures
intent and helps make sure that UIDs and names do not get conflated.
type: string
errors:
items:
properties:
message:
type: string
type:
type: string
required:
- message
type: object
type: array
id:
type: string
observedGeneration:
format: int64
type: integer
operations:
items:
type: string
type: array
type: object
type: array
type: object
type: object
served: true
storage: true
subresources:
status: {}
Loading

0 comments on commit 7d71ba2

Please sign in to comment.