Skip to main content

API

For information about the supported Rancher APIs see here

The Rancher UI contains functionality to manage Kubernetes resources.

Developers can create, edit, fetch and remove individual resources or fetch collections of them. Resources that are fetched, by default, will be automatically updated via WebSocket

In addition the Rancher UI exposes permissions functionality around resources which can determine if the signed in user can perform the various actions around them.

Resource Definitions

Schemas

Schemas are provided in bulk via Rancher and cached locally in the relevant store (management, rancher, etc).

A schema can be fetched synchronously via store getter

import { POD } from '@shell/config/types';

this.$store.getters['cluster/schemaFor'](POD)`

Troubleshooting: Cannot find new schema

Ensure that your schema text in /config/types.js is singular, not plural

As mentioned before a schema dictates the functionality available to that type and what is shown for the type in the UI.

A resource is an instance of a schema e.g. the admin user is an instance of type management.cattle.io.user.

Types

Each type has a Kubernetes API group and a name, but not necessarily a human-readable name. Types are used mainly for building side navigation and defining how the UI should build forms and list views for each resource. For more information about types and how to use them, there are helpful comments in store/type-map.js.

Vuex Stores

There are 3 main stores for communicating with different parts of the Rancher API:

  • management: Points at global-level resources for Rancher as a whole.
  • cluster: Points at resources for the currently selected cluster; changes when you change clusters.
  • rancher: Points at older global-level resources, but some cluster-level resources are actually stored here and not physically in the cluster to be available to the cluster store.

And then a bunch of others:

NameFor
action-menuMaintains the current selection for tables and handling bulk operations on them
authAuthentication, logging in and out, etc
catalogStores the index data for Helm catalogs and methods to find charts, determine if upgrades are available, etc
githubPart of authentication, communicating with the GitHub API
growlGlobal "growl" notifications in the corner of the screen
i18nInternationalization
indexThe root store, manages things like which cluster you're connected to and what namespaces should be shown
prefsUser preferences
type-mapMeta-information about all the k8s types that are available to the current user and how they should be displayed
wm"Window manager" at the bottom of the screen for things like container shells and logs.

Store objects are accessed in different ways, below are common ways they are referenced by models and components

Locationtypeobjectexample
/model/<resource type>Dispatching Actionsthis.$dispatchthis.$dispatch('cluster/find', { type: WORKLOAD_TYPES.JOB, id: relationship.toId }, { root: true })
/model/<resource type>Access getters (store type)this.$gettersthis.$getters['schemaFor'](this.type)
/model/<resource type>Access getters (all)this.$rootGettersthis.$rootGetters['productId']
componentDispatching Actionsthis.$store.dispatchthis.$store.dispatch(`${ inStore }/find`, { type: row.type, id: row.id })
componentAccess gettersthis.$store.gettersthis.$store.getters['rancher/byId'](NORMAN.PRINCIPAL, this.value)

Prefixing a property in a model with $, as per model rows above, results in calling properties on the store object directly.

Troubleshooting: Fetching the name of a resource type

Good - Trims the text and respects . in path to type's string - store.getters['type-map/labelFor']({ id: NORMAN.SPOOFED.GROUP_PRINCIPAL }, 2)

Bad - Does not trim text, issues when resource type contains "." - store.getters['i18n/t'](`typeLabel.${ NORMAN.SPOOFED.GROUP_PRINCIPAL }`, { count: 2 })

Checking User Permissions with Schemas

Schemas dictate

  • Which resources are shown
  • What operations (create, update, delete, etc) can be made against resource/s
  • What actions (archive, change password, etc) can be made against resource/s

In addition the resources themselves can dictate

  • What actions can be made against the collection

The above, plus other factors, will effect what is shown by the UI

  • Resources in the cluster explorer
  • Edit resource buttons
  • Delete resource
  • etc

The schema can be checked in the Rancher API at:

https://<rancher url/v1/schema/provisioning.cattle.io.clusters

To check if a user has access to a resource, you can see if they can see the corresponding schema:

const hasAccess = this.$store.getters[`cluster/schemaFor`](type);

return hasAccess ? this.$store.dispatch('cluster/findAll', { type }) : Promise.resolve([]);

Models

The ES6 class models in the models directory are used to represent Kubernetes resources. The class applies properties and methods to the resource, which defines how the resource can function in the UI and what other components can do with it. Different APIs return models in different structures, but the implementation of the models allows some common functionality to be available for any of them, such as someModel.name, someModel.description, setLabels or setAnnotations.

Much of the reused functionality for each model is taken from the Steve plugin. The class-based models use functionality from plugins/dashboard-store/resource-class.js.

The Resource class in plugins/dashboard-store/resource-class.js should not have any fields defined that conflict with any key ever returned by the APIs (e.g. name, description, state, etc used to be a problem). The SteveModel (plugins/steve/steve-class.js) and NormanModel (plugins/steve/norman-class.js) know how to handle those keys separately now, so the computed name/description/etc is only in the Steve implementation. It is no longer needed to use names like _name to avoid naming conflicts.

Extending Models

The Resource class in plugins/dashboard-store/resource-class.js is the base class for everything and should not be directly extended. (There is a proxy-based counterpart of Resource which is the default export from plugins/dashboard-store/resource-instance.js as well.) If a model needs to extend the basic functionality of a resource, it should extend one of these three models:

  • NormanModel: For a Rancher management type being loaded via the Norman API (/v3, the Rancher store). These have names, descriptions and labels at the root of the object.
  • HybridModel: This model is used for old Rancher types, such as a Project (mostly in management.cattle.io), that are loaded with the Steve API (/v1, the cluster/management stores). These have the name and description at the root, but labels under metadata.
  • SteveModel: Use this model for normal Kubernetes types such as workloads and secrets. The name, description and labels are under metadata.

The Norman and Hybrid models extend the basic Resource class. The Hybrid model is extended by the Steve model.

Model Creation

The Rancher API returns plain objects containing resource data, but we need to convert that data into classes so that we can use methods on them.

Whenever we get an object from the API, we run the classify function (at plugins/dashboard-store/classify.js) which looks at the type field and figures out what type it is supposed to be. That file gives you an instance of a model which you can use to access the properties.

Creating and Fetching Resources

Most of the options to create and fetch resources can be achieved via dispatching actions defined in /plugins/dashboard-store/actions.js

The most basic operations are find({type, id}) to load a single resource by ID, findAll({type}) load all of them. These (anything starting with find) are async calls to the API. Getters like all(type) and byId(type, id) are synchronous and return only info that has already been previously loaded. See plugins/dashboard-store/ for all the available actions and getters.

FunctionActionExample CommandDescription
createCreate$store.$dispatch('<store type>/create', <new object>)Creates a new Proxy object of the required type (type property must be included in the new object)
cloneClone$store.$dispatch('<store type>/clone', { resource: <existing object> })Performs a deep clone and creates a proxy from it
findAllFetch all of a resource type and watch for changes to the returned resources so that the list updates as it changes.$store.dispatch('<store type>/findAll', { type: <resource type> })Fetches all resources of the given type. Also, when applicable, will register the type for automatic updates. If the type has already been fetched return the local cached list instead
findFetch a resource by ID and watch for changes to that individual resource.$store.dispatch('<store type>/find', { type: <resource type>, id: <resource id> })Finds the resource matching the ID. If the type has already been fetched return the local cached instance.
findMatchingFetch resources by label and watch for changes to the returned resources, a live array that updates as it changes.$store.dispatch('<store type>/findMatching', { type: <resource type>, selector: <label name:value map> })Fetches resources that have metadata.labels matching that of the name-value properties in the selector. Does not support match expressions.

Once objects of most types are fetched they will be automatically updated. See Watching Resources for more info. For some types this does not happen. For those cases, or when an immediate update is required, adding force: true to the find style actions will result in a fresh http request.

Synchronous Fetching

It's possible to retrieve values from the store synchronously via getters. For resources this is not normally advised (they may not yet have been fetched), however for items such as schemas, it is valid. Some of the core getters are defined in /plugins/dashboard-store/getters.js:

$store.getters['<store type>/byId'](<resource type>, <id>])

$store.getters['<store type>/schemaFor'](<resource type>)`

