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