diff --git a/apis/exoscale/v1/dbaas_types.go b/apis/exoscale/v1/dbaas_types.go new file mode 100644 index 00000000..342a750d --- /dev/null +++ b/apis/exoscale/v1/dbaas_types.go @@ -0,0 +1,140 @@ +package v1 + +import ( + "fmt" + "regexp" + "strconv" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + exoscaleoapi "github.com/exoscale/egoscale/v2/oapi" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +type DBaaSParameters struct { + // TerminationProtection protects against termination and powering off. + TerminationProtection bool `json:"terminationProtection,omitempty"` + // Version is the (major) version identifier for the instance. + Version string `json:"version,omitempty"` + // Size contains the service capacity settings. + Size SizeSpec `json:"size,omitempty"` + + IPFilter IPFilter `json:"ipFilter,omitempty"` +} + +// NodeState describes the state of a service node. +type NodeState struct { + // Name of the service node + Name string `json:"name,omitempty"` + // Role of this node. + Role exoscaleoapi.DbaasNodeStateRole `json:"role,omitempty"` + // State of the service node. + State exoscaleoapi.DbaasNodeStateState `json:"state,omitempty"` +} + +// Notification contains a service message. +type Notification struct { + // Level of the notification. + Level exoscaleoapi.DbaasServiceNotificationLevel `json:"level,omitempty"` + // Message contains the notification. + Message string `json:"message,omitempty"` + // Type of the notification. + Type exoscaleoapi.DbaasServiceNotificationType `json:"type,omitempty"` + // Metadata contains additional data. + Metadata runtime.RawExtension `json:"metadata,omitempty"` +} + +// BackupSpec contains settings to control the backups of an instance. +type BackupSpec struct { + // TimeOfDay for doing daily backups, in UTC. + // Format: "hh:mm:ss". + TimeOfDay TimeOfDay `json:"timeOfDay,omitempty"` +} + +// MaintenanceSpec contains settings to control the maintenance of an instance. +type MaintenanceSpec struct { + // +kubebuilder:validation:Enum=monday;tuesday;wednesday;thursday;friday;saturday;sunday;never + + // DayOfWeek specifies at which weekday the maintenance is held place. + // Allowed values are [monday, tuesday, wednesday, thursday, friday, saturday, sunday, never] + DayOfWeek exoscaleoapi.DbaasServiceMaintenanceDow `json:"dayOfWeek,omitempty"` + + // TimeOfDay for installing updates in UTC. + // Format: "hh:mm:ss". + TimeOfDay TimeOfDay `json:"timeOfDay,omitempty"` +} + +var timeOfDayRegex = regexp.MustCompile("^([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$") + +// +kubebuilder:validation:Pattern="^([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$" + +// TimeOfDay contains a time in the 24hr clock. +// Format: "hh:mm:ss". +type TimeOfDay string + +// String implements fmt.Stringer. +func (t TimeOfDay) String() string { + return string(t) +} + +// Parse returns the hour and minute of the string representation. +// Returns errors if the format is invalid. +func (t TimeOfDay) Parse() (hour, minute, second int64, err error) { + if t.String() == "" { + return -1, -1, -1, fmt.Errorf("time cannot be empty") + } + arr := timeOfDayRegex.FindStringSubmatch(t.String()) + if len(arr) < 3 { + return -1, -1, -1, fmt.Errorf("invalid format for time of day (hh:mm:ss): %s", t) + } + parts := []int64{0, 0, 0} + for i, part := range arr[1:] { + parsed, err := strconv.ParseInt(part, 10, 64) + if err != nil && part != "" { + return -1, -1, -1, fmt.Errorf("invalid time given for time of day: %w", err) + } + parts[i] = parsed + } + return parts[0], parts[1], parts[2], nil +} + +// Rebuilding returns a Ready condition where the service is rebuilding. +func Rebuilding() xpv1.Condition { + return xpv1.Condition{ + Type: xpv1.TypeReady, + Status: corev1.ConditionFalse, + Reason: "Rebuilding", + Message: "The service is being provisioned", + LastTransitionTime: metav1.Now(), + } +} + +// PoweredOff returns a Ready condition where the service is powered off. +func PoweredOff() xpv1.Condition { + return xpv1.Condition{ + Type: xpv1.TypeReady, + Status: corev1.ConditionFalse, + Reason: "PoweredOff", + Message: "The service is powered off", + LastTransitionTime: metav1.Now(), + } +} + +// Rebalancing returns a Ready condition where the service is rebalancing. +func Rebalancing() xpv1.Condition { + return xpv1.Condition{ + Type: xpv1.TypeReady, + Status: corev1.ConditionFalse, + Reason: "Rebalancing", + Message: "The service is being rebalanced", + LastTransitionTime: metav1.Now(), + } +} + +// Running returns a Ready condition where the service is running. +func Running() xpv1.Condition { + c := xpv1.Available() + c.Message = "The service is running" + return c +} diff --git a/apis/exoscale/v1/dbaas_types_test.go b/apis/exoscale/v1/dbaas_types_test.go new file mode 100644 index 00000000..543c672a --- /dev/null +++ b/apis/exoscale/v1/dbaas_types_test.go @@ -0,0 +1,51 @@ +package v1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTimeOfDay_Parse(t *testing.T) { + tests := []struct { + givenInput string + expectedMinute int64 + expectedHour int64 + expectedSecond int64 + expectedError string + }{ + {givenInput: "00:00:00", expectedHour: 0, expectedMinute: 0, expectedSecond: 0}, + {givenInput: "23:59:00", expectedHour: 23, expectedMinute: 59, expectedSecond: 0}, + {givenInput: "01:01:01", expectedHour: 1, expectedMinute: 1, expectedSecond: 1}, + {givenInput: "1:59:59", expectedHour: 1, expectedMinute: 59, expectedSecond: 59}, + {givenInput: "19:59:01", expectedHour: 19, expectedMinute: 59, expectedSecond: 1}, + {givenInput: "4:59:01", expectedHour: 4, expectedMinute: 59, expectedSecond: 1}, + {givenInput: "04:59:01", expectedHour: 4, expectedMinute: 59, expectedSecond: 1}, + {givenInput: "9:01:01", expectedHour: 9, expectedMinute: 1, expectedSecond: 1}, + {givenInput: "", expectedError: "time cannot be empty"}, + {givenInput: "invalid", expectedError: "invalid format for time of day (hh:mm:ss): invalid"}, + {givenInput: "🕗", expectedError: "invalid format for time of day (hh:mm:ss): 🕗"}, + {givenInput: "-1:1", expectedError: "invalid format for time of day (hh:mm:ss): -1:1"}, + {givenInput: "24:01:30", expectedError: "invalid format for time of day (hh:mm:ss): 24:01:30"}, + {givenInput: "23:60:00", expectedError: "invalid format for time of day (hh:mm:ss): 23:60:00"}, + {givenInput: "foo:01:02", expectedError: "invalid format for time of day (hh:mm:ss): foo:01:02"}, + {givenInput: "01:bar:02", expectedError: "invalid format for time of day (hh:mm:ss): 01:bar:02"}, + } + for _, tc := range tests { + t.Run(tc.givenInput, func(t *testing.T) { + hour, minute, second, err := TimeOfDay(tc.givenInput).Parse() + if tc.expectedError != "" { + assert.EqualError(t, err, tc.expectedError) + assert.Equal(t, int64(-1), hour) + assert.Equal(t, int64(-1), minute) + assert.Equal(t, int64(-1), second) + + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expectedHour, hour) + assert.Equal(t, tc.expectedMinute, minute) + assert.Equal(t, tc.expectedSecond, second) + } + }) + } +} diff --git a/apis/exoscale/v1/postgresql_types.go b/apis/exoscale/v1/postgresql_types.go new file mode 100644 index 00000000..d6b2158b --- /dev/null +++ b/apis/exoscale/v1/postgresql_types.go @@ -0,0 +1,119 @@ +package v1 + +import ( + "reflect" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// PostgreSQLParameters are the configurable fields of a PostgreSQL. +type PostgreSQLParameters struct { + Maintenance MaintenanceSpec `json:"maintenance,omitempty"` + Backup BackupSpec `json:"backup,omitempty"` + + // +kubebuilder:validation:Enum=ch-gva-2;ch-dk-2;de-fra-1;de-muc-1;at-vie-1;bg-sof-1 + // +kubebuilder:validation:Required + + // Zone is the datacenter identifier in which the instance runs in. + Zone string `json:"zone"` + + DBaaSParameters `json:",inline"` + + // PGSettings contains additional PostgreSQL settings. + PGSettings runtime.RawExtension `json:"pgSettings,omitempty"` +} + +// SizeSpec contains settings to control the sizing of a service. +type SizeSpec struct { + Plan string `json:"plan,omitempty"` +} + +// IPFilter is a list of allowed IPv4 CIDR ranges that can access the service. +// If no IP Filter is set, you may not be able to reach the service. +// A value of `0.0.0.0/0` will open the service to all addresses on the public internet. +type IPFilter []string + +// PostgreSQLSpec defines the desired state of a PostgreSQL. +type PostgreSQLSpec struct { + xpv1.ResourceSpec `json:",inline"` + ForProvider PostgreSQLParameters `json:"forProvider"` +} + +// PostgreSQLObservation are the observable fields of a PostgreSQL. +type PostgreSQLObservation struct { + DBaaSParameters `json:",inline"` + Maintenance MaintenanceSpec `json:"maintenance,omitempty"` + Backup BackupSpec `json:"backup,omitempty"` + NoteStates []NodeState `json:"noteStates,omitempty"` + PGSettings runtime.RawExtension `json:"pgSettings,omitempty"` +} + +// PostgreSQLStatus represents the observed state of a PostgreSQL. +type PostgreSQLStatus struct { + xpv1.ResourceStatus `json:",inline"` + AtProvider PostgreSQLObservation `json:"atProvider,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].reason" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status" +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status" +// +kubebuilder:printcolumn:name="External Name",type="string",JSONPath=".metadata.annotations.crossplane\\.io/external-name" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster,categories={crossplane,exoscale} +// +kubebuilder:webhook:verbs=create;update,path=/validate-exoscale-crossplane-io-v1-postgresql,mutating=false,failurePolicy=fail,groups=exoscale.crossplane.io,resources=postgresqls,versions=v1,name=postgresqls.exoscale.crossplane.io,sideEffects=None,admissionReviewVersions=v1 + +// PostgreSQL is the API for creating PostgreSQL. +type PostgreSQL struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PostgreSQLSpec `json:"spec"` + Status PostgreSQLStatus `json:"status,omitempty"` +} + +// GetProviderConfigName returns the name of the ProviderConfig. +// Returns empty string if reference not given. +func (in *PostgreSQL) GetProviderConfigName() string { + if ref := in.GetProviderConfigReference(); ref != nil { + return ref.Name + } + return "" +} + +// GetInstanceName returns the external name of the instance in the following precedence: +// +// .metadata.annotations."crossplane.io/external-name" +// .metadata.name +func (in *PostgreSQL) GetInstanceName() string { + if name := meta.GetExternalName(in); name != "" { + return name + } + return in.Name +} + +// +kubebuilder:object:root=true + +// PostgreSQLList contains a list of PostgreSQL +type PostgreSQLList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []PostgreSQL `json:"items"` +} + +// PostgreSQL type metadata. +var ( + PostgreSQLKind = reflect.TypeOf(PostgreSQL{}).Name() + PostgreSQLGroupKind = schema.GroupKind{Group: Group, Kind: PostgreSQLKind}.String() + PostgreSQLKindAPIVersion = PostgreSQLKind + "." + SchemeGroupVersion.String() + PostgreSQLGroupVersionKind = SchemeGroupVersion.WithKind(PostgreSQLKind) +) + +func init() { + SchemeBuilder.Register(&PostgreSQL{}, &PostgreSQLList{}) +} diff --git a/apis/exoscale/v1/zz_generated.deepcopy.go b/apis/exoscale/v1/zz_generated.deepcopy.go index bc931e65..2905d7cf 100644 --- a/apis/exoscale/v1/zz_generated.deepcopy.go +++ b/apis/exoscale/v1/zz_generated.deepcopy.go @@ -6,9 +6,24 @@ package v1 import ( - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupSpec. +func (in *BackupSpec) DeepCopy() *BackupSpec { + if in == nil { + return nil + } + out := new(BackupSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Bucket) DeepCopyInto(out *Bucket) { *out = *in @@ -132,6 +147,27 @@ func (in *BucketStatus) DeepCopy() *BucketStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DBaaSParameters) DeepCopyInto(out *DBaaSParameters) { + *out = *in + out.Size = in.Size + if in.IPFilter != nil { + in, out := &in.IPFilter, &out.IPFilter + *out = make(IPFilter, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBaaSParameters. +func (in *DBaaSParameters) DeepCopy() *DBaaSParameters { + if in == nil { + return nil + } + out := new(DBaaSParameters) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IAMKey) DeepCopyInto(out *IAMKey) { *out = *in @@ -257,6 +293,207 @@ func (in *IAMKeyStatus) DeepCopy() *IAMKeyStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in IPFilter) DeepCopyInto(out *IPFilter) { + { + in := &in + *out = make(IPFilter, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPFilter. +func (in IPFilter) DeepCopy() IPFilter { + if in == nil { + return nil + } + out := new(IPFilter) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaintenanceSpec) DeepCopyInto(out *MaintenanceSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaintenanceSpec. +func (in *MaintenanceSpec) DeepCopy() *MaintenanceSpec { + if in == nil { + return nil + } + out := new(MaintenanceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeState) DeepCopyInto(out *NodeState) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeState. +func (in *NodeState) DeepCopy() *NodeState { + if in == nil { + return nil + } + out := new(NodeState) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Notification) DeepCopyInto(out *Notification) { + *out = *in + in.Metadata.DeepCopyInto(&out.Metadata) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Notification. +func (in *Notification) DeepCopy() *Notification { + if in == nil { + return nil + } + out := new(Notification) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgreSQL) DeepCopyInto(out *PostgreSQL) { + *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 PostgreSQL. +func (in *PostgreSQL) DeepCopy() *PostgreSQL { + if in == nil { + return nil + } + out := new(PostgreSQL) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PostgreSQL) 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 *PostgreSQLList) DeepCopyInto(out *PostgreSQLList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PostgreSQL, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLList. +func (in *PostgreSQLList) DeepCopy() *PostgreSQLList { + if in == nil { + return nil + } + out := new(PostgreSQLList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PostgreSQLList) 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 *PostgreSQLObservation) DeepCopyInto(out *PostgreSQLObservation) { + *out = *in + in.DBaaSParameters.DeepCopyInto(&out.DBaaSParameters) + out.Maintenance = in.Maintenance + out.Backup = in.Backup + if in.NoteStates != nil { + in, out := &in.NoteStates, &out.NoteStates + *out = make([]NodeState, len(*in)) + copy(*out, *in) + } + in.PGSettings.DeepCopyInto(&out.PGSettings) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLObservation. +func (in *PostgreSQLObservation) DeepCopy() *PostgreSQLObservation { + if in == nil { + return nil + } + out := new(PostgreSQLObservation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgreSQLParameters) DeepCopyInto(out *PostgreSQLParameters) { + *out = *in + out.Maintenance = in.Maintenance + out.Backup = in.Backup + in.DBaaSParameters.DeepCopyInto(&out.DBaaSParameters) + in.PGSettings.DeepCopyInto(&out.PGSettings) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLParameters. +func (in *PostgreSQLParameters) DeepCopy() *PostgreSQLParameters { + if in == nil { + return nil + } + out := new(PostgreSQLParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgreSQLSpec) DeepCopyInto(out *PostgreSQLSpec) { + *out = *in + in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) + in.ForProvider.DeepCopyInto(&out.ForProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLSpec. +func (in *PostgreSQLSpec) DeepCopy() *PostgreSQLSpec { + if in == nil { + return nil + } + out := new(PostgreSQLSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgreSQLStatus) DeepCopyInto(out *PostgreSQLStatus) { + *out = *in + in.ResourceStatus.DeepCopyInto(&out.ResourceStatus) + in.AtProvider.DeepCopyInto(&out.AtProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLStatus. +func (in *PostgreSQLStatus) DeepCopy() *PostgreSQLStatus { + if in == nil { + return nil + } + out := new(PostgreSQLStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SOSSpec) DeepCopyInto(out *SOSSpec) { *out = *in @@ -292,3 +529,18 @@ func (in *ServicesSpec) DeepCopy() *ServicesSpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SizeSpec) DeepCopyInto(out *SizeSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SizeSpec. +func (in *SizeSpec) DeepCopy() *SizeSpec { + if in == nil { + return nil + } + out := new(SizeSpec) + in.DeepCopyInto(out) + return out +} diff --git a/apis/exoscale/v1/zz_generated.managed.go b/apis/exoscale/v1/zz_generated.managed.go index d37c5864..58a87ff4 100644 --- a/apis/exoscale/v1/zz_generated.managed.go +++ b/apis/exoscale/v1/zz_generated.managed.go @@ -135,3 +135,69 @@ func (mg *IAMKey) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetails func (mg *IAMKey) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { mg.Spec.WriteConnectionSecretToReference = r } + +// GetCondition of this PostgreSQL. +func (mg *PostgreSQL) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return mg.Status.GetCondition(ct) +} + +// GetDeletionPolicy of this PostgreSQL. +func (mg *PostgreSQL) GetDeletionPolicy() xpv1.DeletionPolicy { + return mg.Spec.DeletionPolicy +} + +// GetProviderConfigReference of this PostgreSQL. +func (mg *PostgreSQL) GetProviderConfigReference() *xpv1.Reference { + return mg.Spec.ProviderConfigReference +} + +/* +GetProviderReference of this PostgreSQL. +Deprecated: Use GetProviderConfigReference. +*/ +func (mg *PostgreSQL) GetProviderReference() *xpv1.Reference { + return mg.Spec.ProviderReference +} + +// GetPublishConnectionDetailsTo of this PostgreSQL. +func (mg *PostgreSQL) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo { + return mg.Spec.PublishConnectionDetailsTo +} + +// GetWriteConnectionSecretToReference of this PostgreSQL. +func (mg *PostgreSQL) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + return mg.Spec.WriteConnectionSecretToReference +} + +// SetConditions of this PostgreSQL. +func (mg *PostgreSQL) SetConditions(c ...xpv1.Condition) { + mg.Status.SetConditions(c...) +} + +// SetDeletionPolicy of this PostgreSQL. +func (mg *PostgreSQL) SetDeletionPolicy(r xpv1.DeletionPolicy) { + mg.Spec.DeletionPolicy = r +} + +// SetProviderConfigReference of this PostgreSQL. +func (mg *PostgreSQL) SetProviderConfigReference(r *xpv1.Reference) { + mg.Spec.ProviderConfigReference = r +} + +/* +SetProviderReference of this PostgreSQL. +Deprecated: Use SetProviderConfigReference. +*/ +func (mg *PostgreSQL) SetProviderReference(r *xpv1.Reference) { + mg.Spec.ProviderReference = r +} + +// SetPublishConnectionDetailsTo of this PostgreSQL. +func (mg *PostgreSQL) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) { + mg.Spec.PublishConnectionDetailsTo = r +} + +// SetWriteConnectionSecretToReference of this PostgreSQL. +func (mg *PostgreSQL) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { + mg.Spec.WriteConnectionSecretToReference = r +} diff --git a/apis/exoscale/v1/zz_generated.managedlist.go b/apis/exoscale/v1/zz_generated.managedlist.go index c2fe0ab1..17cf43b5 100644 --- a/apis/exoscale/v1/zz_generated.managedlist.go +++ b/apis/exoscale/v1/zz_generated.managedlist.go @@ -21,3 +21,12 @@ func (l *IAMKeyList) GetItems() []resource.Managed { } return items } + +// GetItems of this PostgreSQLList. +func (l *PostgreSQLList) GetItems() []resource.Managed { + items := make([]resource.Managed, len(l.Items)) + for i := range l.Items { + items[i] = &l.Items[i] + } + return items +} diff --git a/generate_sample.go b/generate_sample.go index 77481895..1b266e61 100644 --- a/generate_sample.go +++ b/generate_sample.go @@ -10,17 +10,19 @@ package main import ( "fmt" - "github.com/vshn/provider-exoscale/apis" - exoscalev1 "github.com/vshn/provider-exoscale/apis/exoscale/v1" - providerv1 "github.com/vshn/provider-exoscale/apis/provider/v1" "io" - admissionv1 "k8s.io/api/admission/v1" - authv1 "k8s.io/api/authentication/v1" "log" "os" "path/filepath" "strings" + exoscaleoapi "github.com/exoscale/egoscale/v2/oapi" + "github.com/vshn/provider-exoscale/apis" + exoscalev1 "github.com/vshn/provider-exoscale/apis/exoscale/v1" + providerv1 "github.com/vshn/provider-exoscale/apis/provider/v1" + admissionv1 "k8s.io/api/admission/v1" + authv1 "k8s.io/api/authentication/v1" + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -36,6 +38,44 @@ func main() { generateExoscaleIAMKeySample() generateProviderConfigSample() generateIAMKeyAdmissionRequest() + generatePostgresqlSample() +} + +func generatePostgresqlSample() { + spec := newPostgresqlSample() + serialize(spec, true) +} + +func newPostgresqlSample() *exoscalev1.PostgreSQL { + return &exoscalev1.PostgreSQL{ + TypeMeta: metav1.TypeMeta{ + APIVersion: exoscalev1.PostgreSQLGroupVersionKind.GroupVersion().String(), + Kind: exoscalev1.PostgreSQLKind, + }, + ObjectMeta: metav1.ObjectMeta{Name: "postgresql-local-dev"}, + Spec: exoscalev1.PostgreSQLSpec{ + ResourceSpec: xpv1.ResourceSpec{ + ProviderConfigReference: &xpv1.Reference{Name: "provider-config"}, + }, + ForProvider: exoscalev1.PostgreSQLParameters{ + Maintenance: exoscalev1.MaintenanceSpec{ + TimeOfDay: "12:00:00", + DayOfWeek: exoscaleoapi.DbaasServiceMaintenanceDowMonday, + }, + Backup: exoscalev1.BackupSpec{ + TimeOfDay: "13:00:00", + }, + Zone: "ch-dk-2", + DBaaSParameters: exoscalev1.DBaaSParameters{ + Version: "14", + Size: exoscalev1.SizeSpec{ + Plan: "hobbyist-2", + }, + IPFilter: exoscalev1.IPFilter{"0.0.0.0/0"}, + }, + }, + }, + } } func generateBucketSample() { @@ -93,7 +133,7 @@ func newIAMKeySample() *exoscalev1.IAMKey { KeyName: "iam-key-local-dev", Zone: "CH-DK-2", Services: exoscalev1.ServicesSpec{ - exoscalev1.SOSSpec{ + SOS: exoscalev1.SOSSpec{ Buckets: []string{"bucket-local-dev"}, }, }, diff --git a/main_test.go b/main_test.go deleted file mode 100644 index 2a3c97cb..00000000 --- a/main_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "context" - "flag" - "sync/atomic" - "testing" - - "github.com/go-logr/zapr" - "github.com/urfave/cli/v2" - "go.uber.org/zap/zaptest" -) - -func newAppContext(t *testing.T) *cli.Context { - logger := zapr.NewLogger(zaptest.NewLogger(t)) - instance := &atomic.Value{} - instance.Store(logger) - return cli.NewContext(&cli.App{}, flag.NewFlagSet("", flag.ContinueOnError), &cli.Context{ - Context: context.WithValue(context.Background(), loggerContextKey{}, instance), - }) -} diff --git a/package/crds/exoscale.crossplane.io_postgresqls.yaml b/package/crds/exoscale.crossplane.io_postgresqls.yaml new file mode 100644 index 00000000..eac073cf --- /dev/null +++ b/package/crds/exoscale.crossplane.io_postgresqls.yaml @@ -0,0 +1,437 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: postgresqls.exoscale.crossplane.io +spec: + group: exoscale.crossplane.io + names: + categories: + - crossplane + - exoscale + kind: PostgreSQL + listKind: PostgreSQLList + plural: postgresqls + singular: postgresql + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Ready')].reason + name: State + type: string + - jsonPath: .status.conditions[?(@.type=='Ready')].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=='Synced')].status + name: Synced + type: string + - jsonPath: .metadata.annotations.crossplane\.io/external-name + name: External Name + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: PostgreSQL is the API for creating PostgreSQL. + 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: PostgreSQLSpec defines the desired state of a PostgreSQL. + properties: + deletionPolicy: + default: Delete + description: DeletionPolicy specifies what will happen to the underlying + external when this managed resource is deleted - either "Delete" + or "Orphan" the external resource. + enum: + - Orphan + - Delete + type: string + forProvider: + description: PostgreSQLParameters are the configurable fields of a + PostgreSQL. + properties: + backup: + description: BackupSpec contains settings to control the backups + of an instance. + properties: + timeOfDay: + description: 'TimeOfDay for doing daily backups, in UTC. Format: + "hh:mm:ss".' + pattern: ^([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$ + type: string + type: object + ipFilter: + description: IPFilter is a list of allowed IPv4 CIDR ranges that + can access the service. If no IP Filter is set, you may not + be able to reach the service. A value of `0.0.0.0/0` will open + the service to all addresses on the public internet. + items: + type: string + type: array + maintenance: + description: MaintenanceSpec contains settings to control the + maintenance of an instance. + properties: + dayOfWeek: + description: DayOfWeek specifies at which weekday the maintenance + is held place. Allowed values are [monday, tuesday, wednesday, + thursday, friday, saturday, sunday, never] + enum: + - monday + - tuesday + - wednesday + - thursday + - friday + - saturday + - sunday + - never + type: string + timeOfDay: + description: 'TimeOfDay for installing updates in UTC. Format: + "hh:mm:ss".' + pattern: ^([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$ + type: string + type: object + pgSettings: + description: PGSettings contains additional PostgreSQL settings. + type: object + x-kubernetes-preserve-unknown-fields: true + size: + description: Size contains the service capacity settings. + properties: + plan: + type: string + type: object + terminationProtection: + description: TerminationProtection protects against termination + and powering off. + type: boolean + version: + description: Version is the (major) version identifier for the + instance. + type: string + zone: + description: Zone is the datacenter identifier in which the instance + runs in. + enum: + - ch-gva-2 + - ch-dk-2 + - de-fra-1 + - de-muc-1 + - at-vie-1 + - bg-sof-1 + type: string + required: + - zone + type: object + providerConfigRef: + default: + name: default + description: ProviderConfigReference specifies how the provider that + will be used to create, observe, update, and delete this managed + resource should be configured. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of this + reference is required. The default is 'Required', which + means the reconcile will fail if the reference cannot be + resolved. 'Optional' means this reference will be a no-op + if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should + be resolved. The default is 'IfNotPresent', which will attempt + to resolve the reference only when the corresponding field + is not present. Use 'Always' to resolve the reference on + every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + providerRef: + description: 'ProviderReference specifies the provider that will be + used to create, observe, update, and delete this managed resource. + Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`' + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of this + reference is required. The default is 'Required', which + means the reconcile will fail if the reference cannot be + resolved. 'Optional' means this reference will be a no-op + if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should + be resolved. The default is 'IfNotPresent', which will attempt + to resolve the reference only when the corresponding field + is not present. Use 'Always' to resolve the reference on + every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + publishConnectionDetailsTo: + description: PublishConnectionDetailsTo specifies the connection secret + config which contains a name, metadata and a reference to secret + store config to which any connection details for this managed resource + should be written. Connection details frequently include the endpoint, + username, and password required to connect to the managed resource. + properties: + configRef: + default: + name: default + description: SecretStoreConfigRef specifies which secret store + config should be used for this ConnectionSecret. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of + this reference is required. The default is 'Required', + which means the reconcile will fail if the reference + cannot be resolved. 'Optional' means this reference + will be a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should + be resolved. The default is 'IfNotPresent', which will + attempt to resolve the reference only when the corresponding + field is not present. Use 'Always' to resolve the reference + on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + metadata: + description: Metadata is the metadata for connection secret. + properties: + annotations: + additionalProperties: + type: string + description: Annotations are the annotations to be added to + connection secret. - For Kubernetes secrets, this will be + used as "metadata.annotations". - It is up to Secret Store + implementation for others store types. + type: object + labels: + additionalProperties: + type: string + description: Labels are the labels/tags to be added to connection + secret. - For Kubernetes secrets, this will be used as "metadata.labels". + - It is up to Secret Store implementation for others store + types. + type: object + type: + description: Type is the SecretType for the connection secret. + - Only valid for Kubernetes Secret Stores. + type: string + type: object + name: + description: Name is the name of the connection secret. + type: string + required: + - name + type: object + writeConnectionSecretToRef: + description: WriteConnectionSecretToReference specifies the namespace + and name of a Secret to which any connection details for this managed + resource should be written. Connection details frequently include + the endpoint, username, and password required to connect to the + managed resource. This field is planned to be replaced in a future + release in favor of PublishConnectionDetailsTo. Currently, both + could be set independently and connection details would be published + to both without affecting each other. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + required: + - forProvider + type: object + status: + description: PostgreSQLStatus represents the observed state of a PostgreSQL. + properties: + atProvider: + description: PostgreSQLObservation are the observable fields of a + PostgreSQL. + properties: + backup: + description: BackupSpec contains settings to control the backups + of an instance. + properties: + timeOfDay: + description: 'TimeOfDay for doing daily backups, in UTC. Format: + "hh:mm:ss".' + pattern: ^([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$ + type: string + type: object + ipFilter: + description: IPFilter is a list of allowed IPv4 CIDR ranges that + can access the service. If no IP Filter is set, you may not + be able to reach the service. A value of `0.0.0.0/0` will open + the service to all addresses on the public internet. + items: + type: string + type: array + maintenance: + description: MaintenanceSpec contains settings to control the + maintenance of an instance. + properties: + dayOfWeek: + description: DayOfWeek specifies at which weekday the maintenance + is held place. Allowed values are [monday, tuesday, wednesday, + thursday, friday, saturday, sunday, never] + enum: + - monday + - tuesday + - wednesday + - thursday + - friday + - saturday + - sunday + - never + type: string + timeOfDay: + description: 'TimeOfDay for installing updates in UTC. Format: + "hh:mm:ss".' + pattern: ^([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$ + type: string + type: object + noteStates: + items: + description: NodeState describes the state of a service node. + properties: + name: + description: Name of the service node + type: string + role: + description: Role of this node. + type: string + state: + description: State of the service node. + type: string + type: object + type: array + pgSettings: + type: object + x-kubernetes-preserve-unknown-fields: true + size: + description: Size contains the service capacity settings. + properties: + plan: + type: string + type: object + terminationProtection: + description: TerminationProtection protects against termination + and powering off. + type: boolean + version: + description: Version is the (major) version identifier for the + instance. + type: string + type: object + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time this condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A Message containing details about this condition's + last transition from one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: Type of this condition. At most one of each condition + type may apply to a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/package/webhook/manifests.yaml b/package/webhook/manifests.yaml index 24e4e722..d410f126 100644 --- a/package/webhook/manifests.yaml +++ b/package/webhook/manifests.yaml @@ -45,3 +45,23 @@ webhooks: resources: - iamkeys sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-exoscale-crossplane-io-v1-postgresql + failurePolicy: Fail + name: postgresqls.exoscale.crossplane.io + rules: + - apiGroups: + - exoscale.crossplane.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - postgresqls + sideEffects: None diff --git a/samples/exoscale.crossplane.io_postgresql.yaml b/samples/exoscale.crossplane.io_postgresql.yaml new file mode 100644 index 00000000..720e3705 --- /dev/null +++ b/samples/exoscale.crossplane.io_postgresql.yaml @@ -0,0 +1,27 @@ +apiVersion: exoscale.crossplane.io/v1 +kind: PostgreSQL +metadata: + creationTimestamp: null + name: postgresql-local-dev +spec: + forProvider: + backup: + timeOfDay: "13:00:00" + ipFilter: + - 0.0.0.0/0 + maintenance: + dayOfWeek: monday + timeOfDay: "12:00:00" + pgSettings: null + size: + plan: hobbyist-2 + version: "14" + zone: ch-dk-2 + providerConfigRef: + name: provider-config +status: + atProvider: + backup: {} + maintenance: {} + pgSettings: null + size: {}