- Extend the jsonnet
import
andimportstr
functions with the help of custom importers. - Choose either a specific importer or use the
NewMultiImporter()
function to enable all together. The prefix of an import path will route to the right importer. As a fallback the default go-jsonnetFileImporter
will be used. - A custom importer can be set for a jsonnet VM like:
import (
...
importer "github.com/peterbueschel/jsonnet-custom-importers"
)
...
m := importer.NewMultiImporter()
vm := jsonnet.MakeVM()
vm.Importer(m)
The main idea is to add a kind of intrinsic functionality into the import path string by adding extra content. The custom importers can parse these extra content and act on them.
The pattern for the import path is based on URLs
- url:
[scheme:][//[userinfo@]host][/]path[?query][#fragment]
- import pattern:
import '[<importer-prefix>://][<glob-pattern>|<filepath>][?<query-parameters>]'
Example patterns
Original go-jsonnet FileImporter
import 'example.jsonnet'
where the [<importer-prefix>://]
is empty and the <filepath>
is example.jsonnet
import 'glob.stem+://**/*.jsonnet'
where glob.stem+
is the <importer-prefix>
and the <glob-pattern>
is **/*.jsonnet
.
import 'glob.stem+://**/*.jsonnet?exclude=**/*ignore.*'
same as before but in addition with the <query-parameter>
exclude=**/*ignore.*
.
Name | <importer-prefix> in import path |
<importer-prefix> in importstr path |
<query-parameters> |
---|---|---|---|
MultiImporter |
any - will address the right importer | any | logLevel=<info|debug> , importGraph=<filepath> , onMissingFile=<filepath|content> |
GlobImporter |
glob.<?> , glob.<?>+ , glob+ |
glob-str.<?> , glob-str.<?>+ , glob-str+ |
logLevel=<info|debug> , exclude=<glob-pattern> |
- This importer includes all custom importers and as fallback the default go-jsonnet
FileImporter
. The MultiImporter tries to find the right custom importer with the help of the<importer prefix>
. If it found one, the import string will be forwarded to this custom importer, which in turn takes care of the string. - Optionally, custom importers can be chosen via:
m := NewMultiImporter(NewGlobImporter(), NewFallbackFileImporter())
- Is a custom importer, which:
- Imports multiple files at once via glob patterns handled by the doublestar library.
- Supports Continuous imports: If inside the resolved files other glob-patterns will be found, the GlobImporter will also take these glob-imports and resolves the underlying files.
- Can Exclude imports: use
exclude=<glob pattern>
as query parameter to exclude files from further handlings. - Supports extra JPaths: extra search paths for additional libraries. (
⚠️ matches of library paths have a lower priority then matches in the current work dir) - Sorts the resolved files: in lexicographical and hierarchical order. Example:
[a0 b02 a0/b/c a1 d/x c b2 a1/b b10]
becomes[a0 a0/b/c a1 a1/b b02 b10 b2 c d/x]
- Activate the glob-import via the prefix
glob.<?>
orglob.<?>+
to get the content of the resolved files as object. The content of each file will be available under its resolved path, filename, stem (filename with file extension) or dirname. (see also table in section "Prefixglob.<?>
Andglob.<?>+
") - Use the prefix
glob+
to merge the returned imports. (similar to the jsonnet+:
functionality)
(More examples can be found in the testings file.)
Folder structure:
models
├── blackbox_exporter.json
├── blackbox_exporter.libsonnet
├── node_exporter.json
├── node_exporter.libsonnet
├── development
│  └── grafana.libsonnet
├── production
│ ├── victor_ops.libsonnet
│  └── grafana.libsonnet
└── wavefront.libsonnet
-
Each resolved file, which matched the glob pattern, will be handled individually and will be available in the code under a specific variable name. The variable name can be specified in the
<?>
part. -
<?>
can be one of the following options:option example result path
/foo/bar/baa.jsonnet
file
baa.jsonnet
stem
baa
dir
/foo/bar/
-
⚠️ On collidingfile
|stem
|dir
-names, only the last resolved result in the hierarchy will be used. Use theglob.<?>+
(extra+
) prefix to merge colliding names instead. The imports will be merged in hierarchical and lexicographical order similar toglob+
. (also note:glob.path
andglob.path+
are the same)
import 'glob://models/**/*.libsonnet';
Code which will be evaluated in jsonnet:
{
'models/blackbox_exporter.libsonnet': import 'models/blackbox_exporter.libsonnet',
'models/node_exporter.libsonnet': import 'models/node_exporter.libsonnet',
'models/wavefront.libsonnet': import 'models/wavefront.libsonnet',
'models/development/grafana.libsonnet': import 'models/development/grafana.libsonnet',
'models/production/grafana.libsonnet': import 'models/production/grafana.libsonnet',
'models/production/victor_ops.libsonnet': import 'models/production/victor_ops.libsonnet',
}
import 'glob.stem://models/**/*.libsonnet'
Code which will be evaluated in jsonnet:
{
blackbox_exporter: import 'models/blackbox_exporter.libsonnet',
node_exporter: import 'models/node_exporter.libsonnet',
wavefront: import 'models/wavefront.libsonnet',
grafana: import 'models/production/grafana.libsonnet',
victor_ops: import 'models/production/victor_ops.libsonnet',
}
import 'glob.stem+://models/**/*.libsonnet'
Code which will be evaluated in jsonnet:
{
blackbox_exporter: import 'models/blackbox_exporter.libsonnet',
node_exporter: import 'models/node_exporter.libsonnet',
wavefront: import 'models/wavefront.libsonnet',
grafana: (import 'models/development/grafana.libsonnet') + (import 'models/production/grafana.libsonnet'),
victor_ops: import 'models/production/victor_ops.libsonnet',
}
These files will be merged in the hierarchical and lexicographical order.
import 'glob+://models/**/*.libsonnet'
Code which will be evaluated in jsonnet:
(import 'models/blackbox_exporter.libsonnet') +
(import 'models/node_exporter.libsonnet') +
(import 'models/wavefront.libsonnet') +
(import 'models/sub-folder-2/grafana.libsonnet')
Enable/add a zap.Logger via <Importer>.Logger()
per importer or just use the this method on the MultiImporter
instance to enable this of all underlying custom importers.
import (
...
"go.uber.org/zap"
)
...
l := zap.Must(zap.NewDevelopment()) // or use zap.NewProduction() to avoid debug messages
m := NewMultiImporter()
m.Logger(l)
...
(update since v0.0.3-alpha) Another option is to use the special config import inside an jsonnet
file:
// set the logLevel
local importers = import 'config://set?logLevel=debug';
// enable it for example via:
local myother_imports = importers + (import 'somethingElse.jsonnet');
...
Add an alias for an importer prefix.
g := NewGlobImporter()
if err := g.SetAliasPrefix("glob", "glob.stem+"); err != nil {
return err
}
m := NewMultiImporter(g)
The SetAliasPrefix()
can be used multiple times, whereby only the last setting for an alias-prefix pair will be used.
The MultiImporter
can detect import cycles
and creates an import graph in dot format once it found a cycle.
In addition such import graphs can also be enable independently via the special config import inside an jsonnet
file:
// set the import graph file name
local importers = import 'config://set?importGraph=import_graph.gv';
// enable the file creation:
local myother_imports = importers + (import 'somethingElse.jsonnet');
...
Example image from testdata/inFileConfigs/importGraph.jsonnet:
the image was created via
dot -Tsvg -O graph.gv
command (ref. graphviz cli tool)
To disable the tests and therefore any error handling for import cycles, you can use the following config in your jsonnet code.
// set the import graph file name
local importers = import 'config://set?ignoreImportCycles';
// enable the file creation:
local myother_imports = importers + (import 'somethingElse.jsonnet');
...
Or directly in your go code via:
m := NewMultiImporter(g)
m.IgnoreImportCycles()
(new in v0.0.6-alpha) (see #9)
With that option you can specify a fallback to another file or a concrete content/string, if the file in the import couldn't be found.
// set a predefined file content
local importers = import 'config://set?onMissingFile="{}"';
local myother_imports = importers + (import 'possiblyMissingFile.jsonnet');
// set a replacement file
local importers = import 'config://set?onMissingFile=default.jsonnet';
local myother_imports = importers + (import 'possiblyMissingFile.jsonnet');
m := NewMultiImporter()
m.OnMissingFile("'{}'") // single qoutes marks a content
m := NewMultiImporter()
m.OnMissingFile('default.jsonnet')
A more complex example can also be found in testdata/inFileConfigs/onMissingFile_multi.jsonnet
- https://github.com/google/go-jsonnet the reason for everything :-)
- https://github.com/bmatcuk/doublestar support for double star (
**
) glob patterns - https://github.com/uber-go/zap for structured logging
- https://github.com/dominikbraun/graph to check for import cycles and generate import graphs
- another glob–importer https://qbec.io/reference/jsonnet-glob-importer/
- importstr: add support for
importstr
- Ignore paths: add support in the GlobImporter for ignore paths
- Alias: add a prefix to a custom importer via
<Importer>.AddPrefix(string)
- HTTP support: loads single files per url
- Git support: loads files in branches from repositories