func()

in agent/engine/docker_task_engine.go [1273:1427]


func (engine *DockerTaskEngine) pullContainerManifest(
	task *apitask.Task, container *apicontainer.Container,
) dockerapi.DockerContainerMetadata {
	if container.IsInternal() {
		logger.Info("Digest resolution not required", logger.Fields{
			field.TaskARN:       task.Arn,
			field.ContainerName: container.Name,
			field.Image:         container.Image,
		})
		return dockerapi.DockerContainerMetadata{}
	}

	// AppNet Agent container image is managed at start up so it is not in-scope for digest resolution.
	// (it uses the same image as AppNet Relay container)
	if task.IsServiceConnectEnabled() && container == task.GetServiceConnectContainer() {
		return dockerapi.DockerContainerMetadata{}
	}

	imageManifestDigest := referenceutil.GetDigestFromImageRef(container.Image)
	// Checks if the container's image requires manifest digest resolution.
	// Manifest digest resolution is required if the container's image reference does not
	// have a digest.
	if imageManifestDigest == "" {
		if !engine.imagePullRequired(engine.cfg.ImagePullBehavior, container, task.GetID()) {
			// Image pull is not required for the container so we will use a locally available
			// image for the container. Get digest from a locally available image.
			imgInspect, err := engine.client.InspectImage(container.Image)
			if err != nil {
				logger.Error("Failed to inspect image to find repo digest", logger.Fields{
					field.TaskARN:       task.Arn,
					field.ContainerName: container.Name,
					field.Image:         container.Image,
				})
				return dockerapi.DockerContainerMetadata{
					Error: dockerapi.CannotPullImageManifestError{FromError: err},
				}
			}
			if len(imgInspect.RepoDigests) == 0 {
				// Image was not pulled from a registry, so the user must have cached it on the
				// host directly. Skip digest resolution for this case as there is no digest.
				logger.Info("No repo digest found", logger.Fields{
					field.TaskARN:       task.Arn,
					field.ContainerName: container.Name,
					field.Image:         container.Image,
				})
				return dockerapi.DockerContainerMetadata{}
			}
			parsedDigest, err := referenceutil.GetDigestFromRepoDigests(
				imgInspect.RepoDigests, container.Image)
			if err != nil {
				logger.Error("Failed to find a repo digest matching the image", logger.Fields{
					field.TaskARN:       task.Arn,
					field.ContainerName: container.Name,
					field.Image:         container.Image,
					"repoDigests":       imgInspect.RepoDigests,
				})
				return dockerapi.DockerContainerMetadata{
					Error: dockerapi.CannotPullImageManifestError{
						FromError: fmt.Errorf("failed to find a repo digest matching '%s'", container.Image),
					},
				}
			}

			imageManifestDigest = parsedDigest
			logger.Info("Fetched image manifest digest for container from local image inspect", logger.Fields{
				field.TaskARN:       task.Arn,
				field.ContainerName: container.Name,
				field.ImageDigest:   imageManifestDigest.String(),
				field.Image:         container.Image,
			})
		} else {
			// Digest should be resolved by calling the registry

			// Technically, API version 1.30 is enough to call Distribution API to fetch image manifests
			// from registries. However, Docker engine versions between 17.06 (API version 1.30) and
			// 17.12 (API version 1.35) support image pulls from v1 registries
			// (using `disable-legacy-registry` option) whereas Distribution API does not work against
			// v1 registries. So, to be safe, we will only attempt digest resolution if Docker engine
			// version is >= 17.12 (API version 1.35).
			supportedAPIVersion := dockerclient.GetSupportedDockerAPIVersion(dockerclient.Version_1_35)
			client, err := engine.client.WithVersion(supportedAPIVersion)
			if err != nil {
				logger.Warn(
					"Failed to find a supported API version that supports manifest pulls. Skipping digest resolution.",
					logger.Fields{
						field.TaskARN:        task.Arn,
						field.ContainerName:  container.Name,
						"requiredAPIVersion": supportedAPIVersion,
						field.Error:          err,
					})
				return dockerapi.DockerContainerMetadata{}
			}

			// Set registry auth credentials if required and clear them when no longer needed
			clearCreds, authError := engine.setRegistryCredentials(container, task)
			if authError != nil {
				logger.Error("Failed to set registry auth credentials", logger.Fields{
					field.TaskARN:       task.Arn,
					field.ContainerName: container.Name,
					field.Error:         authError,
				})
				return dockerapi.DockerContainerMetadata{Error: authError}
			}
			if clearCreds != nil {
				defer clearCreds()
			}

			ctx, cancel := context.WithTimeout(engine.ctx, engine.cfg.ManifestPullTimeout)
			defer cancel()
			distInspect, manifestPullErr := client.PullImageManifest(
				ctx, container.Image, container.RegistryAuthentication)
			if manifestPullErr != nil {
				logger.Error("Failed to fetch image manifest from registry", logger.Fields{
					field.TaskARN:       task.Arn,
					field.ContainerName: container.Name,
					field.Image:         container.Image,
					field.Error:         manifestPullErr,
				})
				return dockerapi.DockerContainerMetadata{Error: manifestPullErr}
			}
			imageManifestMediaType := distInspect.Descriptor.MediaType
			logger.Info("Fetched image manifest MediaType for container from registry", logger.Fields{
				field.TaskARN:        task.Arn,
				field.ContainerName:  container.Name,
				field.ImageMediaType: imageManifestMediaType,
				field.Image:          container.Image,
			})
			if imageManifestMediaType == mediaTypeManifestV1 || imageManifestMediaType == mediaTypeSignedManifestV1 {
				logger.Info("skipping digest resolution for manifest v2 schema 1", logger.Fields{
					field.TaskARN:        task.Arn,
					field.ContainerName:  container.Name,
					field.ImageMediaType: imageManifestMediaType,
					field.Image:          container.Image,
				})
				return dockerapi.DockerContainerMetadata{}
			}
			imageManifestDigest = distInspect.Descriptor.Digest
			logger.Info("Fetched image manifest digest for container from registry", logger.Fields{
				field.TaskARN:       task.Arn,
				field.ContainerName: container.Name,
				field.ImageDigest:   imageManifestDigest.String(),
				field.Image:         container.Image,
			})
		}

	}

	logger.Debug("Setting image digest on container", logger.Fields{
		field.TaskARN:       task.Arn,
		field.ContainerName: container.Name,
		field.ImageDigest:   imageManifestDigest.String(),
	})
	container.SetImageDigest(imageManifestDigest.String())
	return dockerapi.DockerContainerMetadata{}
}