Build Tactics¶
When building charms, multiple layers are brought together in an ordered,
depth-first recursive fashion. The individual files of each layer are merged
according to a list of merge tactics. These tactics determine whether the file
from a higher layer will replace or be merged with the copy from the lower
layer, with the details of how the merge happens being implemented by the
tactic. Each file is tested against each tactic in a specific order (as
determined by the DEFAULT_TACTICS
list), with the first one to match being
applied to the file and all other tactics disregarded.
Built-in Tactics¶
ActionsYAML |
Tactic for processing and combining the actions.yaml file from each layer. |
ConfigYAML |
Tactic for processing and combining the config.yaml file from each layer. |
CopyTactic |
Tactic to copy a file without modification or merging. |
CopyrightTactic |
Tactic to combine the copyright info from all layers into a final machine-readable format. |
DistYAML |
Tactic for processing and combining the dist.yaml file from each layer. |
DynamicHookBind |
Base class for process hooks dynamically generated from the hook template. |
ExactMatch |
Mixin to match a file with an exact name. |
ExcludeTactic |
Tactic to handle per-layer excludes. |
IgnoreTactic |
Tactic to handle per-layer ignores. |
InstallerTactic |
Tactic to process any .pypi files and install Python packages directly into the charm’s lib/ directory. |
InterfaceBind |
Tactic to copy the hook template into place for all relation hooks. |
InterfaceCopy |
Tactic to process a relation endpoint using an interface layer. |
JSONTactic |
Base class for tactics dealing with JSON data. |
LayerYAML |
Tactic for processing and combining the layer.yaml file from each layer. |
ManifestTactic |
Tactic to avoid copying a build manifest file from a base layer. |
MetadataYAML |
Tactic for processing and combining the metadata.yaml file from each layer. |
ResourcesYAML |
Tactic for processing and combining the resources.yaml file from each layer. |
SerializedTactic |
Base class for tactics which deal with serialized data, such as YAML or JSON. |
StandardHooksBind |
Tactic to copy the hook template into place for all standard hooks. |
StorageBind |
Tactic to copy the hook template into place for all storage hooks. |
Tactic |
Base class for all tactics. |
VersionTactic |
Tactic to generate the version file with VCS revision info to be displayed in juju status . |
WheelhouseTactic |
Tactic to process the wheelhouse.txt file and build a source-only wheelhouse of Python packages in the charm’s wheelhouse/ directory. |
YAMLTactic |
Base class for tactics dealing with YAML data. |
extend_with_default |
Extend a jsonschema validator to propagate default values prior to validating. |
load_tactic |
Load a tactic from the current layer using a dotted path. |
-
class
charmtools.build.tactics.
ActionsYAML
(*args, **kwargs)¶ Tactic for processing and combining the
actions.yaml
file from each layer.
-
class
charmtools.build.tactics.
ConfigYAML
(*args, **kwargs)¶ Tactic for processing and combining the
config.yaml
file from each layer.
-
class
charmtools.build.tactics.
CopyTactic
(entity, target, layer, next_config)¶ Tactic to copy a file without modification or merging.
The last version of the file “wins” (e.g., from the charm layer).
This is the final fallback tactic if nothing else matches.
-
class
charmtools.build.tactics.
CopyrightTactic
(*args, **kwargs)¶ Tactic to combine the copyright info from all layers into a final machine-readable format.
-
class
charmtools.build.tactics.
DistYAML
(*args, **kwargs)¶ Tactic for processing and combining the
dist.yaml
file from each layer.
-
class
charmtools.build.tactics.
DynamicHookBind
(name, owner, target, config, output_files, template_file)¶ Base class for process hooks dynamically generated from the hook template.
This tactic is not used directly, but serves as a base for the type-specific dynamic hook tactics, like
StandardHooksBind
, orInterfaceBind
.-
HOOKS
= []¶ List of all hooks to populate.
-
sign
()¶ Sign all hook files generated by this tactic.
-
-
class
charmtools.build.tactics.
ExactMatch
¶ Mixin to match a file with an exact name.
-
FILENAME
= None¶ The filename to be matched
-
classmethod
trigger
(entity, target, layer, next_config)¶ Match if the current entity’s filename is what we’re looking for.
-
-
class
charmtools.build.tactics.
ExcludeTactic
(entity, target, layer, next_config)¶ Tactic to handle per-layer excludes.
If a given layer’s
layer.yaml
has anexclude
list, then any file or directory included in that list that is provided by the current layer will be ignored, though any matching file or directory provided by base layers or any higher level layers will be included.The
exclude
list uses the same format as a.gitignore
file.
-
class
charmtools.build.tactics.
IgnoreTactic
(entity, target, layer, next_config)¶ Tactic to handle per-layer ignores.
If a given layer’s
layer.yaml
has anignore
list, then any file or directory included in that list that is provided by base layers will be ignored, though any matching file or directory provided by the current or any higher level layers will be included.The
ignore
list uses the same format as a.gitignore
file.
-
class
charmtools.build.tactics.
InstallerTactic
(entity, target, layer, next_config)¶ Tactic to process any
.pypi
files and install Python packages directly into the charm’slib/
directory.This is used in Kubernetes type charms due to the lack of a proper install or bootstrap phase.
-
class
charmtools.build.tactics.
InterfaceBind
(name, owner, target, config, output_files, template_file)¶ Tactic to copy the hook template into place for all relation hooks.
This tactic is not part of the normal set of tactics that are matched against files. Instead, it is manually called to fill in the set of relation hooks needed by this charm.
-
class
charmtools.build.tactics.
InterfaceCopy
(interface, relation_name, role, target, config)¶ Tactic to process a relation endpoint using an interface layer.
This tactic is not part of the normal set of tactics that are matched against files. Instead, it is manually called for each relation endpoint that has a corresponding interface layer.
-
class
charmtools.build.tactics.
JSONTactic
(*args, **kwargs)¶ Base class for tactics dealing with JSON data.
-
dump
(data)¶ Serialize and write the data to the file.
Must be impelemented by a subclass.
-
load
(fn)¶ Load and deserialize the data from the file.
Must be impelemented by a subclass.
-
-
class
charmtools.build.tactics.
LayerYAML
(*args, **kwargs)¶ Tactic for processing and combining the
layer.yaml
file from each layer.The input
layer.yaml
files can contain the following sections:includes
This is the heart of layering. Layers and interface layers referenced in this list value are pulled in during charm build and combined with each other to produce the final layer.defines
This object can contain a jsonschema used to define and validate options passed to this layer from another layer. The options and schema will be namespaced by the current layer name.options
This object can contain option name/value sections for other layers.config
,metadata
,dist
, orresources
These objects can contain adeletes
object to list keys that should be deleted from the resulting<section>.yaml
.
Example, layer
foo
might define thislayer.yaml
file:includes: - layer:basic - interface:foo defines: foo-opt: type: string default: 'foo-default' options: basic: use_venv: true
And layer
bar
might define thislayer.yaml
file:includes: - layer:foo options: foo-opt: 'bar-value' metadata: deletes: - 'requires.foo-relation'
-
class
charmtools.build.tactics.
ManifestTactic
(entity, target, layer, next_config)¶ Tactic to avoid copying a build manifest file from a base layer.
-
class
charmtools.build.tactics.
MetadataYAML
(*args, **kwargs)¶ Tactic for processing and combining the
metadata.yaml
file from each layer.
-
class
charmtools.build.tactics.
ResourcesYAML
(*args, **kwargs)¶ Tactic for processing and combining the
resources.yaml
file from each layer.
-
class
charmtools.build.tactics.
SerializedTactic
(*args, **kwargs)¶ Base class for tactics which deal with serialized data, such as YAML or JSON.
-
apply_edits
()¶ Apply any edits defined in the final
layer.yaml
file to the data.An example edit definition:
metadata: deletes: - requires.http
-
combine
(existing)¶ Merge the deserialized data from two layers using
deepmerge
.
-
dump
(data)¶ Serialize and write the data to the file.
Must be impelemented by a subclass.
-
load
(fn)¶ Load and deserialize the data from the file.
Must be impelemented by a subclass.
-
process
()¶ Now that the tactics for the current entity have been combined for all layers, process the entity to produce the final output file.
Must be implemented by a subclass.
-
read
()¶ Read and cache the data into memory, using
self.load()
.
-
-
class
charmtools.build.tactics.
StandardHooksBind
(name, owner, target, config, output_files, template_file)¶ Tactic to copy the hook template into place for all standard hooks.
This tactic is not part of the normal set of tactics that are matched against files. Instead, it is manually called to fill in the standard set of hook implementations.
-
class
charmtools.build.tactics.
StorageBind
(name, owner, target, config, output_files, template_file)¶ Tactic to copy the hook template into place for all storage hooks.
This tactic is not part of the normal set of tactics that are matched against files. Instead, it is manually called to fill in the set of storage hooks needed by this charm.
-
class
charmtools.build.tactics.
Tactic
(entity, target, layer, next_config)¶ Base class for all tactics.
Subclasses must implement at least
trigger
andprocess
, and probably also want to implementcombine
.-
combine
(existing)¶ Produce a tactic informed by the existing tactic for an entry.
This is when a rule in a higher level charm overrode something in one of its bases for example.
Should be implemented by a subclass if any sort of merging behavior is desired.
-
config
¶ Return the combined config from the layer above this (if any), this, and all lower layers.
Note that it includes one layer higher so that the tactic can make decisions based on the upcoming layer.
-
current
¶ Alias for
Tactic.layer
-
entity
¶ The current entity (a.k.a. file) being processed.
-
classmethod
get
(entity, target, layer, next_config, current_config, existing_tactic)¶ Factory method to get an instance of the correct Tactic to handle the given entity.
-
layer
¶ The current layer under consideration
-
layer_name
¶ Name of the current layer being processed.
-
lint
()¶ Test the resulting file to ensure that it is valid.
Return
True
if valid. If invalid, returnFalse
or raise aBuildError
Should be implemented by a subclass.
-
process
()¶ Now that the tactics for the current entity have been combined for all layers, process the entity to produce the final output file.
Must be implemented by a subclass.
-
read
()¶ Read the contents of the file to be processed.
Can be implemented by a subclass. By default, returns
None
.
-
relpath
¶ The path to the file relative to the layer.
-
sign
()¶ Return signature in the form
{relpath: (origin layer, SHA256)}
Can be overridden by a subclass, but the default implementation will usually be fine.
-
target
¶ The target (final) layer.
-
target_file
¶ The location where the processed file will be written to.
-
classmethod
trigger
(entity, target, layer, next_config)¶ Determine whether the rule should apply to a given entity (file).
Generally, this should check the entity name, but could conceivably also inspect the contents of the file.
Must be implemented by a subclass or the tactic will never match.
-
-
class
charmtools.build.tactics.
VersionTactic
(charm, target, layer, next_config)¶ Tactic to generate the
version
file with VCS revision info to be displayed injuju status
.This tactic is not part of the normal set of tactics that are matched against files. Instead, it is manually called to generate the
version
file.
-
class
charmtools.build.tactics.
WheelhouseTactic
(*args, **kwargs)¶ Tactic to process the
wheelhouse.txt
file and build a source-only wheelhouse of Python packages in the charm’swheelhouse/
directory.-
read
()¶ Read the contents of the file to be processed.
Can be implemented by a subclass. By default, returns
None
.
-
-
class
charmtools.build.tactics.
YAMLTactic
(*args, **kwargs)¶ Base class for tactics dealing with YAML data.
Tries to ensure that the order of keys is preserved.
-
dump
(data)¶ Serialize and write the data to the file.
Must be impelemented by a subclass.
-
load
(fn)¶ Load and deserialize the data from the file.
Must be impelemented by a subclass.
-
-
charmtools.build.tactics.
extend_with_default
(validator_class)¶ Extend a jsonschema validator to propagate default values prior to validating.
Used internally to ensure validation of layer options supports default values.
Custom Tactics¶
A charm or layer can also define one or more custom tactics in its layer.yaml
file. The file can contain a top-level tactics
key, whose value is a list of
dotted Python module names, relative to the layer’s base directory. For
example, a layer could include this in its layer.yaml
:
tactics:
- tactics.my_layer.READMETactic
This would cause the build command to look for a module tactics/my_layer.py
with a class of READMETactic
in it, which must inherit from
Tactic
.
Custom tactics are tested before the built-in tactics, so they can override the behavior of built-in tactics if desired. Care should be taken if doing this because changing the behavior of built-in tactics can end up breaking other layers or charms.