Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions docs/rfc/0000-run-from-image-lib.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Run a module from a library image

## Summary

The `-m` flag to `jk` commands specifies a module to use as the
script; the `--lib` flag gives a library image to download and put on
the module search path.

The RFC proposes a syntax so that `-m` can do both jobs, to reduce the
amount of typing needed.

## Example

The [Kubernetes library](https://github.com/jkcfg/kubernetes) is
pushed to DockerHub in the repository `jkcfg/kubernetes`, and has a
module `@jkcfg/kubernetes/validate` which can be invoked directly to
validate YAML files. To use that module as a validation script, use:

jk validate -m @jkcfg/kubernetes:0.6.2/validate *.yaml

The `@` character denotes the start of the path; the image tag
delimits the image vs. the module elements. You must supply a tag, so
there is always a clear division between the path elements.

In the example above, they overlap (the path includes the image
name). This won't always be the case -- perhaps you are using
[GCR](https://cloud.google.com/container-registry/docs/overview)
rather than DockerHub:

jk generate -m gcr.io/@example.com/cluster-config:1.3.4/generate

In this case, the image will be
`gcr.io/example.com/cluster-config:1.3.4`, and the module
`@example.com/cluster-config/generate`.

## Motivation

Using images as libraries is very useful, since you can just mention
an image and it will be downloaded every time it is needed -- as
opposed to having to either get NPM to fetch packages for you, or copy
them into place, each time you need them.

However it is a bit painful to have to supply both the image name, and
the module name, when calling a module, since these will often
coincide. For instance, in the case of `jkcfg/kubernetes`, the module
name is `@jkcfg/kubernetes`, and the image name is
`jkcfg/kubernetes`. The full invocation is:

jk validate -m @jkcfg/kubernetes/validate --lib jkcfg/kubernetes:0.6.2 *.yaml

## Design

_Describe here the design of the change._

- _What is the user interface or API? Give examples if you can._
- _How will it work, technically?_
_ _What are the corner cases, and how are they covered?_

## Backward compatibility

_Enumerate any backward-compatibility concerns:_

- _Will people have to change their existing code?_
- _If they don't, what might happen?_

_If the change will be backward-compatible, explain why._

## Drawbacks and limitations

**Limited use cases**

This only really helps if you are invoking a module that's in the
image. It doesn't help beyond that, since you'd have to just use
`--lib` otherwise:

jk generate --lib jkcfg/kubernetes:0.6.2 ./mystuff.js

There's only one or two examples of modules that can be invoked
directly, in the @jkcfg libraries. When building your own images, of
course, you have the opportunity to arrange it for yourself.

Often you'll want more than one library image -- e.g., if using Tekton
with Kubernetes, you need both of `@jkcfg/tekton` and
`@jkcfg/kubernetes`, so now you have

jk generate --lib jkcfg/kubernetes:0.6.2 --lib jkcfg/tekton:0.1.1 ./mystuff.js

**Image repositories vs modules**

The syntax proposed works best if your image names line up with your
module names. If you want to publish your modules elsewhere though,
you have to own the repository in both places; e.g., both example.com
in DockerHub, and @example.com in npmjs.org.

_What are the drawbacks of adopting this proposal; e.g.,_

- _Does it require more engineering work from users (for an
equivalent result)?_
- _Are there performance concerns?_
- _Will it close off other possibilities?_
- _Does it add significant complexity to the runtime or standard library?_
- _Does it make understanding `jk` harder?_

## Alternatives

**Leave it to shell expansion**

E.g.,

jk generate --libs jkcfg/{kubernetes:0.6.1,tekton:0.1.1} -- ./mystuff.js

This is only a little better, and only occasionally useful. It doesn't
help with the original motivation -- the module would still have to be
spelt out.

**Use a URI-style syntax**

E.g.,

jk validate oci:@jkcfg/kubernetes:0.6.2/validate

**Use a spec file and lock file**

Many programming languages have closely associated, sometimes
integrated, package management. For example, Ruby has
[bundler](https://bundler.io/), Node.JS has
[NPM](https://npmjs.org/). All of these systems end up using a
specification of the dependencies (e.g., `package.json`), then a lock
file (`package-lock.json`) to record exact versions.

Instead of making it easier to specify a single dependency, all of
them could be put in the specification file (and lock file).

The downside is that this needs a bit of invention.

**Invent a `go mod`-like system instead**

Go goes a bit further than e.g., NPM, and analyses source code for
dependencies, before fetching them. It can do this because it has
defined a mechanism to resolve import paths to VCS (git, etc.)
repositories.

In principle, we can do the same for `jk` import paths;
e.g., to treat

```javascript
import { valuesForGenerate } 'index.docker.io/@jkcfg/kubernetes';
```

.. as referring to the image `index.docker.io/jkcfg/kubernetes` and
the path `@jkcfg/kubernetes` within it. The `@` is still useful for
delimiting the module path, and for compatibility with NPM (e.g., if
you publish the same package to NPM).

**Use import paths to specify images**

In brief: use the syntax proposed, but in import paths, e.g.,

```js
import { validate } from '@jkcfg/kubernetes:0.6.1/validate';
```

This is not quite an _alternative_ to the proposal, since you could
have both. The big downside is that you have to go through and update
all the versions -- and what happens if you miss one?

_Explain other designs or formulations that were considered (including
doing nothing, if not already covered above), and why the proposed
design is superior._

## Unresolved questions

_Keep track here of questions that come up while this is a draft.
Ideally, there will be nothing unresolved by the time the RFC is
accepted. It is OK to resolve a question by explaining why it
does not need to be answered_ yet _._