--- title: "Packaging API dependency management" authors: [ "Joao Pereira <joao.pereira@broadcom.com>" ] status: "draft" approvers: [ "Dmitriy Kalinin <dkalinin@vmware.com>" ] --- # Packaging API dependency management ## Problem Statement kapp-controller is being used in many contexts to deploy applications that users want to have in their clusters. One main problem that was never addressed was how can a user install multiple packages or even how can a user say that a particular package needs other packages or resources to be present in the cluster in order for it to work. The primary way this was being accomplished today was to create a Package that was able to create PackageInstalls for these other Packages, since kapp-controller does not do any validation of the resources that are being installed in the cluster. We believe that this method is effective when the package author is responsible for packaging all these other package, but in a world where we can have multiple package authors and each one of them only manages a subset of these packages we need a better solution. In this proposal we are going to propose some changes to the Packaging API that will allow the Package Authors to reference other Packages that they depend on as well as expand the API in a way that would allow them to be even more generic, by creating dependencies on the APIs present in the cluster. ## Terminology / Concepts *GVK* - Group Version Kind, it is a way in Kubernetes to define a particular API ## Proposal In this proposal we will describe the changes that are necessary to enable the Packaging API to support dependency management. With these changes we ### Goals and Non-goals #### Goals - Evolve the current Packaging API to support dependency management #### Non-goals - Define implementation details ### Specification / Use Cases Updated Package API ```yaml= apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: internalpackages.internal.packaging.carvel.dev spec: group: internal.packaging.carvel.dev names: kind: InternalPackage listKind: InternalPackageList plural: internalpackages singular: internalpackage scope: Namespaced versions: - name: v1alpha1 schema: openAPIV3Schema: properties: ... spec: properties: ... installationType: description: Different types of installation available. type: string enum: - onePerCluster - onePerNamespace - noRestrictions default: noRestrictions dependencies: description: Dependencies list of all the dependencies of the current package items: &dependencies description: Dependency contains the possible dependencies restrictions properties: gvk: description: GVK contains the version of an API that the Package depends on properties: apiVersion: description: APIVersion contains group and version of the API type: string kind: description: Kind contains the kind of the API type: string required: - apiVersion - kind type: object or: description: OrDependency contains a list of multiple dependencies that only 1 needs to be present type: array items: *dependencies package: description: Package contains the reference to a Package and version properties: refName: description: RefName Reference of the Package type: string version: description: Version Version of the Package type: string type: object type: object type: array provides: description: List of GVKs that this Package provides items: description: GVK API provided properties: apiVersion: description: APIVersion contains group and version of the API type: string kind: description: Kind contains the kind of the API type: string required: - apiVersion - kind type: object type: array ``` Updated PackageInstall API ```yaml= apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: packageinstalls.packaging.carvel.dev spec: group: packaging.carvel.dev names: categories: - carvel kind: PackageInstall listKind: PackageInstallList plural: packageinstalls shortNames: - pkgi singular: packageinstall scope: Namespaced versions: - name: v1alpha1 schema: openAPIV3Schema: description: A Package Install is an actual installation of a package and its underlying resources on a Kubernetes cluster. It is represented in kapp-controller by a PackageInstall CR. A PackageInstall CR must reference a Package CR. properties: ... spec: properties: ... dependencies: description: Dependencies that need overrides properties: install: default: true description: When install is set to false it will only validate the present of the dependencies, when set to true it will try to install the Packages type: boolean override: description: Override the versions of packages to be installed items: properties: refName: type: string versionSelection: properties: constraints: type: string prereleases: properties: identifiers: items: type: string type: array type: object type: object type: object type: array values: description: List of secrets that contain values that will be used for package to be installed, only needed if Packages should be installed items: properties: alias: description: When Alias is set at the Package level this field should be used type: string refName: description: Reference to the Package the secret should be used on type: string secretRef: description: Reference to the secret properties: key: type: string name: type: string type: object required: - secretRef type: object type: array type: object status: properties: ... dependencies: description: Array will all the fullfilled dependencies information items: properties: created: description: Was this dependency created by the current PackageInstall type: boolean package: properties: packageInstallRef: description: PackageInstall reference type: string refName: description: Name of the Package type: string version: description: Exact version that was installed or is present in the cluster type string type: object gvk: description: Some of the information in this section might not be surfaced if the GVK is native to kubernetes or installed using a different method properties: providedBy: description: Package name that provides this GVK type: string version: description: Version of the Package type: string type: array ``` Package Example: ```yaml= apiVersion: data.packaging.carvel.dev/v1alpha1 kind: Package metadata: name: pkg.test.carvel.dev.1.0.0 spec: refName: pkg.test.carvel.dev version: 1.0.0 installationType: onePerCluster dependencies: packages: - or: - gvk: apiVersion: some.api.org/v1 kind: MainAPI - package: refName: pkg2.test.carvel.dev version: ">=1.0.0" - package: refName: pkg3.test.carvel.dev version: 0.1.0 - package: refName: pkg4.test.carvel.dev version: 0.2.0 optional: true ``` PackageInstall Example: ```yaml= apiVersion: packaging.carvel.dev/v1alpha1 kind: PackageInstall metadata: name: pkg-demo spec: serviceAccountName: default-ns-sa packageRef: refName: pkg.test.carvel.dev versionSelection: constraints: 1.0.0 dependencies: override: - refName: pkg1.test.carvel.dev versionSelection: contraints: 1.0.1 - refName: pkg4.test.carvel.dev install: false # Will not install this Package values: - refName: pkg1.test.carvel.dev secretRef: name: pkg1.dev-values - refName: pkg3.test.carvel.dev secretRef: name: pkg3.dev-values status: dependencies: - packageInstallRef: pkg1.test.carvel.dev.1.0.0 - packageInstallRef: pkg3.test.carvel.dev.1.0.0 ``` Example of a PackageInstall that is automatically created ```yaml= apiVersion: packaging.carvel.dev/v1alpha1 kind: PackageInstall metadata: name: pkg3.test.carvel.dev.1.0.0 annotations: kapp-controller.carvel.dev/owner: "PackageInstall/pkg-demo" spec: serviceAccountName: default-ns-sa packageRef: refName: pkg3.test.carvel.dev versionSelection: constraints: 1.0.0 values: secretRef: name: pkg3.dev-values ``` #### Dependency resolution The dependency resolution will be done one PackageInstall at a time, basically each PackageInstall will only create the PackageInstalls that it directly depends on. ex: ``` When pkg depends on pkg1 and pkg3, while pkg1 depends on pkg2 kapp-controller when it is processing pkg it will only create PackageInstalls for pkg1 and pkg3. pkg -> pkg1 -> pkg2 -> pkg3 ``` For future versions we can contenplate the possibility of each PackageInstall checking if the dependencies it has can be fulfilled with the Packages present in the Cluster. ##### Alternative dependency resolution Each package would be responsible for creating all the PackageInstall that it depends on as well as all the PackageInstalls that the dependencies might depend on ex: ``` When pkg depends on pkg1 and pkg3, while pkg1 depends on pkg2 kapp-controller when it is processing pkg it will create PackageInstalls for pkg1, pkg2 and, pkg3. pkg -> pkg1 -> pkg2 -> pkg3 ``` #### Use Cases ##### Package depends on another Package - When the Package that match the dependency is already installed, no new installation is needed - When the dependent Pacakage is not installed, it will try to install it - When a dependent Package is already installed but the version does not fullfil the requirements of the current PackageInstall, kapp-controller will try to install the version that matches - When there is no Package that fullfills the dependency of a PackageInstall the installation will fail - ##### Two Packages depend on the same Package In this scenario the first PackageInstall that creates the dependent PackageInstall will own it. This means that the second PackageInstall will not need to create the dependent PackageInstall ##### A Package uses "or" dependency In this case kapp-controller will go in order to try to fulfil this dependency. This scenario will be more clear when there is a GVK dependency or if there are multiple dependencies and the user decided not to install dependencies #### Future explorations ##### Automatic search and installation of packages In case of gvk dependencies or other dependencies that we can create in the future it is possible for a package to automatically find a Package that could provide the needed functionality. ```yaml= apiVersion: data.packaging.carvel.dev/v1alpha1 kind: Package metadata: name: some-pkg.test.carvel.dev.1.0.0 spec: refName: some-pkg.test.carvel.dev version: 1.0.0 dependencies: - gvk: group: api.carvel.dev kind: Resource version: v1 --- apiVersion: data.packaging.carvel.dev/v1alpha1 kind: Package metadata: name: provider.carvel.dev.1.0.0 spec: refName: provider.carvel.dev version: 1.0.0 provides: - gvk: apiVersion: api.carvel.dev/v1 kind: Resource ``` In the example above the author of the `some-pkg.test.carvel.dev` does not have to specify a particular Package to install because kapp-controller would be able to automatically find and install the needed package ##### Force deletion of dependent packages When creating a PackageInstall the user can define if they want to delete the dependent packages when the current PackageInstall is removed. ##### Enforce dependent PackageInstall cannot be modified The current proposal assume that by convension the PackageInstalls created by kapp-controller should not be updated. Nevertheless we can explore the possibility of creating a webhook that will enforce this behavior. ### Other Approaches Considered _Mention of other reasonable ways that the problem(s) could be addressed with rationale for why they were less desirable than the proposed approach._ ## Open Questions - What happens if the Resource did not change version but new fields where added and the package requires them but that version of the Resource is not present in the cluster? ## Answered Questions _A list of questions that have been answered._