Skip to content

Commit

Permalink
This adds a simple / functional nodejs_image rule.
Browse files Browse the repository at this point in the history
For now these images are based on gcr.io/google-appengine/base instead of gcr.io/distroless/nodejs for two reasons:
1. The nodejs_binary runfiles includes the node binary, so we need not use a Node.JS base runtime.
1. The generated nodejs_binary entrypoint is currently a bash script, so if/until we can move this logic into our own entrypoint we need a more substantial base runtime.
  • Loading branch information
mattmoor committed Dec 5, 2017
1 parent caa55f1 commit 1a1debf
Show file tree
Hide file tree
Showing 20 changed files with 371 additions and 29 deletions.
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ https://github.com/bazelbuild/rules_go#go_binary))
https://docs.bazel.build/versions/master/be/python.html#py_binary))
* [py3_image](#py3_image) ([signature](
https://docs.bazel.build/versions/master/be/python.html#py_binary))
* [nodejs_image](#nodejs_image) ([usage](
https://github.com/bazelbuild/rules_nodejs#usage))
* [java_image](#java_image) ([signature](
https://docs.bazel.build/versions/master/be/java.html#java_binary))
* [war_image](#war_image) ([signature](
Expand Down Expand Up @@ -342,6 +344,58 @@ py_image(
To use a Python 3 runtime instead of the default of Python 2, use `py3_image`,
instead of `py_image`. The other semantics are identical.

### nodejs_image

**It is notable that unlike the other image rules, `nodejs_image` is not
currently using the `gcr.io/distroless/nodejs` image for a handful of reasons.**
This is a switch we plan to make, when we can manage it. We are currently
utilizing the `gcr.io/google-appengine/base` image as our base.

To use `nodejs_image`, add the following to `WORKSPACE`:

```python
git_repository(
name = "build_bazel_rules_nodejs",
# Replace with a real commit SHA
commit = "{HEAD}",
remote = "https://github.com/bazelbuild/rules_nodejs.git",
)

load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories", "npm_install")

# Download Node toolchain, etc.
node_repositories(package_json = ["//:package.json"])

# Install your declared Node.js dependencies
npm_install(
name = "npm_deps",
packages = "//:package.json",
)

# Download base images, etc.
load(
"//nodejs:image.bzl",
_nodejs_image_repos = "repositories",
)

_nodejs_image_repos()
```

Then in your `BUILD` file, simply rewrite `nodejs_binary` to `nodejs_image` with
the following import:
```python
load("@io_bazel_rules_docker//nodejs:image.bzl", "nodejs_image")

nodejs_image(
name = "nodejs_image",
entry_point = "your_workspace/path/to/file.js",
# This will be put into its own layer.
node_modules = "@npm_deps//:node_modules",
data = [":file.js"],
...
)
```

### go_image

To use `go_image`, add the following to `WORKSPACE`:
Expand Down
22 changes: 22 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,25 @@ git_repository(
load("@io_bazel_rules_d//d:d.bzl", "d_repositories")

d_repositories()

git_repository(
name = "build_bazel_rules_nodejs",
commit = "6b498e36095350bd88d2ea89e1a87ce791e51d82",
remote = "https://github.com/bazelbuild/rules_nodejs.git",
)

load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories", "npm_install")

node_repositories(package_json = ["//testdata:package.json"])

npm_install(
name = "npm_deps",
package_json = "//testdata:package.json",
)

load(
"//nodejs:image.bzl",
_nodejs_image_repos = "repositories",
)

_nodejs_image_repos()
6 changes: 3 additions & 3 deletions cc/cc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
# git repository.

DIGESTS = {
# "gcr.io/distroless/cc:debug" circa 2017-11-17 12:25 -0500
"debug": "sha256:a9dde7b572769587072b2af3956ed2a073223e865aab15290d3585787e613e1a",
# "gcr.io/distroless/cc:latest" circa 2017-11-17 12:25 -0500
# "gcr.io/distroless/cc:debug" circa 2017-12-03 22:43 +0000
"debug": "sha256:d3fe07be221200e6a7c9981545da4394cd6177ab80012da65a858069bd374bb9",
# "gcr.io/distroless/cc:latest" circa 2017-12-03 22:43 +0000
"latest": "sha256:7a52af4e4f09c905f2264c99ec75f65481fd132454f3ff4dd06962c99c7dab6e",
}
4 changes: 4 additions & 0 deletions container/image_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ def test_py_image(self):
'/app/testdata/py_image.binary.runfiles/io_bazel_rules_docker/testdata/py_image_library.py',
'/app/testdata/py_image.binary.runfiles/io_bazel_rules_docker/testdata/__init__.py',
'/app/testdata/py_image.binary',
'/app/testdata/py_image.binary.runfiles/io_bazel_rules_docker/external',
])

# Check the library layer, which is one below our application layer.
Expand Down Expand Up @@ -341,6 +342,9 @@ def test_cc_image(self):
'/app',
'/app/testdata',
'/app/testdata/cc_image.binary',
'/app/testdata/cc_image.binary.runfiles',
'/app/testdata/cc_image.binary.runfiles/io_bazel_rules_docker',
'/app/testdata/cc_image.binary.runfiles/io_bazel_rules_docker/external',
])

# The linker pulls the object files into the final binary,
Expand Down
6 changes: 3 additions & 3 deletions go/go.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
# git repository.

DIGESTS = {
# "gcr.io/distroless/base:debug" circa 2017-11-17 12:25 -0500
"debug": "sha256:f4fcf7c98bb83dd79deafd590ea0c774aebe386d4486a926f05078689a48c2b7",
# "gcr.io/distroless/base:latest" circa 2017-11-17 12:25 -0500
# "gcr.io/distroless/base:debug" circa 2017-12-03 22:43 +0000
"debug": "sha256:2a4edff9a50ea5c0aa40cf3c99206b9e8efe6ca131f774f2c279e22762e90792",
# "gcr.io/distroless/base:latest" circa 2017-12-03 22:43 +0000
"latest": "sha256:bef8d030c7f36dfb73a8c76137616faeea73ac5a8495d535f27c911d0db77af3",
}
24 changes: 24 additions & 0 deletions java/image.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ jar_dep_layer = rule(
# The dependency whose runfiles we're appending.
"dep": attr.label(mandatory = True),

# Whether to lay out each dependency in a manner that is agnostic
# of the binary in which it is participating. This can increase
# sharing of the dependency's layer across images, but requires a
# symlink forest in the app layers.
"agnostic_dep_layout": attr.bool(default = True),

# Override the defaults.
"directory": attr.string(default = "/app"),
# https://github.com/bazelbuild/bazel/issues/2176
Expand Down Expand Up @@ -192,6 +198,12 @@ jar_app_layer = rule(
# The main class to invoke on startup.
"main_class": attr.string(mandatory = True),

# Whether to lay out each dependency in a manner that is agnostic
# of the binary in which it is participating. This can increase
# sharing of the dependency's layer across images, but requires a
# symlink forest in the app layers.
"agnostic_dep_layout": attr.bool(default = True),

# Whether the classpath should be passed as a file.
"_classpath_as_file": attr.bool(default = False),

Expand Down Expand Up @@ -256,6 +268,12 @@ _war_dep_layer = rule(
# The dependency whose runfiles we're appending.
"dep": attr.label(mandatory = True),

# Whether to lay out each dependency in a manner that is agnostic
# of the binary in which it is participating. This can increase
# sharing of the dependency's layer across images, but requires a
# symlink forest in the app layers.
"agnostic_dep_layout": attr.bool(default = True),

# Override the defaults.
"directory": attr.string(default = "/jetty/webapps/ROOT/WEB-INF/lib"),
# WE WANT PATHS FLATTENED
Expand Down Expand Up @@ -296,6 +314,12 @@ _war_app_layer = rule(
"base": attr.label(mandatory = True),
"entrypoint": attr.string_list(default = []),

# Whether to lay out each dependency in a manner that is agnostic
# of the binary in which it is participating. This can increase
# sharing of the dependency's layer across images, but requires a
# symlink forest in the app layers.
"agnostic_dep_layout": attr.bool(default = True),

# Override the defaults.
"directory": attr.string(default = "/jetty/webapps/ROOT/WEB-INF/lib"),
# WE WANT PATHS FLATTENED
Expand Down
8 changes: 4 additions & 4 deletions java/java.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
# git repository.

DIGESTS = {
# "gcr.io/distroless/java:debug" circa 2017-11-17 12:25 -0500
"debug": "sha256:51f368384b96711a2b541dd74f79cbe26b897f5ee5854194e719d9682a0fc1ab",
# "gcr.io/distroless/java:latest" circa 2017-11-17 12:25 -0500
"latest": "sha256:0965d0dfe2ef58f063d0818fd595aa7eb973fa59cd410c6ed935503b38ff920d",
# "gcr.io/distroless/java:debug" circa 2017-12-03 22:43 +0000
"debug": "sha256:1294c859736606ad3984f0a2676f69b1eddb2a84c4f44a53c71538c26cb45182",
# "gcr.io/distroless/java:latest" circa 2017-12-03 22:43 +0000
"latest": "sha256:676cc129925e071bcb093d777c3a47ecdcd9cba9371e811a2674f3bbafec7171",
}
8 changes: 4 additions & 4 deletions java/jetty.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
# git repository.

DIGESTS = {
# "gcr.io/distroless/java/jetty:debug" circa 2017-11-17 12:25 -0500
"debug": "sha256:0b765ee8e9c8b2b3fca8588c06f3d3ef5bf669140b3cb8095628ae4e54682002",
# "gcr.io/distroless/java/jetty:latest" circa 2017-11-17 12:25 -0500
"latest": "sha256:7bceb33e33f3954ea0b57fa46d8777ff297d5bb445c47adf0d60b0ad1d26ada6",
# "gcr.io/distroless/java/jetty:debug" circa 2017-12-03 22:43 +0000
"debug": "sha256:20238f6ad4f1d6005b3358996d5d45f7cf710e30aee9b86794c9237ee81dbb7b",
# "gcr.io/distroless/java/jetty:latest" circa 2017-12-03 22:43 +0000
"latest": "sha256:6db64cf9d24b5c16156d9921890c7905ae01c9e0e32509a6392af4600caaffaa",
}
46 changes: 40 additions & 6 deletions lang/image.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ def _reference_dir(ctx):
# /app/bar/baz/blah.runfiles/foo
return "/".join([_runfiles_dir(ctx), ctx.workspace_name])

# The special "external" directory which is an alternate way of accessing
# other repositories.
def _external_dir(ctx):
# For @foo//bar/baz:blah this would translate to
# /app/bar/baz/blah.runfiles/foo/external
return "/".join([_reference_dir(ctx), "external"])

# The final location that this file needs to exist for the foo_binary target to
# properly execute.
def _final_emptyfile_path(ctx, name):
Expand Down Expand Up @@ -95,6 +102,9 @@ def dep_layer_impl(ctx, runfiles=None, emptyfiles=None):
runfiles = runfiles or _default_runfiles
emptyfiles = emptyfiles or _default_emptyfiles

filepath = layer_file_path if ctx.attr.agnostic_dep_layout else _final_file_path
emptyfilepath = _layer_emptyfile_path if ctx.attr.agnostic_dep_layout else _final_emptyfile_path

return _container.image.implementation(
ctx,
# We use all absolute paths.
Expand All @@ -105,11 +115,11 @@ def dep_layer_impl(ctx, runfiles=None, emptyfiles=None):
# This references the binary package because the file paths are
# relative to it, and normalized by the tarball package.
file_map={
layer_file_path(ctx, f): f
filepath(ctx, f): f
for f in runfiles(ctx.attr.dep)
},
empty_files=[
_layer_emptyfile_path(ctx, empty)
emptyfilepath(ctx, empty)
for empty in emptyfiles(ctx.attr.dep)
]
)
Expand All @@ -119,7 +129,16 @@ dep_layer = rule(
# The base image on which to overlay the dependency layers.
"base": attr.label(mandatory = True),
# The dependency whose runfiles we're appending.
"dep": attr.label(mandatory = True),
"dep": attr.label(mandatory = True, allow_files = True),

# Whether to lay out each dependency in a manner that is agnostic
# of the binary in which it is participating. This can increase
# sharing of the dependency's layer across images, but requires a
# symlink forest in the app layers.
"agnostic_dep_layout": attr.bool(default = True),
# The binary target for which we are synthesizing an image.
# This is needed iff agnostic_dep_layout.
"binary": attr.label(mandatory = False),

# Override the defaults.
# https://github.com/bazelbuild/bazel/issues/2176
Expand Down Expand Up @@ -171,11 +190,20 @@ def _app_layer_impl(ctx, runfiles=None, emptyfiles=None):
# For each of the runfiles we aren't including directly into
# the application layer, link to their binary-agnostic
# location from the runfiles path.
symlinks = available + {
symlinks = {}
# Include symlinks to available files if they were laid out in a
# binary-agnostic fashion.
if ctx.attr.agnostic_dep_layout:
symlinks = available

symlinks += {
# Create a symlink from our entrypoint to where it will actually be put
# under runfiles.
_binary_name(ctx): "/".join([_reference_dir(ctx), ctx.label.package,
ctx.attr.binary.label.name])
ctx.attr.binary.label.name]),
# Create a directory symlink from <workspace>/external to the runfiles
# root, since they may be accessed via either path.
_external_dir(ctx): _runfiles_dir(ctx),
}

return _container.image.implementation(
Expand All @@ -195,11 +223,17 @@ app_layer = rule(
"binary": attr.label(mandatory = True),
# The full list of dependencies that have their own layers
# factored into our base.
"layers": attr.label_list(),
"layers": attr.label_list(allow_files = True),
# The base image on which to overlay the dependency layers.
"base": attr.label(mandatory = True),
"entrypoint": attr.string_list(default = []),

# Whether each dependency is laid out in a manner that is agnostic
# of the binary in which it is participating. This can increase
# sharing of the dependency's layer across images, but requires a
# symlink forest in the app layers.
"agnostic_dep_layout": attr.bool(default = True),

# Override the defaults.
"data_path": attr.string(default = "."),
"workdir": attr.string(default = "/app"),
Expand Down
16 changes: 16 additions & 0 deletions nodejs/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2017 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
package(default_visibility = ["//visibility:public"])

licenses(["notice"]) # Apache 2.0
Loading

0 comments on commit 1a1debf

Please sign in to comment.