Package Resolution Policy Examples

This section describes how to use the package allow, default, lock, and retire claim types to write policy rules to ensure that jobs in a particular namespace use the correct package. See Policy Permissions for additional guidance.

Controlling Package Resolution with Policy

Policy offers a robust system to control the versions and types of packages and runtimes available to various teams in your organization. Consider a scenario where a security update renders an old version of a runtime undesirable for use in production. In this situation, you want to ensure that newly staged packages do not rely upon the insecure runtime, without impacting live applications. In this case you can retire the package.

There are four claim types for controlling package resolution. Note that these claims are applied to job resources.

The following sections explain each package resolution claim type in more detail. See also the following video tutorial on package resolution:

Natural package resolution

The claim type package.allow points to a package FQN that is allowed to be used by jobs in a given namespace. If the package FQN doesn't have a local name, all packages within the namespace are accessible.

For example, if you want to write policy such that:

  • All the jobs in the root (/) namespace have access to packages in the /apcera namespace, and

  • All the jobs in /prod namespace have access to packages in /apcera/pkg and packages in /prod/pkg namespace.

You could implement the following policy rules:

job::/ {
  {package.allow "package::/apcera"}
}
job::/prod {
  {package.allow "package::/prod/pkg"}
}

Any package FQNs allowed on a namespace will be accessible throughout all child namespaces.

Default package resolution

The claim type package.default imposes a soft requirement that a dependency type and name on jobs in a namespace should use a specific package if there are no other hard requirements (that is, package.lock, see below). Apcera provides a list of default packages for every cluster, which can be overridden with this type of package resolution policy.

For example, you want to write policy such that:

  • All jobs in the /prod namespace that depend on ruby runtime, should use ruby-2.1.2 as a default runtime (in absence of other overrides).

You could implement the following policy rule:

job::/prod {
  if (dependency beginsWith runtime.ruby) {
  	package.default "package::/apcera/pkg/runtimes::ruby-2.1.2"
  }
}

You can override the default policy using the hard requirements package.lock or package.retire

Strictly enforce package resolution

The claim type package.lock enforces the hard requirement of using a specified package as a dependency by jobs in a namespace. This ensures that the dependency requirement is always met.

For example, if you want to write policy such that all jobs in the (/prod) namespace:

  • That depend on Ruby runtime, will use the Ruby runtime in the /prod namespace, and

  • Will only use the Ubuntu-14.04 operating system.

In this case you could use the following policy rules to enforce these dependencies:

on job::/prod {
  if (dependency equals runtime.ruby) { package.lock "package::/prod::ruby" }
  if (dependency beginsWith os) { package.lock "package::/apcera/pkg/os::ubuntu-14.04" }
}

Before using package.lock, keep in mind the following important considerations:

  • Package locks are hard requirements. As such, they should be used sparingly and with care. If you have a package with multiple provides and a policy that locks one of those provides, the system will always use the locked package, regardless if a different package is specified during job creation.

  • For most situations package.default is preferred over package.lock because package.default is a soft requirement. When package.default is used, job creation will at least attempt to use the system default packages if the specified one doesn't actually exist.

  • In some cases, the package.lock claim can conflict with the package.retire claim, leading to a resolution that cannot be fufilled.

Retire packages

The claim type package.retire points to any packages that can no longer be used by jobs in a namespace or any child namespaces. This is typically used in scenarios where you want to deprecate a package.

For example, if you want to write a policy such that:

  • No jobs in the /prod namespace can depend upon ruby-1.8.7.

You could implement the following policy rule:

on job::/prod {
  { package.retire "package::/apcera/pkg/runtimes::ruby-1.8.7" }
}

With this approach, existing jobs remain functional, but new packages won't be allowed to depend upon that package. If a retired package happens to be locked for a certain dependency, users that try to use the package for that dependency will receive an error when staging their package.