k8c.io/api/v3@v3.0.0-20230904060738-b0a93889c0b6/pkg/apis/apps.kubermatic/v1/application_installation.go (about) 1 /* 2 Copyright 2023 The Kubermatic Kubernetes Platform contributors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package v1 18 19 import ( 20 corev1 "k8s.io/api/core/v1" 21 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 "k8s.io/apimachinery/pkg/runtime" 23 ) 24 25 const ( 26 // ApplicationInstallationResourceName represents "Resource" defined in Kubernetes. 27 ApplicationInstallationResourceName = "applicationinstallations" 28 29 // ApplicationInstallationKindName represents "Kind" defined in Kubernetes. 30 ApplicationInstallationKindName = "ApplicationInstallations" 31 32 // ApplicationInstallationsFQDNName represents "FQDN" defined in Kubernetes. 33 ApplicationInstallationsFQDNName = ApplicationInstallationResourceName + "." + GroupName 34 ) 35 36 // +genclient 37 // +kubebuilder:object:root=true 38 // +kubebuilder:subresource:status 39 // +kubebuilder:resource:shortName=appinstall 40 41 // ApplicationInstallation describes a single installation of an Application. 42 type ApplicationInstallation struct { 43 metav1.TypeMeta `json:",inline"` 44 metav1.ObjectMeta `json:"metadata,omitempty"` 45 46 Spec ApplicationInstallationSpec `json:"spec,omitempty"` 47 Status ApplicationInstallationStatus `json:"status,omitempty"` 48 } 49 50 // +kubebuilder:object:root=true 51 52 // ApplicationInstallationList is a list of ApplicationInstallations. 53 type ApplicationInstallationList struct { 54 metav1.TypeMeta `json:",inline"` 55 metav1.ListMeta `json:"metadata,omitempty"` 56 57 Items []ApplicationInstallation `json:"items"` 58 } 59 60 type ApplicationInstallationSpec struct { 61 // Namespace describe the desired state of the namespace where application will be created. 62 Namespace AppNamespaceSpec `json:"namespace"` 63 64 // ApplicationRef is a reference to identify which Application should be deployed 65 ApplicationRef ApplicationRef `json:"applicationRef"` 66 67 // Values describe overrides for manifest-rendering. It's a free yaml field. 68 // +kubebuilder:pruning:PreserveUnknownFields 69 Values runtime.RawExtension `json:"values,omitempty"` 70 // As kubebuilder does not support interface{} as a type, deferring json decoding, seems to be our best option (see https://github.com/kubernetes-sigs/controller-tools/issues/294#issuecomment-518379253) 71 72 // ReconciliationInterval is the interval at which to force the reconciliation of the application. By default, Applications are only reconciled 73 // on changes on spec, annotations, or the parent application definition. Meaning that if the user manually deletes the workload 74 // deployed by the application, nothing will happen until the application CR change. 75 // 76 // Setting a value greater than zero force reconciliation even if no changes occurred on application CR. 77 // Setting a value equal to 0 disables the force reconciliation of the application (default behavior). 78 // Setting this too low can cause a heavy load and may disrupt your application workload depending on the template method. 79 ReconciliationInterval metav1.Duration `json:"reconciliationInterval,omitempty"` 80 81 // DeployOptions holds the settings specific to the templating method used to deploy the application. 82 DeployOptions *DeployOptions `json:"deployOptions,omitempty"` 83 } 84 85 // DeployOptions holds the settings specific to the templating method used to deploy the application. 86 type DeployOptions struct { 87 Helm *HelmDeployOptions `json:"helm,omitempty"` 88 } 89 90 // HelmDeployOptions holds the deployment settings when templating method is Helm. 91 type HelmDeployOptions struct { 92 // Wait corresponds to the --wait flag on Helm cli. 93 // if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as timeout 94 Wait bool `json:"wait,omitempty"` 95 96 // Timeout corresponds to the --timeout flag on Helm cli. 97 // time to wait for any individual Kubernetes operation. 98 Timeout metav1.Duration `json:"timeout,omitempty"` 99 100 // Atomic corresponds to the --atomic flag on Helm cli. 101 // if set, the installation process deletes the installation on failure; the upgrade process rolls back changes made in case of failed upgrade. 102 Atomic bool `json:"atomic,omitempty"` 103 104 // EnableDNS corresponds to the --enable-dns flag on Helm cli. 105 // enable DNS lookups when rendering templates. 106 // if you enable this flag, you have to verify that helm template function 'getHostByName' is not being used in a chart to disclose any information you do not want to be passed to DNS servers.(c.f. CVE-2023-25165) 107 EnableDNS bool `json:"enableDNS,omitempty"` 108 } 109 110 // AppNamespaceSpec describe the desired state of the namespace where application will be created. 111 type AppNamespaceSpec struct { 112 // Name is the namespace to deploy the Application into. 113 // Should be a valid lowercase RFC1123 domain name 114 // +kubebuilder:validation:Pattern:=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$` 115 // +kubebuilder:validation:MaxLength:=63 116 // +kubebuilder:validation:Type=string 117 Name string `json:"name"` 118 119 // +kubebuilder:default:=true 120 121 // Create defines whether the namespace should be created if it does not exist. Defaults to true 122 Create bool `json:"create"` 123 124 // Labels of the namespace 125 // More info: http://kubernetes.io/docs/user-guide/labels 126 // +optional 127 Labels map[string]string `json:"labels,omitempty"` 128 129 // Annotations of the namespace 130 // More info: http://kubernetes.io/docs/user-guide/annotations 131 // +optional 132 Annotations map[string]string `json:"annotations,omitempty"` 133 } 134 135 // ApplicationRef describes a KKP-wide, unique reference to an Application. 136 type ApplicationRef struct { 137 // Name of the Application. 138 // Should be a valid lowercase RFC1123 domain name 139 // +kubebuilder:validation:Pattern:=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$` 140 // +kubebuilder:validation:MaxLength:=63 141 // +kubebuilder:validation:Type=string 142 Name string `json:"name"` 143 144 // +kubebuilder:validation:Pattern:=v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))? 145 // +kubebuilder:validation:Type=string 146 147 // Version of the Application. Must be a valid SemVer version 148 Version string `json:"version"` 149 // (pattern taken from masterminds/semver we use https://github.com/Masterminds/semver/blob/master/version.go#L42) 150 151 // NOTE: We are not using Masterminds/semver here, as it keeps data in unexported fields witch causes issues for 152 // DeepEqual used in our reconciliation packages. At the same time, we are not using pkg/semver because 153 // of the reasons stated in https://github.com/kubermatic/kubermatic/pull/10891. 154 } 155 156 // ApplicationInstallationStatus denotes status information about an ApplicationInstallation. 157 type ApplicationInstallationStatus struct { 158 // Conditions contains conditions an installation is in, its primary use case is status signaling between controllers or between controllers and the API 159 Conditions map[ApplicationInstallationConditionType]ApplicationInstallationCondition `json:"conditions,omitempty"` 160 161 // ApplicationVersion contains information installing / removing application 162 ApplicationVersion *ApplicationVersion `json:"applicationVersion,omitempty"` 163 164 // Method used to install the application 165 Method TemplateMethod `json:"method"` 166 167 // HelmRelease holds the information about the helm release installed by this application. This field is only filled if template method is 'helm'. 168 HelmRelease *HelmRelease `json:"helmRelease,omitempty"` 169 170 // Failures counts the number of failed installation or updagrade. it is reset on successful reconciliation. 171 Failures int `json:"failures,omitempty"` 172 } 173 174 type HelmRelease struct { 175 // Name is the name of the release. 176 Name string `json:"name,omitempty"` 177 178 // Version is an int which represents the revision of the release. 179 Version int `json:"version,omitempty"` 180 181 // Info provides information about a release. 182 Info *HelmReleaseInfo `json:"info,omitempty"` 183 } 184 185 // HelmReleaseInfo describes release information. 186 // tech note: we can not use release.Info from Helm because the underlying type used for time has no json tag. 187 type HelmReleaseInfo struct { 188 // FirstDeployed is when the release was first deployed. 189 FirstDeployed metav1.Time `json:"firstDeployed,omitempty"` 190 191 // LastDeployed is when the release was last deployed. 192 LastDeployed metav1.Time `json:"lastDeployed,omitempty"` 193 194 // Deleted tracks when this object was deleted. 195 Deleted metav1.Time `json:"deleted,omitempty"` 196 197 // Description is human-friendly "log entry" about this release. 198 Description string `json:"description,omitempty"` 199 200 // Status is the current state of the release. This field can take one of the 201 // values of helm.hhs/helm/v3/pkg/releases/Status. 202 Status string `json:"status,omitempty"` 203 204 // Notes is the rendered templates/NOTES.txt if available. 205 Notes string `json:"notes,omitempty"` 206 } 207 208 type ApplicationInstallationCondition struct { 209 // Status of the condition, one of True, False, Unknown. 210 Status corev1.ConditionStatus `json:"status"` 211 // Last time we got an update on a given condition. 212 // +optional 213 LastHeartbeatTime metav1.Time `json:"lastHeartbeatTime,omitempty"` 214 // Last time the condition transit from one status to another. 215 // +optional 216 LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` 217 // (brief) reason for the condition's last transition. 218 Reason string `json:"reason,omitempty"` 219 // Human readable message indicating details about last transition. 220 Message string `json:"message,omitempty"` 221 222 // observedGeneration represents the .metadata.generation that the condition was set based upon. 223 // For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date 224 // with respect to the current state of the instance. 225 // +optional 226 ObservedGeneration int64 `json:"observedGeneration,omitempty"` 227 } 228 229 // +kubebuilder:validation:Enum=ManifestsRetrieved;Ready 230 231 // swagger:enum ApplicationInstallationConditionType 232 // All condition types must be registered within the `AllApplicationInstallationConditionTypes` variable. 233 type ApplicationInstallationConditionType string 234 235 const ( 236 // ManifestsRetrieved indicates all necessary manifests have been fetched from the external source. 237 ManifestsRetrieved ApplicationInstallationConditionType = "ManifestsRetrieved" 238 239 // Ready describes all components have been successfully rolled out and are ready. 240 Ready ApplicationInstallationConditionType = "Ready" 241 ) 242 243 var AllApplicationInstallationConditionTypes = []ApplicationInstallationConditionType{ 244 ManifestsRetrieved, 245 Ready, 246 } 247 248 // SetCondition of the applicationInstallation. It take care of update LastHeartbeatTime and LastTransitionTime if needed. 249 func (appInstallation *ApplicationInstallation) SetCondition(conditionType ApplicationInstallationConditionType, status corev1.ConditionStatus, reason, message string) { 250 now := metav1.Now() 251 252 condition, exists := appInstallation.Status.Conditions[conditionType] 253 if exists && condition.Status != status { 254 condition.LastTransitionTime = now 255 } 256 257 condition.Status = status 258 condition.LastHeartbeatTime = now 259 condition.Reason = reason 260 condition.Message = message 261 condition.ObservedGeneration = appInstallation.Generation 262 263 if appInstallation.Status.Conditions == nil { 264 appInstallation.Status.Conditions = map[ApplicationInstallationConditionType]ApplicationInstallationCondition{} 265 } 266 appInstallation.Status.Conditions[conditionType] = condition 267 } 268 269 // SetReadyCondition sets the ReadyCondition and appInstallation.Status.Failures counter according to the installError. 270 func (appInstallation *ApplicationInstallation) SetReadyCondition(installErr error, hasLimitedRetries bool) { 271 if installErr != nil { 272 appInstallation.SetCondition(Ready, corev1.ConditionFalse, "InstallationFailed", installErr.Error()) 273 if hasLimitedRetries { // increment only if limited retries to avoid overflow otherwise 274 appInstallation.Status.Failures++ 275 } 276 } else { 277 appInstallation.SetCondition(Ready, corev1.ConditionTrue, "InstallationSuccessful", "application successfully installed or upgraded") 278 appInstallation.Status.Failures = 0 279 } 280 }