Skip to content

Commit e8eab15

Browse files
authored
Merge pull request #909 from yuvipanda/feat/new-base
Let `FROM <base_image>` in the Dockerfile template be configurable
2 parents 671c423 + e1051c3 commit e8eab15

File tree

18 files changed

+159
-62
lines changed

18 files changed

+159
-62
lines changed

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# syntax = docker/dockerfile:1.3
2-
ARG ALPINE_VERSION=3.16
2+
ARG ALPINE_VERSION=3.17
33
FROM alpine:${ALPINE_VERSION}
44

5-
RUN apk add --no-cache git python3 python3-dev py-pip build-base
5+
RUN apk add --no-cache git python3 python3-dev py3-pip py3-setuptools build-base
66

77
# build wheels in first image
88
ADD . /tmp/src
@@ -16,7 +16,7 @@ RUN mkdir /tmp/wheelhouse \
1616
FROM alpine:${ALPINE_VERSION}
1717

1818
# install python, git, bash, mercurial
19-
RUN apk add --no-cache git git-lfs python3 py-pip bash docker mercurial
19+
RUN apk add --no-cache git git-lfs python3 py3-pip py3-setuptools bash docker mercurial
2020

2121
# install hg-evolve (Mercurial extensions)
2222
RUN pip3 install hg-evolve --user --no-cache-dir

