gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/images.mk (about)

     1  #!/usr/bin/make -f
     2  
     3  # Copyright 2018 The gVisor Authors.
     4  #
     5  # Licensed under the Apache License, Version 2.0 (the "License");
     6  # you may not use this file except in compliance with the License.
     7  # You may obtain a copy of the License at
     8  #
     9  #   http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  # Unless required by applicable law or agreed to in writing, software
    12  # distributed under the License is distributed on an "AS IS" BASIS,
    13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  # See the License for the specific language governing permissions and
    15  # limitations under the License.
    16  
    17  ##
    18  ## Docker image targets.
    19  ##
    20  ##   Images used by the tests must also be built and available locally.
    21  ##   The canonical test targets defined below will automatically load
    22  ##   relevant images. These can be loaded or built manually via these
    23  ##   targets.
    24  ##
    25  ##   (*) Note that you may provide an ARCH parameter in order to build
    26  ##   and load images from an alternate archiecture (using qemu). When
    27  ##   bazel is run as a server, this has the effect of running an full
    28  ##   cross-architecture chain, and can produce cross-compiled binaries.
    29  ##
    30  
    31  # ARCH is the architecture used for the build. This may be overriden at the
    32  # command line in order to perform a cross-build (in a limited capacity).
    33  ARCH := $(shell uname -m)
    34  ifneq ($(ARCH),$(shell uname -m))
    35  DOCKER_PLATFORM_ARGS := --platform=$(ARCH)
    36  else
    37  DOCKER_PLATFORM_ARGS :=
    38  endif
    39  
    40  # Note that the image prefixes used here must match the image mangling in
    41  # runsc/testutil.MangleImage. Names are mangled in this way to ensure that all
    42  # tests are using locally-defined images (that are consistent and idempotent).
    43  REMOTE_IMAGE_PREFIX ?= us-central1-docker.pkg.dev/gvisor-presubmit/gvisor-presubmit-images
    44  LOCAL_IMAGE_PREFIX  ?= gvisor.dev/images
    45  ALL_IMAGES          := $(subst /,_,$(subst images/,,$(shell find images/ -name Dockerfile -o -name Dockerfile.$(ARCH) | xargs -n 1 dirname | uniq)))
    46  NON_TEST_IMAGES     := gpu/ollama/bench
    47  TEST_IMAGES         := $(subst /,_,$(subst images/,,$(shell find images/ -name Dockerfile -o -name Dockerfile.$(ARCH) | xargs -n 1 dirname | uniq | grep -v $(NON_TEST_IMAGES))))
    48  SUB_IMAGES          := $(foreach image,$(ALL_IMAGES),$(if $(findstring _,$(image)),$(image),))
    49  IMAGE_GROUPS        := $(sort $(foreach image,$(SUB_IMAGES),$(firstword $(subst _, ,$(image)))))
    50  
    51  define expand_group =
    52  load-$(1): $$(patsubst $(1)_%, load-$(1)_%, $$(filter $(1)_%,$$(ALL_IMAGES)))
    53  	@
    54  .PHONY: load-$(1)
    55  push-$(1): $$(patsubst $(1)_%, push-$(1)_%, $$(filter $(1)_%,$$(ALL_IMAGES)))
    56  	@
    57  .PHONY: push-$(1)
    58  endef
    59  $(foreach group,$(IMAGE_GROUPS),$(eval $(call expand_group,$(group))))
    60  
    61  list-all-images: ## List all images.
    62  	@for image in $(ALL_IMAGES); do echo $${image}; done
    63  .PHONY: list-all-images
    64  
    65  list-all-test-images: ## List all test images.
    66  	@for image in $(TEST_IMAGES); do echo $${image}; done
    67  .PHONY: list-all-test-images
    68  
    69  load-all-images: ## Load all images.
    70  load-all-images: $(patsubst %,load-%,$(ALL_IMAGES))
    71  .PHONY: load-all-images
    72  
    73  load-all-test-images: ## Load all test images.
    74  load-all-test-images: $(patsubst %,load-%,$(TEST_IMAGES))
    75  .PHONY: load-all-test-images
    76  
    77  test-all-test-images: ## Test all test images.
    78  test-all-test-images: $(patsubst %,test-%,$(TEST_IMAGES))
    79  .PHONY: test-all-test-images
    80  
    81  push-all-images: ## Push all images.
    82  push-all-images: $(patsubst %,push-%,$(ALL_IMAGES))
    83  .PHONY: push-all-images
    84  
    85  push-all-test-images: ## Push all images.
    86  push-all-test-images: $(patsubst %,push-%,$(TEST_IMAGES))
    87  .PHONY: push-all-test-images
    88  
    89  # path and dockerfile are used to extract the relevant path and dockerfile
    90  # (depending on what's available for the given architecture).
    91  path = images/$(subst _,/,$(1))
    92  dockerfile = $$(if [ -f "$(call path,$(1))/Dockerfile.$(ARCH)" ]; then echo Dockerfile.$(ARCH); else echo Dockerfile; fi)
    93  
    94  # The tag construct is used to memoize the image generated (see README.md).
    95  # This scheme is used to enable aggressive caching in a central repository, but
    96  # ensuring that images will always be sourced using the local files.
    97  tag = $(shell cd images && find $(subst _,/,$(1)) -type f | sort -f -d | xargs -n 1 sha256sum | sha256sum - | cut -c 1-16)
    98  remote_image = $(REMOTE_IMAGE_PREFIX)/$(subst _,/,$(1))_$(ARCH)
    99  local_image = $(LOCAL_IMAGE_PREFIX)/$(subst _,/,$(1))
   100  
   101  # Include all existing images as targets here.
   102  #
   103  # Note that we use a _ for the tag separator, instead of :, as the latter is
   104  # interpreted by Make, unfortunately. tag_expand expands the generic rules to
   105  # tag-specific targets. These is needed to provide sensible targets for load
   106  # below, with caching. Basically, if there is a rule generated here, then the
   107  # load will be skipped. If there is no load generated here, then the default
   108  # rule for load will kick in.
   109  #
   110  # Note that if this rule does not successfully rule, we will simply have
   111  # additional Docker pull commands that run for all images that are already
   112  # pulled. No real harm done.
   113  EXISTING_IMAGES = $(shell docker images --format '{{.Repository}}_{{.Tag}}' | grep -v '<none>')
   114  define existing_image_rule =
   115  loaded0_$(1)=load-$$(1): tag-$$(1) # Already available.
   116  loaded1_$(1)=.PHONY: load-$$(1)
   117  endef
   118  $(foreach image, $(EXISTING_IMAGES), $(eval $(call existing_image_rule,$(image))))
   119  define tag_expand_rule =
   120  $(eval $(loaded0_$(call remote_image,$(1))_$(call tag,$(1))))
   121  $(eval $(loaded1_$(call remote_image,$(1))_$(call tag,$(1))))
   122  endef
   123  $(foreach image, $(ALL_IMAGES), $(eval $(call tag_expand_rule,$(image))))
   124  
   125  # tag tags a local image. This applies both the hash-based tag from above to
   126  # ensure that caching works as expected, as well as the "latest" tag that is
   127  # used by the tests.
   128  local_tag = \
   129    docker tag $(call remote_image,$(1)):$(call tag,$(1)) $(call local_image,$(1)):$(call tag,$(1)) >&2
   130  latest_tag = \
   131    docker tag $(call local_image,$(1)):$(call tag,$(1)) $(call local_image,$(1)) >&2
   132  tag-%: ## Tag a local image.
   133  	@$(call header,TAG $*)
   134  	@$(call local_tag,$*) && $(call latest_tag,$*)
   135  
   136  image_manifest = \
   137  	docker run --rm gcr.io/go-containerregistry/crane manifest $(call remote_image,$(1)):$(call tag,$(1))
   138  
   139  # pull forces the image to be pulled.
   140  pull = \
   141    $(call header,PULL $(1)) && \
   142    docker pull $(DOCKER_PLATFORM_ARGS) $(call remote_image,$(1)):$(call tag,$(1)) >&2 && \
   143    $(call local_tag,$(1)) && \
   144    $(call latest_tag,$(1))
   145  pull-%: register-cross ## Force a repull of the image.
   146  	@$(call pull,$*)
   147  
   148  # rebuild builds the image locally. Only the "remote" tag will be applied. Note
   149  # we need to explicitly repull the base layer in order to ensure that the
   150  # architecture is correct. Note that we use the term "rebuild" here to avoid
   151  # conflicting with the bazel "build" terminology, which is used elsewhere.
   152  rebuild = \
   153    $(call header,REBUILD $(1)) && \
   154    (T=$$(mktemp -d) && cp -a $(call path,$(1))/* $$T && \
   155    $(foreach image,$(shell grep FROM "$(call path,$(1))/$(call dockerfile,$(1))" 2>/dev/null | cut -d' ' -f2),docker pull $(DOCKER_PLATFORM_ARGS) $(image) >&2 &&) \
   156    docker build $(DOCKER_PLATFORM_ARGS) \
   157      -f "$$T/$(call dockerfile,$(1))" \
   158      -t "$(call remote_image,$(1)):$(call tag,$(1))" \
   159      -t "$(call remote_image,$(1))":latest \
   160      $$T >&2 && \
   161    rm -rf $$T) && \
   162    $(call local_tag,$(1)) && \
   163    $(call latest_tag,$(1))
   164  rebuild-%: register-cross ## Force rebuild an image locally.
   165  	@$(call rebuild,$*)
   166  
   167  # load will either pull the "remote" or build it locally. This is the preferred
   168  # entrypoint, as it should never fail. The local tag should always be set after
   169  # this returns (either by the pull or the build).
   170  # If the image is not available for the current architecture, it is not loaded.
   171  load-%: register-cross ## Pull or build an image locally.
   172  	@if [ -f "$(call path,$*)/$(call dockerfile,$*)" ]; then \
   173  	  ($(call pull,$*)) || ($(call rebuild,$*)); \
   174  	else \
   175  	  echo "Image $* is not available on $$(uname -m), ignoring it." >&2; \
   176  	fi
   177  
   178  test-%: register-cross ## Build an image locally if the remote doesn't exist.
   179  	@($(call image_manifest,$*)) >&2 || ($(call rebuild,$*))
   180  
   181  # push pushes the remote image, after validating that the tag doesn't exist
   182  # yet. Note that this generic rule will match the fully-expanded remote image
   183  # tag.
   184  push-%:
   185  	$(call image_manifest,$*) >&2 || \
   186  	( $(call rebuild,$*) && docker image push $(call remote_image,$*):$(call tag,$*) >&2 )
   187  
   188  # register-cross registers the necessary qemu binaries for cross-compilation.
   189  # This may be used by any target that may execute containers that are not the
   190  # native format. Note that this will only apply on the first execution.
   191  register-cross:
   192  ifneq ($(ARCH),$(shell uname -m))
   193  ifeq (,$(wildcard /proc/sys/fs/binfmt_misc/qemu-*))
   194  	@docker run --rm --privileged multiarch/qemu-user-static --reset --persistent yes >&2
   195  else
   196  	@
   197  endif
   198  else
   199  	@
   200  endif