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  }