docs/source/howto/base_image.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Change the base image used by Docker
2+
3+
You may change the base image used in the `Dockerfile` that creates images by repo2docker.
4+
This is equivalent to changing the `FROM <base_image>` in the Dockerfile.
5+
6+
To do so, use the `base_image` traitlet when invoking `repo2docker`.
7+
Note that this is not configurable by individual repositories, it is configured when you invoke the `repo2docker` command.
8+
9+
```{note}
10+
By default repo2docker builds on top of the `buildpack-deps:bionic` base image, an Ubuntu-based image.
11+
```
12+
13+
## Requirements for your base image
14+
15+
`repo2docker` will only work if a specific set of packages exists in the base image.
16+
Only images that match the following criteria are supported:
17+
18+
- Ubuntu based distributions (minimum `18.04`)
19+
- Contains a set of base packages installed with [the `buildpack-deps` image family](https://hub.docker.com/_/buildpack-deps).
20+
21+
Other images _may_ work, but are not officially supported.
22+
23+
## This will affect reproducibility 🚨
24+
25+
Changing the base image may have an impact on the reproducibility of repositories that are built.
26+
There are **no guarantees that repositories will behave the same way as other repo2docker builds if you change the base image**.
27+
For example these are two scenarios that would make your repositories non-reproducible:
28+
29+
- **Your base image is different from `Ubuntu:bionic`.**
30+
If you change the base image in a way that is different from repo2docker's default (the Ubuntu `bionic` image), then repositories that **you** build with repo2docker may be significantly different from those that **other** instances of repo2docker build (e.g., those from [`mybinder.org`](https://mybinder.org)).
31+
- **Your base image changes over time.**
32+
If you choose a base image that changes its composition over time (e.g., an image provided by some other community), then it may cause repositories build with your base image to change in unpredictable ways.
33+
We recommend choosing a base image that you know to be stable and trustworthy.

docs/source/howto/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ Select from the pages listed below to get started.
1515
lab_workspaces
1616
jupyterhub_images
1717
deploy
18+
base_image

repo2docker/app.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,21 @@ def _dry_run_changed(self, change):
447447
""",
448448
)
449449

450+
base_image = Unicode(
451+
"docker.io/library/buildpack-deps:bionic",
452+
config=True,
453+
help="""
454+
Base image to use when building docker images.
455+
456+
Only images that match the following criteria are supported:
457+
- Ubuntu based distributions, minimum 18.04
458+
- Contains set of base packages installed with the buildpack-deps
459+
image family: https://hub.docker.com/_/buildpack-deps
460+
461+
Other images *may* work, but are not officially supported.
462+
""",
463+
)
464+
450465
def get_engine(self):
451466
"""Return an instance of the container engine.
452467
@@ -793,12 +808,14 @@ def build(self):
793808

794809
with chdir(checkout_path):
795810
for BP in self.buildpacks:
796-
bp = BP()
811+
bp = BP(base_image=self.base_image)
797812
if bp.detect():
798813
picked_buildpack = bp
799814
break
800815
else:
801-
picked_buildpack = self.default_buildpack()
816+
picked_buildpack = self.default_buildpack(
817+
base_image=self.base_image
818+
)
802819

803820
picked_buildpack.platform = self.platform
804821
picked_buildpack.appendix = self.appendix

repo2docker/buildpacks/_r_base.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,19 @@ def rstudio_base_scripts(r_version):
1414
shiny_proxy_version = "1.1"
1515
shiny_sha256sum = "80f1e48f6c824be7ef9c843bb7911d4981ac7e8a963e0eff823936a8b28476ee"
1616

17-
rstudio_url = "https://download2.rstudio.org/server/bionic/amd64/rstudio-server-2022.02.1-461-amd64.deb"
18-
rstudio_sha256sum = (
19-
"239e8d93e103872e7c6d827113d88871965f82ffb0397f5638025100520d8a54"
17+
# RStudio server has different builds based on wether OpenSSL 3 or 1.1 is available in the base
18+
# image. 3 is present Jammy+, 1.1 until then. Instead of hardcoding URLs based on distro, we actually
19+
# check for the dependency itself directly in the code below. You can find these URLs in
20+
# https://posit.co/download/rstudio-server/, toggling between Ubuntu 22 (for openssl3) vs earlier versions (openssl 1.1)
21+
# you may forget about openssl, but openssl never forgets you.
22+
rstudio_openssl3_url = "https://download2.rstudio.org/server/jammy/amd64/rstudio-server-2022.12.0-353-amd64.deb"
23+
rstudio_openssl3_sha256sum = (
24+
"a5aa2202786f9017a6de368a410488ea2e4fc6c739f78998977af214df0d6288"
25+
)
26+
27+
rstudio_openssl1_url = "https://download2.rstudio.org/server/bionic/amd64/rstudio-server-2022.12.0-353-amd64.deb"
28+
rstudio_openssl1_sha256sum = (
29+
"bb88e37328c304881e60d6205d7dac145525a5c2aaaf9da26f1cb625b7d47e6e"
2030
)
2131
rsession_proxy_version = "2.0.1"
2232

@@ -27,11 +37,18 @@ def rstudio_base_scripts(r_version):
2737
# but here it's important because these recommend r-base,
2838
# which will upgrade the installed version of R, undoing our pinned version
2939
rf"""
30-
curl --silent --location --fail {rstudio_url} > /tmp/rstudio.deb && \
40+
apt-get update > /dev/null && \
41+
if apt-cache search libssl3 > /dev/null; then \
42+
RSTUDIO_URL="{rstudio_openssl3_url}" ;\
43+
RSTUDIO_HASH="{rstudio_openssl3_sha256sum}" ;\
44+
else \
45+
RSTUDIO_URL="{rstudio_openssl1_url}" ;\
46+
RSTUDIO_HASH="{rstudio_openssl1_sha256sum}" ;\
47+
fi && \
48+
curl --silent --location --fail ${{RSTUDIO_URL}} > /tmp/rstudio.deb && \
3149
curl --silent --location --fail {shiny_server_url} > /tmp/shiny.deb && \
32-
echo '{rstudio_sha256sum} /tmp/rstudio.deb' | sha256sum -c - && \
50+
echo "${{RSTUDIO_HASH}} /tmp/rstudio.deb" | sha256sum -c - && \
3351
echo '{shiny_sha256sum} /tmp/shiny.deb' | sha256sum -c - && \
34-
apt-get update > /dev/null && \
3552
apt install -y --no-install-recommends /tmp/rstudio.deb /tmp/shiny.deb && \
3653
rm /tmp/*.deb && \
3754
apt-get -qq purge && \

repo2docker/buildpacks/base.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
# Only use syntax features supported by Docker 17.09
1616
TEMPLATE = r"""
17-
FROM buildpack-deps:bionic
17+
FROM {{base_image}}
1818
1919
# Avoid prompts from apt
2020
ENV DEBIAN_FRONTEND=noninteractive
@@ -211,7 +211,6 @@ class BuildPack:
211211
Specifically used for creating Dockerfiles for use with repo2docker only.
212212
213213
Things that are kept constant:
214-
- base image
215214
- some environment variables (such as locale)
216215
- user creation & ownership of home directory
217216
- working directory
@@ -221,9 +220,13 @@ class BuildPack:
221220
222221
"""
223222

224-
def __init__(self):
223+
def __init__(self, base_image):
224+
"""
225+
base_image specifies the base image to use when building docker images
226+
"""
225227
self.log = logging.getLogger("repo2docker")
226228
self.appendix = ""
229+
self.base_image = base_image
227230
self.labels = {}
228231
if sys.platform.startswith("win"):
229232
self.log.warning(
@@ -257,6 +260,8 @@ def get_base_packages(self):
257260
# Utils!
258261
"less",
259262
"unzip",
263+
# Gives us envsubst
264+
"gettext-base",
260265
}
261266

262267
@lru_cache()
@@ -535,6 +540,7 @@ def render(self, build_args=None):
535540
appendix=self.appendix,
536541
# For docker 17.09 `COPY --chown`, 19.03 would allow using $NBUSER
537542
user=build_args.get("NB_UID", DEFAULT_NB_UID),
543+
base_image=self.base_image,
538544
)
539545

540546
@staticmethod

repo2docker/buildpacks/legacy/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ class LegacyBinderDockerBuildPack:
1616
This buildpack has been deprecated.
1717
"""
1818

19+
def __init__(self, *args, **kwargs):
20+
pass
21+
1922
def detect(self):
2023
"""Check if current repo should be built with the Legacy BuildPack."""
2124
log = logging.getLogger("repo2docker")

repo2docker/buildpacks/r.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ def get_packages(self):
196196
"libapparmor1",
197197
"sudo",
198198
"lsb-release",
199+
"libssl-dev",
199200
]
200201

201202
return super().get_packages().union(packages)
@@ -216,7 +217,10 @@ def get_rspm_snapshot_url(self, snapshot_date, max_days_prior=7):
216217
# Construct a snapshot URL that will give us binary packages for Ubuntu Bionic (18.04)
217218
if "upsi" in snapshots:
218219
return (
219-
"https://packagemanager.posit.co/all/__linux__/bionic/"
220+
# Env variables here are expanded by envsubst in the Dockerfile, after sourcing
221+
# /etc/os-release. This allows us to use distro specific variables here to get
222+
# appropriate binary packages without having to hard code version names here.
223+
"https://packagemanager.posit.co/all/__linux__/${VERSION_CODENAME}/"
220224
+ snapshots["upsi"]
221225
)
222226
raise ValueError(
@@ -262,7 +266,10 @@ def get_devtools_snapshot_url(self):
262266
# Hardcoded rather than dynamically determined from a date to avoid extra API calls
263267
# Plus, we can always use packagemanager.posit.co here as we always install the
264268
# necessary apt packages.
265-
return "https://packagemanager.posit.co/all/__linux__/bionic/2022-01-04+Y3JhbiwyOjQ1MjYyMTU7NzlBRkJEMzg"
269+
# Env variables here are expanded by envsubst in the Dockerfile, after sourcing
270+
# /etc/os-release. This allows us to use distro specific variables here to get
271+
# appropriate binary packages without having to hard code version names here.
272+
return "https://packagemanager.posit.co/all/__linux__/${VERSION_CODENAME}/2022-06-03+Y3JhbiwyOjQ1MjYyMTU7RkM5ODcwN0M"
266273

267274
@lru_cache()
268275
def get_build_scripts(self):
@@ -343,16 +350,18 @@ def get_build_scripts(self):
343350
rf"""
344351
R RHOME && \
345352
mkdir -p /etc/rstudio && \
346-
echo 'options(repos = c(CRAN = "{cran_mirror_url}"))' > /opt/R/{self.r_version}/lib/R/etc/Rprofile.site && \
347-
echo 'r-cran-repos={cran_mirror_url}' > /etc/rstudio/rsession.conf
353+
EXPANDED_CRAN_MIRROR_URL="$(. /etc/os-release && echo {cran_mirror_url} | envsubst)" && \
354+
echo "options(repos = c(CRAN = \"${{EXPANDED_CRAN_MIRROR_URL}}\"))" > /opt/R/{self.r_version}/lib/R/etc/Rprofile.site && \
355+
echo "r-cran-repos=${{EXPANDED_CRAN_MIRROR_URL}}" > /etc/rstudio/rsession.conf
348356
""",
349357
),
350358
(
351359
"${NB_USER}",
352360
# Install a pinned version of devtools, IRKernel and shiny
353361
rf"""
354-
R --quiet -e "install.packages(c('devtools', 'IRkernel', 'shiny'), repos='{self.get_devtools_snapshot_url()}')" && \
355-
R --quiet -e "IRkernel::installspec(prefix='$NB_PYTHON_PREFIX')"
362+
export EXPANDED_CRAN_MIRROR_URL="$(. /etc/os-release && echo {cran_mirror_url} | envsubst)" && \
363+
R --quiet -e "install.packages(c('devtools', 'IRkernel', 'shiny'), repos=Sys.getenv(\"EXPANDED_CRAN_MIRROR_URL\"))" && \
364+
R --quiet -e "IRkernel::installspec(prefix=Sys.getenv(\"NB_PYTHON_PREFIX\"))"
356365
""",
357366
),
358367
]

repo2docker/contentproviders/git.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ def fetch(self, spec, output_dir, yield_output=False):
4545
self.log.error(
4646
f"Failed to check out ref {ref}", extra=dict(phase=R2dState.FAILED)
4747
)
48-
if ref == "master":
48+
if ref == "master" or ref == "main":
4949
msg = (
50-
"Failed to check out the 'master' branch. "
51-
"Maybe the default branch is not named 'master' "
50+
f"Failed to check out the '{ref}' branch. "
51+
f"Maybe the default branch is not named '{ref}' "
5252
"for this repository.\n\nTry not explicitly "
5353
"specifying `--ref`."
5454
)

tests/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ def run_test(args):
100100
return run_test
101101

102102

103+
@pytest.fixture()
104+
def base_image():
105+
"""
106+
Base ubuntu image to use when testing specific BuildPacks
107+
"""
108+
return "buildpack-deps:bionic"
109+
110+
103111
def _add_content_to_git(repo_dir):
104112
"""Add content to file 'test' in git repository and commit."""
105113
# use append mode so this can be called multiple times

0 commit comments

Comments
 (0)