The all, byId and matching getters are equivalents of the asynchronous findAll, find and findMatching, respectively.

Watching Resources

For each API call, a websocket is created for the cluster you're talking to. For example:

  • If you findAll Pods, Rancher will watch all Pods after that.
  • if you get an individual resource using findMatching, it will watch a particular resource with that ID in a namespace.

If you already called findAll for a resource, then do findMatching, no additional Pods would be watched because all of the Pod resources are already being watched. The subscribe call uses equivalentWatch to detect duplicate watches so that it won't send another request to watch the subset of resources that were already loaded previously.

The unsubscribe function is used to unwatch resources. However, as a general rule, resources are never unwatched because Rancher assumes that any data you have already loaded needs to be kept up-to-date.

The code related to watching resources is in plugins/steve/subscribe.js.

Pinging

The UI normally has three websocket connections with rancher (Steve's global cluster management), management (Norman) and cluster (Steve for an individual cluster). The UI is pinged by Steve every five seconds and by Norman every thirty seconds. Steve's messages send the server version they are sent from, which sends another action and reloads the page if the server has been upgraded.

To avoid excessive rerendering, the messages that change state, such as resource.{create, change, remove}, are saved in a buffer. Once per second they are all flushed together to update the store.

Resource Selectors

The parse utility function in utils/selector.js helps you match or convert labels. For example, matchLabels could be used to get all the Pods for each workload.

Kubernetes supports matching with matchExpressions, but the UI converts selectors that use match expressions to selectors with matchLabels when possible because matchLabels is simpler and better supported by Rancher. When loading multiple resources at once from the Rancher API, you can only use matchLabels, but if you are loading only one resource at a time, you can use matchExpressions.

Error Handling

When catching exceptions thrown by anything that contacts the API use utils/error exceptionToErrorsArray to correctly parse the response into a commonly accepted array of errors

Unauthenticated Endpoints

If you need to add an endpoint to an unauthenticated route for loading from the store before login, you will need to add it here.

Examining the Contents of a Resource

Due to the way Dashboard resources are constructed examining the contents of one can sometimes provide unexpected results. It's recommended to read the sections covering resource proxy and resource instance before continuing.

  • When viewing the object via template {{ resource }} the resource-instance.js toString method will print out a basic interpretation
  • When printing the object via console the resource's Proxy will be displayed. To make this simpler to view use JSON.parse(JSON.stringify(<resource>)).

Q Why are my resource's nameDisplay, description, etc missing?

A These are part of the common underlying resource-instance.js or, if the resource type has it, the type's own model.

API Calls to Third-party Domains

Rancher includes a proxy that can be used to make requests to third-party domains (like a cloud provider's API) without requiring that the other end supports CORS. Send requests to /meta/proxy/example.com/path and the request will be made from the Rancher server and proxied back to you.

Nodes vs. Machines

Upstream Kubernetes manages nodes, as shown in Cluster Explorer. Rancher deals with machines, which control node provisioning, and they are available from Cluster Management.

When you create a cluster, the cluster has a spec that contains machine pools. Each machine pool uses a machine config (rke-machine-config), which configures the size and role of the machine. Rancher sends the configuration to CAPI (Cluster API), which uses it to create the machines.

When provisioning RKE clusters, you could create node templates and define virtual machines in them. Then those definitions could be used when creating node pools. In contrast, RKE2 and K3s cluster provisioning uses CAPI as its standard for provisioning clusters, and CAPI requires that a machine template can only used once per node pool. For that reason, node templates cannot be shared across node pools the same way they used to be able to for RKE provisioning. The forms for provisioning new RKE2 and K3s clusters through Rancher don't introduce the concept of node templates. Instead they let you create new node pools for each new cluster.

UI Components for Resource Types

The dashboard has a framework and set of components to support (conditional) representation of resource type/s. Common UI features include

  • Collections of resources in a common table (Resource List). Usually shown when clicking on the side nav name type.
  • Visual overview of a resource (Resource Detail). Usually shown when clicking on a List's row's name.
  • Creating, Viewing and Editing a resource as a form (Resource Edit).
  • Viewing and Editing a resource as YAML (Resource YAML)

By default only the table and, if enabled by the resource type, viewing/editing as YAML are enabled. To provide a richer experience the resource's table columns should be defined and custom overview and edit pages provided.

The top level list page is defined in ./components/ResourceList. This displays a common masthead and table for the given resource type. Without any customisation the columns are restricted to a base set of state, nameDisplay, namespace and ages.

Any resources returned by getInstances should have a kind matching the required type. This results in the tables showing the correct actions, handling create/edit, etc.

Resource Detail Pages

The top level detail page is defined in ./components/ResourceDetail. This is a container page that covers a number of resource instance use cases (create, edit, view, etc). Like resource list this contains a common Masthead and additionally a sub header DetailTop (displays common properties of resources such as description, labels, annotations, etc). For a resource type that provides no customisation it will mostly likely just display a way to view and edit the resource by YAML.

The Create/Edit Yaml experience is controlled by /components/ResourceYaml.vue. Other features are handled by custom components described below.

Special attention should be made of the mode and as params that's available via the CreateEditView mixin (as well as other helpful functionality). Changing these should change the behaviour of the resource details page (depending on the availability of resource type custom components).

For more information about CreateEditView and how to add new create/edit forms, see Create/Edit Forms.

modeasContent
falsyfalsyShows the View YAML or Customised Detail component
falsyconfigShows the View YAML or Customised Edit component (in read only mode)
editfalsyShows the Customised Edit component
edityamlShows the Edit Yaml component

In addition the Create process (assessable with the same url + /create) is also managed by the resource detail page with similar param options.

modeasContent
falsyyamlShow the Edit YAML component in create mode
editfalsyShow the Customised Edit component in create mode
clonefalsyShows the Customised Edit component in create mode pre-populated with an existing resource

Customising Resource Detail Pages

A more detailed overview page can be added by creating a resource type component in /detail/. This should provide a more eye pleasing presentation than a collection of inputs or yaml blob.