Add Flows and Actions
[ZITADOPER-4]
This commit is contained in:
18
src/PROJECT
18
src/PROJECT
@@ -65,4 +65,22 @@ resources:
|
||||
kind: APIApp
|
||||
path: bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1
|
||||
version: v1alpha1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: true
|
||||
controller: true
|
||||
domain: topmanage.com
|
||||
group: zitadel
|
||||
kind: Action
|
||||
path: bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1
|
||||
version: v1alpha1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: true
|
||||
controller: true
|
||||
domain: topmanage.com
|
||||
group: zitadel
|
||||
kind: Flow
|
||||
path: bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1
|
||||
version: v1alpha1
|
||||
version: "3"
|
||||
|
||||
109
src/api/v1alpha1/action_types.go
Normal file
109
src/api/v1alpha1/action_types.go
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2024.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
// ActionSpec defines the desired state of Action
|
||||
type ActionSpec struct {
|
||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
// +kubebuilder:validation:Required
|
||||
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||
OrganizationRef OrganizationRef `json:"organizationRef"`
|
||||
Script string `json:"script"`
|
||||
// +kubebuilder:default=true
|
||||
AllowedToFail bool `json:"allowedToFail"`
|
||||
// +kubebuilder:validation:Type=string
|
||||
// +kubebuilder:validation:Format=duration
|
||||
Timeout *metav1.Duration `json:"timeout"`
|
||||
}
|
||||
|
||||
// ActionStatus defines the observed state of Action
|
||||
type ActionStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
// +optional
|
||||
// +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"}
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
// +kubebuilder:default=""
|
||||
ActionId string `json:"actionId"`
|
||||
}
|
||||
|
||||
func (d *ActionStatus) SetCondition(condition metav1.Condition) {
|
||||
if d.Conditions == nil {
|
||||
d.Conditions = make([]metav1.Condition, 0)
|
||||
}
|
||||
meta.SetStatusCondition(&d.Conditions, condition)
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
//+kubebuilder:subresource:status
|
||||
|
||||
// Action is the Schema for the actions API
|
||||
type Action struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ActionSpec `json:"spec,omitempty"`
|
||||
Status ActionStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
func (d *Action) IsBeingDeleted() bool {
|
||||
return !d.DeletionTimestamp.IsZero()
|
||||
}
|
||||
|
||||
func (d *Action) IsReady() bool {
|
||||
return meta.IsStatusConditionTrue(d.Status.Conditions, ConditionTypeReady)
|
||||
}
|
||||
|
||||
func (d *Action) ZitadelClusterRef(ctx context.Context, refresolver *RefResolver) (*ZitadelClusterRef, error) {
|
||||
org, err := refresolver.OrganizationRef(ctx, &d.Spec.OrganizationRef, d.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if org.Status.OrgId == "" {
|
||||
return nil, fmt.Errorf("Organization has not been created yet...")
|
||||
}
|
||||
|
||||
ref, err := org.ZitadelClusterRef(ctx, refresolver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
|
||||
// ActionList contains a list of Action
|
||||
type ActionList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Action `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Action{}, &ActionList{})
|
||||
}
|
||||
106
src/api/v1alpha1/flow_types.go
Normal file
106
src/api/v1alpha1/flow_types.go
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright 2024.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
// FlowSpec defines the desired state of Flow
|
||||
type FlowSpec struct {
|
||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
// +kubebuilder:validation:Required
|
||||
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||
OrganizationRef OrganizationRef `json:"organizationRef"`
|
||||
// +kubebuilder:validation:Enum=FLOW_TYPE_EXTERNAL_AUTHENTICATION;"1";"2";"3";"4"
|
||||
FlowType string `json:"flowType"`
|
||||
// +kubebuilder:validation:Enum=TRIGGER_TYPE_POST_AUTHENTICATION;TRIGGER_TYPE_PRE_CREATION;TRIGGER_TYPE_POST_CREATION;TRIGGER_TYPE_POST_AUTHENTICATION;TRIGGER_TYPE_PRE_CREATION;TRIGGER_TYPE_POST_CREATION;"1";"2";"3";"4";"5";"6"
|
||||
TriggerType string `json:"triggerType"`
|
||||
ActionRefs []ActionRef `json:"actionRefs"`
|
||||
}
|
||||
|
||||
// FlowStatus defines the observed state of Flow
|
||||
type FlowStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
// +optional
|
||||
// +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"}
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
func (d *FlowStatus) SetCondition(condition metav1.Condition) {
|
||||
if d.Conditions == nil {
|
||||
d.Conditions = make([]metav1.Condition, 0)
|
||||
}
|
||||
meta.SetStatusCondition(&d.Conditions, condition)
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
//+kubebuilder:subresource:status
|
||||
|
||||
// Flow is the Schema for the flows API
|
||||
type Flow struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec FlowSpec `json:"spec,omitempty"`
|
||||
Status FlowStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
func (d *Flow) IsBeingDeleted() bool {
|
||||
return !d.DeletionTimestamp.IsZero()
|
||||
}
|
||||
|
||||
func (d *Flow) IsReady() bool {
|
||||
return meta.IsStatusConditionTrue(d.Status.Conditions, ConditionTypeReady)
|
||||
}
|
||||
|
||||
func (d *Flow) ZitadelClusterRef(ctx context.Context, refresolver *RefResolver) (*ZitadelClusterRef, error) {
|
||||
org, err := refresolver.OrganizationRef(ctx, &d.Spec.OrganizationRef, d.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if org.Status.OrgId == "" {
|
||||
return nil, fmt.Errorf("Organization has not been created yet...")
|
||||
}
|
||||
|
||||
ref, err := org.ZitadelClusterRef(ctx, refresolver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
|
||||
// FlowList contains a list of Flow
|
||||
type FlowList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Flow `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Flow{}, &FlowList{})
|
||||
}
|
||||
@@ -38,3 +38,10 @@ type ProjectRef struct {
|
||||
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||
corev1.ObjectReference `json:",inline"`
|
||||
}
|
||||
|
||||
type ActionRef struct {
|
||||
// ObjectReference is a reference to a object.
|
||||
// +kubebuilder:validation:Required
|
||||
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||
corev1.ObjectReference `json:",inline"`
|
||||
}
|
||||
|
||||
@@ -63,6 +63,27 @@ func (r *RefResolver) OIDCAppRef(ctx context.Context, ref *OIDCAppRef,
|
||||
return &zitadel, nil
|
||||
}
|
||||
|
||||
func (r *RefResolver) ActionRef(ctx context.Context, ref *ActionRef,
|
||||
namespace string) (*Action, error) {
|
||||
if ref.Kind != "" && ref.Kind != "Action" {
|
||||
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.Kind)
|
||||
}
|
||||
|
||||
key := types.NamespacedName{
|
||||
Name: ref.Name,
|
||||
Namespace: namespace,
|
||||
}
|
||||
if ref.Namespace != "" {
|
||||
key.Namespace = ref.Namespace
|
||||
}
|
||||
|
||||
var zitadel Action
|
||||
if err := r.client.Get(ctx, key, &zitadel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &zitadel, nil
|
||||
}
|
||||
|
||||
func (r *RefResolver) ProjectRef(ctx context.Context, ref *ProjectRef,
|
||||
namespace string) (*Project, error) {
|
||||
if ref.Kind != "" && ref.Kind != "Project" {
|
||||
|
||||
@@ -123,6 +123,124 @@ func (in *APIAppStatus) DeepCopy() *APIAppStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Action) DeepCopyInto(out *Action) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Action.
|
||||
func (in *Action) DeepCopy() *Action {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Action)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Action) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ActionList) DeepCopyInto(out *ActionList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Action, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActionList.
|
||||
func (in *ActionList) DeepCopy() *ActionList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ActionList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ActionList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ActionRef) DeepCopyInto(out *ActionRef) {
|
||||
*out = *in
|
||||
out.ObjectReference = in.ObjectReference
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActionRef.
|
||||
func (in *ActionRef) DeepCopy() *ActionRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ActionRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ActionSpec) DeepCopyInto(out *ActionSpec) {
|
||||
*out = *in
|
||||
out.OrganizationRef = in.OrganizationRef
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActionSpec.
|
||||
func (in *ActionSpec) DeepCopy() *ActionSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ActionSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ActionStatus) DeepCopyInto(out *ActionStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActionStatus.
|
||||
func (in *ActionStatus) DeepCopy() *ActionStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ActionStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CrdbClusterRef) DeepCopyInto(out *CrdbClusterRef) {
|
||||
*out = *in
|
||||
@@ -154,6 +272,108 @@ func (in *DomainSettings) DeepCopy() *DomainSettings {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Flow) DeepCopyInto(out *Flow) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Flow.
|
||||
func (in *Flow) DeepCopy() *Flow {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Flow)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Flow) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FlowList) DeepCopyInto(out *FlowList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Flow, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlowList.
|
||||
func (in *FlowList) DeepCopy() *FlowList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FlowList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *FlowList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FlowSpec) DeepCopyInto(out *FlowSpec) {
|
||||
*out = *in
|
||||
out.OrganizationRef = in.OrganizationRef
|
||||
if in.ActionRefs != nil {
|
||||
in, out := &in.ActionRefs, &out.ActionRefs
|
||||
*out = make([]ActionRef, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlowSpec.
|
||||
func (in *FlowSpec) DeepCopy() *FlowSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FlowSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FlowStatus) DeepCopyInto(out *FlowStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlowStatus.
|
||||
func (in *FlowStatus) DeepCopy() *FlowStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FlowStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Grant) DeepCopyInto(out *Grant) {
|
||||
*out = *in
|
||||
|
||||
@@ -137,6 +137,14 @@ func main() {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "APIApp")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err = controller.NewActionReconciler(client, refResolver, builder, conditionReady, requeueZitadel).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Action")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err = controller.NewFlowReconciler(client, refResolver, builder, conditionReady, requeueZitadel).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Flow")
|
||||
os.Exit(1)
|
||||
}
|
||||
//+kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
|
||||
174
src/config/crd/bases/zitadel.topmanage.com_actions.yaml
Normal file
174
src/config/crd/bases/zitadel.topmanage.com_actions.yaml
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.1
|
||||
creationTimestamp: null
|
||||
name: actions.zitadel.topmanage.com
|
||||
spec:
|
||||
group: zitadel.topmanage.com
|
||||
names:
|
||||
kind: Action
|
||||
listKind: ActionList
|
||||
plural: actions
|
||||
singular: action
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Action is the Schema for the actions 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: ActionSpec defines the desired state of Action
|
||||
properties:
|
||||
allowedToFail:
|
||||
default: true
|
||||
type: boolean
|
||||
organizationRef:
|
||||
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
Important: Run "make" to regenerate code after modifying this file'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: API version of the referent.
|
||||
type: string
|
||||
fieldPath:
|
||||
description: 'If referring to a piece of an object instead of
|
||||
an entire object, this string should contain a valid JSON/Go
|
||||
field access statement, such as desiredState.manifest.containers[2].
|
||||
For example, if the object reference is to a container within
|
||||
a pod, this would take on a value like: "spec.containers{name}"
|
||||
(where "name" refers to the name of the container that triggered
|
||||
the event) or if no container name is specified "spec.containers[2]"
|
||||
(container with index 2 in this pod). This syntax is chosen
|
||||
only to have some well-defined way of referencing a part of
|
||||
an object. TODO: this design is not final and this field is
|
||||
subject to change in the future.'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
|
||||
type: string
|
||||
namespace:
|
||||
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
|
||||
type: string
|
||||
resourceVersion:
|
||||
description: 'Specific resourceVersion to which this reference
|
||||
is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
|
||||
type: string
|
||||
uid:
|
||||
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
script:
|
||||
type: string
|
||||
timeout:
|
||||
format: duration
|
||||
type: string
|
||||
required:
|
||||
- allowedToFail
|
||||
- organizationRef
|
||||
- script
|
||||
- timeout
|
||||
type: object
|
||||
status:
|
||||
description: ActionStatus defines the observed state of Action
|
||||
properties:
|
||||
actionId:
|
||||
default: ""
|
||||
type: string
|
||||
conditions:
|
||||
description: 'INSERT ADDITIONAL STATUS FIELD - define observed state
|
||||
of cluster Important: Run "make" to regenerate code after modifying
|
||||
this file'
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource. --- This struct is intended for direct
|
||||
use as an array at the field path .status.conditions. For example,
|
||||
\n type FooStatus struct{ // Represents the observations of a
|
||||
foo's current state. // Known .status.conditions.type are: \"Available\",
|
||||
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
|
||||
// +listType=map // +listMapKey=type Conditions []metav1.Condition
|
||||
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
|
||||
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: lastTransitionTime is the last time the condition
|
||||
transitioned from one status to another. This should be when
|
||||
the underlying condition changed. If that is not known, then
|
||||
using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: message is a human readable message indicating
|
||||
details about the transition. This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: observedGeneration represents the .metadata.generation
|
||||
that the condition was set based upon. For instance, if .metadata.generation
|
||||
is currently 12, but the .status.conditions[x].observedGeneration
|
||||
is 9, the condition is out of date with respect to the current
|
||||
state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: reason contains a programmatic identifier indicating
|
||||
the reason for the condition's last transition. Producers
|
||||
of specific condition types may define expected values and
|
||||
meanings for this field, and whether the values are considered
|
||||
a guaranteed API. The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
--- Many .condition.type values are consistent across resources
|
||||
like Available, but because arbitrary conditions can be useful
|
||||
(see .node.status.conditions), the ability to deconflict is
|
||||
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- actionId
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
222
src/config/crd/bases/zitadel.topmanage.com_flows.yaml
Normal file
222
src/config/crd/bases/zitadel.topmanage.com_flows.yaml
Normal file
@@ -0,0 +1,222 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.1
|
||||
creationTimestamp: null
|
||||
name: flows.zitadel.topmanage.com
|
||||
spec:
|
||||
group: zitadel.topmanage.com
|
||||
names:
|
||||
kind: Flow
|
||||
listKind: FlowList
|
||||
plural: flows
|
||||
singular: flow
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Flow is the Schema for the flows 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: FlowSpec defines the desired state of Flow
|
||||
properties:
|
||||
actionRefs:
|
||||
items:
|
||||
properties:
|
||||
apiVersion:
|
||||
description: API version of the referent.
|
||||
type: string
|
||||
fieldPath:
|
||||
description: 'If referring to a piece of an object instead of
|
||||
an entire object, this string should contain a valid JSON/Go
|
||||
field access statement, such as desiredState.manifest.containers[2].
|
||||
For example, if the object reference is to a container within
|
||||
a pod, this would take on a value like: "spec.containers{name}"
|
||||
(where "name" refers to the name of the container that triggered
|
||||
the event) or if no container name is specified "spec.containers[2]"
|
||||
(container with index 2 in this pod). This syntax is chosen
|
||||
only to have some well-defined way of referencing a part of
|
||||
an object. TODO: this design is not final and this field is
|
||||
subject to change in the future.'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
|
||||
type: string
|
||||
namespace:
|
||||
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
|
||||
type: string
|
||||
resourceVersion:
|
||||
description: 'Specific resourceVersion to which this reference
|
||||
is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
|
||||
type: string
|
||||
uid:
|
||||
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
type: array
|
||||
flowType:
|
||||
enum:
|
||||
- FLOW_TYPE_EXTERNAL_AUTHENTICATION
|
||||
- "1"
|
||||
- "2"
|
||||
- "3"
|
||||
- "4"
|
||||
type: string
|
||||
organizationRef:
|
||||
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
Important: Run "make" to regenerate code after modifying this file'
|
||||
properties:
|
||||
apiVersion:
|
||||
description: API version of the referent.
|
||||
type: string
|
||||
fieldPath:
|
||||
description: 'If referring to a piece of an object instead of
|
||||
an entire object, this string should contain a valid JSON/Go
|
||||
field access statement, such as desiredState.manifest.containers[2].
|
||||
For example, if the object reference is to a container within
|
||||
a pod, this would take on a value like: "spec.containers{name}"
|
||||
(where "name" refers to the name of the container that triggered
|
||||
the event) or if no container name is specified "spec.containers[2]"
|
||||
(container with index 2 in this pod). This syntax is chosen
|
||||
only to have some well-defined way of referencing a part of
|
||||
an object. TODO: this design is not final and this field is
|
||||
subject to change in the future.'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
|
||||
type: string
|
||||
namespace:
|
||||
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
|
||||
type: string
|
||||
resourceVersion:
|
||||
description: 'Specific resourceVersion to which this reference
|
||||
is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
|
||||
type: string
|
||||
uid:
|
||||
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
triggerType:
|
||||
enum:
|
||||
- TRIGGER_TYPE_POST_AUTHENTICATION
|
||||
- TRIGGER_TYPE_PRE_CREATION
|
||||
- TRIGGER_TYPE_POST_CREATION
|
||||
- TRIGGER_TYPE_POST_AUTHENTICATION
|
||||
- TRIGGER_TYPE_PRE_CREATION
|
||||
- TRIGGER_TYPE_POST_CREATION
|
||||
- "1"
|
||||
- "2"
|
||||
- "3"
|
||||
- "4"
|
||||
- "5"
|
||||
- "6"
|
||||
type: string
|
||||
required:
|
||||
- actionRefs
|
||||
- flowType
|
||||
- organizationRef
|
||||
- triggerType
|
||||
type: object
|
||||
status:
|
||||
description: FlowStatus defines the observed state of Flow
|
||||
properties:
|
||||
conditions:
|
||||
description: 'INSERT ADDITIONAL STATUS FIELD - define observed state
|
||||
of cluster Important: Run "make" to regenerate code after modifying
|
||||
this file'
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource. --- This struct is intended for direct
|
||||
use as an array at the field path .status.conditions. For example,
|
||||
\n type FooStatus struct{ // Represents the observations of a
|
||||
foo's current state. // Known .status.conditions.type are: \"Available\",
|
||||
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
|
||||
// +listType=map // +listMapKey=type Conditions []metav1.Condition
|
||||
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
|
||||
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: lastTransitionTime is the last time the condition
|
||||
transitioned from one status to another. This should be when
|
||||
the underlying condition changed. If that is not known, then
|
||||
using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: message is a human readable message indicating
|
||||
details about the transition. This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: observedGeneration represents the .metadata.generation
|
||||
that the condition was set based upon. For instance, if .metadata.generation
|
||||
is currently 12, but the .status.conditions[x].observedGeneration
|
||||
is 9, the condition is out of date with respect to the current
|
||||
state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: reason contains a programmatic identifier indicating
|
||||
the reason for the condition's last transition. Producers
|
||||
of specific condition types may define expected values and
|
||||
meanings for this field, and whether the values are considered
|
||||
a guaranteed API. The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
--- Many .condition.type values are consistent across resources
|
||||
like Available, but because arbitrary conditions can be useful
|
||||
(see .node.status.conditions), the ability to deconflict is
|
||||
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -8,6 +8,8 @@ resources:
|
||||
- bases/zitadel.topmanage.com_oidcapps.yaml
|
||||
- bases/zitadel.topmanage.com_machineusers.yaml
|
||||
- bases/zitadel.topmanage.com_apiapps.yaml
|
||||
- bases/zitadel.topmanage.com_actions.yaml
|
||||
- bases/zitadel.topmanage.com_flows.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizeresource
|
||||
|
||||
patchesStrategicMerge:
|
||||
@@ -19,6 +21,8 @@ patchesStrategicMerge:
|
||||
#- patches/webhook_in_oidcapps.yaml
|
||||
#- patches/webhook_in_machineusers.yaml
|
||||
#- patches/webhook_in_apiapps.yaml
|
||||
#- patches/webhook_in_actions.yaml
|
||||
#- patches/webhook_in_flows.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizewebhookpatch
|
||||
|
||||
# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
|
||||
@@ -29,6 +33,8 @@ patchesStrategicMerge:
|
||||
#- patches/cainjection_in_oidcapps.yaml
|
||||
#- patches/cainjection_in_machineusers.yaml
|
||||
#- patches/cainjection_in_apiapps.yaml
|
||||
#- patches/cainjection_in_actions.yaml
|
||||
#- patches/cainjection_in_flows.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizecainjectionpatch
|
||||
|
||||
# the following config is for teaching kustomize how to do kustomization for CRDs.
|
||||
|
||||
7
src/config/crd/patches/cainjection_in_actions.yaml
Normal file
7
src/config/crd/patches/cainjection_in_actions.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
# The following patch adds a directive for certmanager to inject CA into the CRD
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME
|
||||
name: actions.zitadel.topmanage.com
|
||||
7
src/config/crd/patches/cainjection_in_flows.yaml
Normal file
7
src/config/crd/patches/cainjection_in_flows.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
# The following patch adds a directive for certmanager to inject CA into the CRD
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME
|
||||
name: flows.zitadel.topmanage.com
|
||||
16
src/config/crd/patches/webhook_in_actions.yaml
Normal file
16
src/config/crd/patches/webhook_in_actions.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
# The following patch enables a conversion webhook for the CRD
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: actions.zitadel.topmanage.com
|
||||
spec:
|
||||
conversion:
|
||||
strategy: Webhook
|
||||
webhook:
|
||||
clientConfig:
|
||||
service:
|
||||
namespace: system
|
||||
name: webhook-service
|
||||
path: /convert
|
||||
conversionReviewVersions:
|
||||
- v1
|
||||
16
src/config/crd/patches/webhook_in_flows.yaml
Normal file
16
src/config/crd/patches/webhook_in_flows.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
# The following patch enables a conversion webhook for the CRD
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: flows.zitadel.topmanage.com
|
||||
spec:
|
||||
conversion:
|
||||
strategy: Webhook
|
||||
webhook:
|
||||
clientConfig:
|
||||
service:
|
||||
namespace: system
|
||||
name: webhook-service
|
||||
path: /convert
|
||||
conversionReviewVersions:
|
||||
- v1
|
||||
31
src/config/rbac/action_editor_role.yaml
Normal file
31
src/config/rbac/action_editor_role.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
# permissions for end users to edit actions.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: clusterrole
|
||||
app.kubernetes.io/instance: action-editor-role
|
||||
app.kubernetes.io/component: rbac
|
||||
app.kubernetes.io/created-by: src
|
||||
app.kubernetes.io/part-of: src
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: action-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- actions
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- actions/status
|
||||
verbs:
|
||||
- get
|
||||
27
src/config/rbac/action_viewer_role.yaml
Normal file
27
src/config/rbac/action_viewer_role.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
# permissions for end users to view actions.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: clusterrole
|
||||
app.kubernetes.io/instance: action-viewer-role
|
||||
app.kubernetes.io/component: rbac
|
||||
app.kubernetes.io/created-by: src
|
||||
app.kubernetes.io/part-of: src
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: action-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- actions
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- actions/status
|
||||
verbs:
|
||||
- get
|
||||
31
src/config/rbac/flow_editor_role.yaml
Normal file
31
src/config/rbac/flow_editor_role.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
# permissions for end users to edit flows.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: clusterrole
|
||||
app.kubernetes.io/instance: flow-editor-role
|
||||
app.kubernetes.io/component: rbac
|
||||
app.kubernetes.io/created-by: src
|
||||
app.kubernetes.io/part-of: src
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: flow-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- flows
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- flows/status
|
||||
verbs:
|
||||
- get
|
||||
27
src/config/rbac/flow_viewer_role.yaml
Normal file
27
src/config/rbac/flow_viewer_role.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
# permissions for end users to view flows.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: clusterrole
|
||||
app.kubernetes.io/instance: flow-viewer-role
|
||||
app.kubernetes.io/component: rbac
|
||||
app.kubernetes.io/created-by: src
|
||||
app.kubernetes.io/part-of: src
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: flow-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- flows
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- flows/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -170,6 +170,32 @@ rules:
|
||||
- list
|
||||
- patch
|
||||
- watch
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- actions
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- actions/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- actions/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
@@ -196,6 +222,32 @@ rules:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- flows
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- flows/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
- flows/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- zitadel.topmanage.com
|
||||
resources:
|
||||
|
||||
@@ -6,4 +6,6 @@ resources:
|
||||
- zitadel_v1alpha1_oidcapp.yaml
|
||||
- zitadel_v1alpha1_machineuser.yaml
|
||||
- zitadel_v1alpha1_apiapp.yaml
|
||||
- zitadel_v1alpha1_action.yaml
|
||||
- zitadel_v1alpha1_flow.yaml
|
||||
#+kubebuilder:scaffold:manifestskustomizesamples
|
||||
|
||||
12
src/config/samples/zitadel_v1alpha1_action.yaml
Normal file
12
src/config/samples/zitadel_v1alpha1_action.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
apiVersion: zitadel.topmanage.com/v1alpha1
|
||||
kind: Action
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: action
|
||||
app.kubernetes.io/instance: action-sample
|
||||
app.kubernetes.io/part-of: src
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
app.kubernetes.io/created-by: src
|
||||
name: action-sample
|
||||
spec:
|
||||
# TODO(user): Add fields here
|
||||
12
src/config/samples/zitadel_v1alpha1_flow.yaml
Normal file
12
src/config/samples/zitadel_v1alpha1_flow.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
apiVersion: zitadel.topmanage.com/v1alpha1
|
||||
kind: Flow
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: flow
|
||||
app.kubernetes.io/instance: flow-sample
|
||||
app.kubernetes.io/part-of: src
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
app.kubernetes.io/created-by: src
|
||||
name: flow-sample
|
||||
spec:
|
||||
# TODO(user): Add fields here
|
||||
191
src/internal/controller/action_controller.go
Normal file
191
src/internal/controller/action_controller.go
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
Copyright 2024.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
zitadelv1alpha1 "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1"
|
||||
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/builder"
|
||||
condition "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/condition"
|
||||
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/controller/zitadel"
|
||||
"github.com/zitadel/zitadel-go/v2/pkg/client/management"
|
||||
"github.com/zitadel/zitadel-go/v2/pkg/client/middleware"
|
||||
pb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
|
||||
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
)
|
||||
|
||||
// ActionReconciler reconciles a Action object
|
||||
type ActionReconciler struct {
|
||||
client.Client
|
||||
RefResolver *zitadelv1alpha1.RefResolver
|
||||
ConditionReady *condition.Ready
|
||||
RequeueInterval time.Duration
|
||||
Builder *builder.Builder
|
||||
}
|
||||
|
||||
func NewActionReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder, conditionReady *condition.Ready,
|
||||
requeueInterval time.Duration) *ActionReconciler {
|
||||
return &ActionReconciler{
|
||||
Client: client,
|
||||
RefResolver: refResolver,
|
||||
ConditionReady: conditionReady,
|
||||
RequeueInterval: requeueInterval,
|
||||
Builder: builder,
|
||||
}
|
||||
}
|
||||
|
||||
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=actions,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=actions/status,verbs=get;update;patch
|
||||
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=actions/finalizers,verbs=update
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
func (r *ActionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
var Action zitadelv1alpha1.Action
|
||||
if err := r.Get(ctx, req.NamespacedName, &Action); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
wr := newWrappedActionReconciler(r.Client, r.RefResolver, r.Builder, &Action)
|
||||
wf := newWrappedActionFinalizer(r.Client, &Action, r.RefResolver)
|
||||
tf := zitadel.NewZitadelFinalizer(r.Client, wf)
|
||||
tr := zitadel.NewZitadelReconciler(r.Client, r.ConditionReady, wr, tf, r.RequeueInterval)
|
||||
|
||||
result, err := tr.Reconcile(ctx, &Action)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("error reconciling in ActionReconciler: %v", err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type wrappedActionReconciler struct {
|
||||
client.Client
|
||||
refResolver *zitadelv1alpha1.RefResolver
|
||||
Action *zitadelv1alpha1.Action
|
||||
Builder *builder.Builder
|
||||
}
|
||||
|
||||
func newWrappedActionReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder,
|
||||
Action *zitadelv1alpha1.Action) zitadel.WrappedReconciler {
|
||||
return &wrappedActionReconciler{
|
||||
Client: client,
|
||||
refResolver: refResolver,
|
||||
Action: Action,
|
||||
Builder: builder,
|
||||
}
|
||||
}
|
||||
|
||||
type actionReoncilePhase struct {
|
||||
Name string
|
||||
Reconcile func(context.Context, *management.Client) error
|
||||
}
|
||||
|
||||
func (wr *wrappedActionReconciler) Reconcile(ctx context.Context, ztdClient *management.Client) error {
|
||||
phases := []actionReoncilePhase{
|
||||
{
|
||||
Name: "action",
|
||||
Reconcile: wr.reconcileAction,
|
||||
},
|
||||
}
|
||||
for _, p := range phases {
|
||||
err := p.Reconcile(ctx, ztdClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wr *wrappedActionReconciler) reconcileAction(ctx context.Context, ztdClient *management.Client) error {
|
||||
org, err := wr.refResolver.OrganizationRef(ctx, &wr.Action.Spec.OrganizationRef, wr.Action.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx = middleware.SetOrgID(ctx, org.Status.OrgId)
|
||||
|
||||
if wr.Action.Status.ActionId != "" {
|
||||
p, err := ztdClient.GetAction(ctx, &pb.GetActionRequest{Id: wr.Action.Status.ActionId})
|
||||
if p != nil {
|
||||
_, err := ztdClient.UpdateAction(ctx,
|
||||
&pb.UpdateActionRequest{
|
||||
Id: p.Action.Id,
|
||||
Name: wr.Action.Name,
|
||||
Script: wr.Action.Spec.Script,
|
||||
Timeout: durationpb.New(wr.Action.Spec.Timeout.Duration),
|
||||
AllowedToFail: wr.Action.Spec.AllowedToFail,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "No changes") {
|
||||
return fmt.Errorf("Error updating Action: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "not found") {
|
||||
return fmt.Errorf("Error getting Action: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := ztdClient.CreateAction(ctx,
|
||||
&pb.CreateActionRequest{
|
||||
Name: wr.Action.Name,
|
||||
Script: wr.Action.Spec.Script,
|
||||
Timeout: durationpb.New(wr.Action.Spec.Timeout.Duration),
|
||||
AllowedToFail: wr.Action.Spec.AllowedToFail,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "AlreadyExists") {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("error creating action in Zitadel: %v", err)
|
||||
}
|
||||
patch := ctrlClient.MergeFrom(wr.Action.DeepCopy())
|
||||
wr.Action.Status.ActionId = resp.Id
|
||||
return wr.Client.Status().Patch(ctx, wr.Action, patch)
|
||||
}
|
||||
|
||||
func (wr *wrappedActionReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||
patch := client.MergeFrom(wr.Action.DeepCopy())
|
||||
patcher(&wr.Action.Status)
|
||||
|
||||
if err := wr.Client.Status().Patch(ctx, wr.Action, patch); err != nil {
|
||||
return fmt.Errorf("error patching Action status: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *ActionReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&zitadelv1alpha1.Action{}).
|
||||
WithOptions(controller.Options{RateLimiter: workqueue.NewItemExponentialFailureRateLimiter(time.Millisecond*500, time.Minute*3)}).
|
||||
Complete(r)
|
||||
}
|
||||
94
src/internal/controller/action_controller_finalizer.go
Normal file
94
src/internal/controller/action_controller_finalizer.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
zitadelv1alpha1 "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1"
|
||||
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/controller/zitadel"
|
||||
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/zitadel/zitadel-go/v2/pkg/client/management"
|
||||
"github.com/zitadel/zitadel-go/v2/pkg/client/middleware"
|
||||
pb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
const (
|
||||
actionFinalizerName = "action.zitadel.topmanage.com/action"
|
||||
)
|
||||
|
||||
type wrappedActionFinalizer struct {
|
||||
client.Client
|
||||
action *zitadelv1alpha1.Action
|
||||
refresolver *zitadelv1alpha1.RefResolver
|
||||
}
|
||||
|
||||
func newWrappedActionFinalizer(client client.Client, action *zitadelv1alpha1.Action, refresolver *zitadelv1alpha1.RefResolver) zitadel.WrappedFinalizer {
|
||||
return &wrappedActionFinalizer{
|
||||
Client: client,
|
||||
action: action,
|
||||
refresolver: refresolver,
|
||||
}
|
||||
}
|
||||
|
||||
func (wf *wrappedActionFinalizer) AddFinalizer(ctx context.Context) error {
|
||||
if wf.ContainsFinalizer() {
|
||||
return nil
|
||||
}
|
||||
return wf.patch(ctx, wf.action, func(action *zitadelv1alpha1.Action) {
|
||||
controllerutil.AddFinalizer(action, actionFinalizerName)
|
||||
})
|
||||
}
|
||||
|
||||
func (wf *wrappedActionFinalizer) RemoveFinalizer(ctx context.Context) error {
|
||||
if !wf.ContainsFinalizer() {
|
||||
return nil
|
||||
}
|
||||
return wf.patch(ctx, wf.action, func(action *zitadelv1alpha1.Action) {
|
||||
controllerutil.RemoveFinalizer(wf.action, actionFinalizerName)
|
||||
})
|
||||
}
|
||||
|
||||
func (wr *wrappedActionFinalizer) ContainsFinalizer() bool {
|
||||
return controllerutil.ContainsFinalizer(wr.action, actionFinalizerName)
|
||||
}
|
||||
|
||||
func (wf *wrappedActionFinalizer) Reconcile(ctx context.Context, ztdClient *management.Client) error {
|
||||
if wf.action.Status.ActionId == "" {
|
||||
return nil
|
||||
}
|
||||
org, err := wf.refresolver.OrganizationRef(ctx, &wf.action.Spec.OrganizationRef, wf.action.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
{
|
||||
|
||||
_, err := ztdClient.GetAction(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.GetActionRequest{Id: wf.action.Status.ActionId})
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), `doesn't exist`) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = ztdClient.DeleteAction(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.DeleteActionRequest{Id: wf.action.Status.ActionId})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wr *wrappedActionFinalizer) patch(ctx context.Context, action *zitadelv1alpha1.Action,
|
||||
patchFn func(*zitadelv1alpha1.Action)) error {
|
||||
patch := ctrlClient.MergeFrom(action.DeepCopy())
|
||||
patchFn(action)
|
||||
|
||||
if err := wr.Client.Patch(ctx, action, patch); err != nil {
|
||||
return fmt.Errorf("error patching Action finalizer: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
164
src/internal/controller/flow_controller.go
Normal file
164
src/internal/controller/flow_controller.go
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright 2024.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
zitadelv1alpha1 "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1"
|
||||
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/builder"
|
||||
condition "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/condition"
|
||||
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/controller/zitadel"
|
||||
"github.com/zitadel/zitadel-go/v2/pkg/client/management"
|
||||
"github.com/zitadel/zitadel-go/v2/pkg/client/middleware"
|
||||
pb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
)
|
||||
|
||||
// FlowReconciler reconciles a Flow object
|
||||
type FlowReconciler struct {
|
||||
client.Client
|
||||
RefResolver *zitadelv1alpha1.RefResolver
|
||||
ConditionReady *condition.Ready
|
||||
RequeueInterval time.Duration
|
||||
Builder *builder.Builder
|
||||
}
|
||||
|
||||
func NewFlowReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder, conditionReady *condition.Ready,
|
||||
requeueInterval time.Duration) *FlowReconciler {
|
||||
return &FlowReconciler{
|
||||
Client: client,
|
||||
RefResolver: refResolver,
|
||||
ConditionReady: conditionReady,
|
||||
RequeueInterval: requeueInterval,
|
||||
Builder: builder,
|
||||
}
|
||||
}
|
||||
|
||||
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=flows,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=flows/status,verbs=get;update;patch
|
||||
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=flows/finalizers,verbs=update
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
func (r *FlowReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
var Flow zitadelv1alpha1.Flow
|
||||
if err := r.Get(ctx, req.NamespacedName, &Flow); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
wr := newWrappedFlowReconciler(r.Client, r.RefResolver, r.Builder, &Flow)
|
||||
wf := newWrappedFlowFinalizer(r.Client, &Flow, r.RefResolver)
|
||||
tf := zitadel.NewZitadelFinalizer(r.Client, wf)
|
||||
tr := zitadel.NewZitadelReconciler(r.Client, r.ConditionReady, wr, tf, r.RequeueInterval)
|
||||
|
||||
result, err := tr.Reconcile(ctx, &Flow)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("error reconciling in FlowReconciler: %v", err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type wrappedFlowReconciler struct {
|
||||
client.Client
|
||||
refResolver *zitadelv1alpha1.RefResolver
|
||||
Flow *zitadelv1alpha1.Flow
|
||||
Builder *builder.Builder
|
||||
}
|
||||
|
||||
func newWrappedFlowReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder,
|
||||
Flow *zitadelv1alpha1.Flow) zitadel.WrappedReconciler {
|
||||
return &wrappedFlowReconciler{
|
||||
Client: client,
|
||||
refResolver: refResolver,
|
||||
Flow: Flow,
|
||||
Builder: builder,
|
||||
}
|
||||
}
|
||||
|
||||
type flowReoncilePhase struct {
|
||||
Name string
|
||||
Reconcile func(context.Context, *management.Client) error
|
||||
}
|
||||
|
||||
func (wr *wrappedFlowReconciler) Reconcile(ctx context.Context, ztdClient *management.Client) error {
|
||||
phases := []flowReoncilePhase{
|
||||
{
|
||||
Name: "flow",
|
||||
Reconcile: wr.reconcileFlow,
|
||||
},
|
||||
}
|
||||
for _, p := range phases {
|
||||
err := p.Reconcile(ctx, ztdClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wr *wrappedFlowReconciler) reconcileFlow(ctx context.Context, ztdClient *management.Client) error {
|
||||
org, err := wr.refResolver.OrganizationRef(ctx, &wr.Flow.Spec.OrganizationRef, wr.Flow.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx = middleware.SetOrgID(ctx, org.Status.OrgId)
|
||||
|
||||
actionIds := []string{}
|
||||
for _, actionRef := range wr.Flow.Spec.ActionRefs {
|
||||
action, err := wr.refResolver.ActionRef(ctx, &actionRef, wr.Flow.Namespace)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error resolving action reference: %v", err)
|
||||
}
|
||||
if action.Status.ActionId == "" {
|
||||
return fmt.Errorf("Action with name: %s not ready for trigger", action.Name)
|
||||
}
|
||||
actionIds = append(actionIds, action.Status.ActionId)
|
||||
}
|
||||
|
||||
_, err = ztdClient.SetTriggerActions(ctx, &pb.SetTriggerActionsRequest{
|
||||
FlowType: wr.Flow.Spec.FlowType,
|
||||
TriggerType: wr.Flow.Spec.TriggerType,
|
||||
ActionIds: actionIds,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error triggering action flow: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wr *wrappedFlowReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||
patch := client.MergeFrom(wr.Flow.DeepCopy())
|
||||
patcher(&wr.Flow.Status)
|
||||
|
||||
if err := wr.Client.Status().Patch(ctx, wr.Flow, patch); err != nil {
|
||||
return fmt.Errorf("error patching Flow status: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *FlowReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&zitadelv1alpha1.Flow{}).
|
||||
WithOptions(controller.Options{RateLimiter: workqueue.NewItemExponentialFailureRateLimiter(time.Millisecond*500, time.Minute*3)}).
|
||||
Complete(r)
|
||||
}
|
||||
82
src/internal/controller/flow_controller_finalizer.go
Normal file
82
src/internal/controller/flow_controller_finalizer.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
zitadelv1alpha1 "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1"
|
||||
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/controller/zitadel"
|
||||
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/zitadel/zitadel-go/v2/pkg/client/management"
|
||||
"github.com/zitadel/zitadel-go/v2/pkg/client/middleware"
|
||||
pb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
const (
|
||||
flowFinalizerName = "flow.zitadel.topmanage.com/flow"
|
||||
)
|
||||
|
||||
type wrappedFlowFinalizer struct {
|
||||
client.Client
|
||||
flow *zitadelv1alpha1.Flow
|
||||
refresolver *zitadelv1alpha1.RefResolver
|
||||
}
|
||||
|
||||
func newWrappedFlowFinalizer(client client.Client, flow *zitadelv1alpha1.Flow, refresolver *zitadelv1alpha1.RefResolver) zitadel.WrappedFinalizer {
|
||||
return &wrappedFlowFinalizer{
|
||||
Client: client,
|
||||
flow: flow,
|
||||
refresolver: refresolver,
|
||||
}
|
||||
}
|
||||
|
||||
func (wf *wrappedFlowFinalizer) AddFinalizer(ctx context.Context) error {
|
||||
if wf.ContainsFinalizer() {
|
||||
return nil
|
||||
}
|
||||
return wf.patch(ctx, wf.flow, func(flow *zitadelv1alpha1.Flow) {
|
||||
controllerutil.AddFinalizer(flow, flowFinalizerName)
|
||||
})
|
||||
}
|
||||
|
||||
func (wf *wrappedFlowFinalizer) RemoveFinalizer(ctx context.Context) error {
|
||||
if !wf.ContainsFinalizer() {
|
||||
return nil
|
||||
}
|
||||
return wf.patch(ctx, wf.flow, func(flow *zitadelv1alpha1.Flow) {
|
||||
controllerutil.RemoveFinalizer(wf.flow, flowFinalizerName)
|
||||
})
|
||||
}
|
||||
|
||||
func (wr *wrappedFlowFinalizer) ContainsFinalizer() bool {
|
||||
return controllerutil.ContainsFinalizer(wr.flow, flowFinalizerName)
|
||||
}
|
||||
|
||||
func (wf *wrappedFlowFinalizer) Reconcile(ctx context.Context, ztdClient *management.Client) error {
|
||||
org, err := wf.refresolver.OrganizationRef(ctx, &wf.flow.Spec.OrganizationRef, wf.flow.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = ztdClient.ClearFlow(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.ClearFlowRequest{
|
||||
Type: wf.flow.Spec.FlowType,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wr *wrappedFlowFinalizer) patch(ctx context.Context, flow *zitadelv1alpha1.Flow,
|
||||
patchFn func(*zitadelv1alpha1.Flow)) error {
|
||||
patch := ctrlClient.MergeFrom(flow.DeepCopy())
|
||||
patchFn(flow)
|
||||
|
||||
if err := wr.Client.Patch(ctx, flow, patch); err != nil {
|
||||
return fmt.Errorf("error patching Flow finalizer: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user