Skip to content

Commit 3aea3d7

Browse files
committed
feat: add resource
1 parent 7e0762d commit 3aea3d7

19 files changed

Lines changed: 2650 additions & 46 deletions
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
package v1alpha1
2+
3+
import (
4+
// #nosec G501: Blocklisted import crypto/md5: weak cryptographic primitive
5+
"crypto/md5"
6+
"fmt"
7+
"reflect"
8+
"slices"
9+
"strings"
10+
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/runtime/schema"
13+
14+
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
15+
)
16+
17+
// MtaParameters are the configurable fields of a Mta.
18+
type MtaParameters struct {
19+
// (Bool) Use blue-green deployment
20+
// +kubebuilder:validation:Optional
21+
BlueGreenDeploy *bool `json:"blueGreenDeploy,omitempty"`
22+
23+
// (String) The URL of the deploy service, if a custom one has been used(should be present in the same landscape). By default 'deploy-service.<system-domain>'
24+
// The URL of the deploy service, if a custom one has been used(should be present in the same landscape). By default 'deploy-service.<system-domain>'
25+
// +kubebuilder:validation:Optional
26+
DeployURL *string `json:"deployUrl,omitempty"`
27+
28+
// (String) The namespace of the MTA. Should be of valid host format
29+
// The namespace of the MTA. Should be of valid host format
30+
// +kubebuilder:validation:Optional
31+
Namespace *string `json:"namespace,omitempty"`
32+
33+
// Reference to a Space in space to populate space.
34+
SpaceReference `json:",inline"`
35+
36+
// +kubebuilder:validation:Required
37+
File *File `json:"file"`
38+
39+
Extension *string `json:"extension,omitempty"`
40+
41+
// (Bool) Specifies whether the deployment should be aborted if an error occurs
42+
// +kubebuilder:validation:Optional
43+
AbortOnError *bool `json:"abortOnError,omitempty"`
44+
45+
// Specifies the versioning rule to be applied for the resource
46+
// +kubebuilder-validation:Enum=HIGHER;SAME_HIGHER;ALL
47+
VersionRule *string `json:"versionRule,omitempty"`
48+
49+
// Deploy only the modules of the MTA with the specified names. If not specified, all modules are deployed.
50+
// +kubebuilder-validation:Optional
51+
Modules *[]string `json:"modules,omitempty"`
52+
53+
// (Bool) Specifies whether to re-create changed services and delete discontinued services.
54+
// +kubebuilder:validation:Optional
55+
DeleteServices *bool `json:"deleteServices,omitempty"`
56+
}
57+
58+
type FileObservation struct {
59+
ID *string `json:"id,omitempty"`
60+
61+
AppInstance *string `json:"appInstance,omitempty"`
62+
63+
URL *string `json:"url,omitempty"`
64+
65+
LastOperation *Operation `json:"operation,omitempty"`
66+
}
67+
68+
// MtaObservation are the observable fields of a Mta.
69+
type MtaObservation struct {
70+
MtaId *string `json:"mtaId,omitempty"`
71+
72+
MtaExtensionId *string `json:"mtaExtensionId,omitempty"`
73+
74+
MtaExtensionHash *string `json:"mtaExtensionHash,omitempty"`
75+
76+
MtaModules *[]string `json:"mtaModulesForDeployment,omitempty"`
77+
78+
Files *[]FileObservation `json:"files,omitempty"`
79+
80+
LastOperation *Operation `json:"lastOperation,omitempty"`
81+
}
82+
83+
type File struct {
84+
// Reference to a secret containing a user and an optional password, which is added to the URL of the MTA.
85+
// +kubebuilder:validation:Optional
86+
CredentialsSecretRef *xpv1.SecretReference `json:"credentialsSecretRef,omitempty"`
87+
88+
// (String) The remote URL where the MTA archive is present
89+
// The remote URL where the MTA archive is present
90+
// +kubebuilder:validation:Required
91+
URL *string `json:"url,omitempty"`
92+
}
93+
94+
// A MtaSpec defines the desired state of a Mta.
95+
type MtaSpec struct {
96+
xpv1.ResourceSpec `json:",inline"`
97+
ForProvider MtaParameters `json:"forProvider"`
98+
}
99+
100+
// A MtaStatus represents the observed state of a Mta.
101+
type MtaStatus struct {
102+
xpv1.ResourceStatus `json:",inline"`
103+
AtProvider MtaObservation `json:"atProvider,omitempty"`
104+
}
105+
106+
// +kubebuilder:object:root=true
107+
108+
// A Mta is an example API type.
109+
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status"
110+
// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status"
111+
// +kubebuilder:printcolumn:name="External-Name",type="string",JSONPath=".metadata.annotations.crossplane\\.io/external-name"
112+
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
113+
// +kubebuilder:subresource:status
114+
// +kubebuilder:resource:scope=Cluster,categories={crossplane,managed,crossplaneprovidermta}
115+
type Mta struct {
116+
metav1.TypeMeta `json:",inline"`
117+
metav1.ObjectMeta `json:"metadata,omitempty"`
118+
119+
Spec MtaSpec `json:"spec"`
120+
Status MtaStatus `json:"status,omitempty"`
121+
}
122+
123+
// +kubebuilder:object:root=true
124+
125+
// MtaList contains a list of Mta
126+
type MtaList struct {
127+
metav1.TypeMeta `json:",inline"`
128+
metav1.ListMeta `json:"metadata,omitempty"`
129+
Items []Mta `json:"items"`
130+
}
131+
132+
// Mta type metadata.
133+
var (
134+
MtaKind = reflect.TypeOf(Mta{}).Name()
135+
MtaGroupKind = schema.GroupKind{Group: CRDGroup, Kind: MtaKind}.String()
136+
MtaKindAPIVersion = MtaKind + "." + CRDGroupVersion.String()
137+
MtaGroupVersionKind = CRDGroupVersion.WithKind(MtaKind)
138+
)
139+
140+
func init() {
141+
SchemeBuilder.Register(&Mta{}, &MtaList{})
142+
}
143+
144+
func (m *Mta) AllFiles() []File {
145+
files := []File{}
146+
147+
if m.Spec.ForProvider.File != nil {
148+
files = append(files, *m.Spec.ForProvider.File)
149+
}
150+
151+
return files
152+
}
153+
154+
func (m *Mta) HasExtension() bool {
155+
return m.Spec.ForProvider.Extension != nil
156+
}
157+
158+
func (m *Mta) IsExtensionAlreadyUploaded() bool {
159+
return m.Status.AtProvider.MtaExtensionId != nil
160+
}
161+
162+
func (m *Mta) AreModulesApplied() bool {
163+
return m.Status.AtProvider.MtaModules != nil
164+
}
165+
166+
func (m *Mta) HasChangedExtension() bool {
167+
var desired string
168+
if m.Spec.ForProvider.Extension != nil {
169+
// #nosec G401: Use of weak cryptographic primitive
170+
desired = fmt.Sprintf("%x", md5.Sum([]byte(*m.Spec.ForProvider.Extension)))
171+
} else {
172+
desired = ""
173+
}
174+
175+
var actual string
176+
if m.Status.AtProvider.MtaExtensionHash != nil {
177+
actual = *m.Status.AtProvider.MtaExtensionHash
178+
} else {
179+
actual = ""
180+
}
181+
182+
if !strings.EqualFold(desired, actual) {
183+
return true
184+
}
185+
return false
186+
}
187+
188+
func (m *Mta) HaveDeploymentModulesChanged() bool {
189+
return !reflect.DeepEqual(m.Spec.ForProvider.Modules, m.Status.AtProvider.MtaModules)
190+
}
191+
192+
func (m *Mta) FindFileObservation(file *File) *FileObservation {
193+
if m.Status.AtProvider.Files == nil {
194+
return nil
195+
}
196+
197+
for _, f := range *m.Status.AtProvider.Files {
198+
if f.URL != nil && *f.URL == *file.URL {
199+
return &f
200+
}
201+
}
202+
203+
return nil
204+
}
205+
206+
func (m *Mta) HasChangedUrls() bool {
207+
files := m.AllFiles()
208+
209+
for _, file := range files {
210+
fileCopy := file
211+
if m.FindFileObservation(&fileCopy) == nil {
212+
return true
213+
}
214+
}
215+
216+
return false
217+
}
218+
219+
func (m *Mta) HasRunningOperation() bool {
220+
return slices.ContainsFunc(m.allOperations(), func(operation Operation) bool {
221+
return operation.IsRunning()
222+
})
223+
}
224+
225+
func (m *Mta) HasErrorOperation() bool {
226+
return slices.ContainsFunc(m.allOperations(), func(operation Operation) bool {
227+
return operation.HasError() || operation.isAborted()
228+
})
229+
}
230+
231+
func (m *Mta) GetErrorOperation() string {
232+
operations := m.allOperations()
233+
234+
errIndex := slices.IndexFunc(operations, func(operation Operation) bool {
235+
return operation.HasError() || operation.isAborted()
236+
})
237+
238+
return operations[errIndex].GetError()
239+
}
240+
241+
func (m *Mta) allOperations() []Operation {
242+
operations := []Operation{}
243+
244+
if m.Status.AtProvider.LastOperation != nil && m.Status.AtProvider.LastOperation.ID != nil {
245+
operations = append(operations, *m.Status.AtProvider.LastOperation)
246+
}
247+
248+
if m.Status.AtProvider.Files == nil {
249+
return operations
250+
}
251+
252+
for _, v := range *m.Status.AtProvider.Files {
253+
if v.LastOperation != nil && v.LastOperation.ID != nil {
254+
operations = append(operations, *v.LastOperation)
255+
}
256+
}
257+
258+
return operations
259+
}
260+
261+
// implement SpaceScoped interface
262+
func (m *Mta) GetSpaceRef() *SpaceReference {
263+
return &m.Spec.ForProvider.SpaceReference
264+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package v1alpha1
2+
3+
// TODO clearly separate from lastoperation type e.g. "MTA last operation type"
4+
type Operation struct {
5+
ID *string `json:"id,omitempty"`
6+
Error *string `json:"error,omitempty"`
7+
State *string `json:"state,omitempty"`
8+
}
9+
10+
func (o *Operation) IsRunning() bool {
11+
if o.State == nil {
12+
return true
13+
}
14+
return *o.State == "RUNNING"
15+
}
16+
17+
func (o *Operation) HasError() bool {
18+
if o.State == nil {
19+
return false
20+
}
21+
return *o.State == "ERROR"
22+
}
23+
24+
func (o *Operation) isAborted() bool {
25+
if o.State == nil {
26+
return false
27+
}
28+
return *o.State == "ABORTED"
29+
}
30+
31+
func (o *Operation) GetError() string {
32+
if !o.HasError() && !o.isAborted() {
33+
return ""
34+
}
35+
if len(*o.Error) > 0 {
36+
return *o.Error
37+
}
38+
return *o.State
39+
}

0 commit comments

Comments
 (0)