Package Resolution

Part of Apcera's process for deploying and governing applications is to ensure that each application's needs are satisfied accurately, subject to policies that you set. Packaging an application, or any executable unit in Apcera, entails identifying what it needs and bundling that information with it as metadata. This works because other packages can satisfy those needs. Packages that can satisfy needs carry that information as metadata too.

Metadata – provides and dependencies

A package's needs are called dependencies, and the needs a package can satisfy are called provides (singular, a provides). A package can have both dependencies and provides.

As metadata associated with a package, each dependency and each provides has the following elements:

  • type (os, package, file, runtime)
  • name (a subtype or version of the type)

For example, you use a package with a provides of type os to create a capsule.

Package provides and dependencies are generally represented with dot notation. For example, os.ubuntu describes a dependency or a provides for a package that needs or provides the Ubuntu operating system.

When you create a package you can specify dependencies and provides at different levels of granularity. For example, the Ubuntu-14.04 operating system package, provided by Apcera, has the following metadata:

Package: ubuntu-14.04
FQN: package::/apcera/pkg/os::ubuntu-14.04
State: ready
Provides: os: linux
  os: ubuntu
  os: ubuntu-14.04
Environment: PATH="$PATH:/bin:/sbin:/usr/bin:/usr/sbin"

Any package that depends on os.linux, os.ubuntu, or os.ubuntu-14.04 can use this package to satisfy that dependency.

APC resolves dependencies when it executes commands. For example, apc capsule create <your-capsule-name> -i linux creates a capsule from a package that provides the Linux operating system.

Dependencies describe what a package needs to operate. For example, the Ruby v2.0.0 p481 runtime has the following metadata:

Package: ruby-2.0.0-p481
FQN: package::/apcera/pkg/runtimes::ruby-2.0.0-p481
State: ready
Dependencies: os: ubuntu
Provides: runtime: ruby
  runtime: ruby-2.0
  runtime: ruby-2.0.0
  runtime: ruby-2.0.0-p481
Environment: PATH="/opt/apcera/ruby-2.0.0-p481/bin:$PATH"

Any app that depends on Ruby can use this package, but only if this package's dependency (os.ubuntu) is met. Thus, a package that provides os.ubuntu (for example, ubuntu-14.04 above) must be available.

Inspecting package metadata

The apc package show command displays a package's dependencies and provides.

The apc package list --type <type> command displays all packages of the specified type (os, package, file, runtime).

The apc package list --provides <name> command displays all packages that provide the specified subtype.

For example, to see all packages in the /apcera namespace with type os:

$ apc package list --type os --namespace /apcera

Or, to see all packages in the /apcera namespace that provide ubuntu:

$ apc package list --provides ubuntu --namespace /apcera

How Apcera resolves package dependencies

When you upload your application's source code, Apcera stages it. The stager prepares the source code and locates and resolves its dependencies, so the app has an appropriate runtime and operating system in its isolation context.

The stager matches the app's dependencies with provides throughout the system. It creates and traverses a graph of dependencies and provides to resolve to a complete set of concrete packages.

You can set policies that control which packages the stager can use in the graph it builds. This enables you to govern the versions and types of packages and runtimes available to various teams in your organization.

The following diagram illustrates how Apcera resolves package dependencies. In this example, 'My App' has 2 dependencies, 'X' and 'Z'. The staging coordinator creates a graph of the dependencies and available package provides and then matches them to resolve the dependencies.

screenshot

If you have two packages that satisfy the same dependency, the package that was imported first takes precedence, assuming there is no policy rule set up to specify otherwise.

How to specify application dependencies

You can set dependencies on a new application package in the following ways:

  • Let Apcera's built-in stagers choose the dependencies:

    For most basic application runtimes, Apcera's stagers can inspect your source code and determine what dependencies are needed in order to stage your app. They place depends tags on your application for the runtimes and packages necessary to make your app work.

  • Provide a runtime-specific file:

    Apcera's stagers support inspecting files like runtime.txt or package.json in order to determine what version best supports your app.

  • Issue an APC command:

    When you create an app using apc app create, you can specify additional dependencies using the --depends-on option. For example, to test your app on the latest ruby-2.1.0 version while your cluster's defaults point to ruby-2.0.0:

    $ apc app create my-ruby-app --depends-on runtime.ruby-2.1.0
    

    You can supply multiple packages and runtimes as a space-separated list.

You do not need to tag every package dependency at create time; Apcera's resolution process ensures that your app works with the best option available.

Control package resolution with policy

You use policy to control package resolution. Policy offers a robust system to control the version and types of packages and runtimes available in the system. Using policy, you can allow a set of packages, lock or establish a default for a certain dependency, or retire a package so that it is no longer available for use by new apps.

Refer to the Policy documentation for details on using policy rules to control package resolution.