mirror of
https://github.com/containers/podman.git
synced 2025-06-22 18:08:11 +08:00
Autoupdate local label functional
Digests were used to compare local image and container image Registry alias added for Image Policy Refactored to integrate new feature + change some naming conventions Tested this using a modified version of the docs autoupdate instructions & it worked successfully Signed-off-by: Parker Van Roy <pvanroy@redhat.com>
This commit is contained in:
1
go.sum
1
go.sum
@ -209,6 +209,7 @@ github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQ
|
|||||||
github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4=
|
github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4=
|
||||||
github.com/containers/ocicrypt v1.1.1 h1:prL8l9w3ntVqXvNH1CiNn5ENjcCnr38JqpSyvKKB4GI=
|
github.com/containers/ocicrypt v1.1.1 h1:prL8l9w3ntVqXvNH1CiNn5ENjcCnr38JqpSyvKKB4GI=
|
||||||
github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
|
github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
|
||||||
|
github.com/containers/podman v1.9.3 h1:GLQceCWhFkeTTYSU2qrpS3rsNYrrsilkUMF9LwNp4lg=
|
||||||
github.com/containers/psgo v1.5.2 h1:3aoozst/GIwsrr/5jnFy3FrJay98uujPCu9lTuSZ/Cw=
|
github.com/containers/psgo v1.5.2 h1:3aoozst/GIwsrr/5jnFy3FrJay98uujPCu9lTuSZ/Cw=
|
||||||
github.com/containers/psgo v1.5.2/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU=
|
github.com/containers/psgo v1.5.2/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU=
|
||||||
github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM=
|
github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM=
|
||||||
|
@ -33,15 +33,25 @@ type Policy string
|
|||||||
const (
|
const (
|
||||||
// PolicyDefault is the default policy denoting no auto updates.
|
// PolicyDefault is the default policy denoting no auto updates.
|
||||||
PolicyDefault Policy = "disabled"
|
PolicyDefault Policy = "disabled"
|
||||||
// PolicyNewImage is the policy to update as soon as there's a new image found.
|
// PolicyRegistryImage is the policy to update as soon as there's a new image found.
|
||||||
PolicyNewImage = "image"
|
PolicyRegistryImage = "image"
|
||||||
|
// PolicyLocalImage is the policy to run auto-update based on a local image
|
||||||
|
PolicyLocalImage = "local"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Map for easy lookups of supported policies.
|
// Map for easy lookups of supported policies.
|
||||||
var supportedPolicies = map[string]Policy{
|
var supportedPolicies = map[string]Policy{
|
||||||
"": PolicyDefault,
|
"": PolicyDefault,
|
||||||
"disabled": PolicyDefault,
|
"disabled": PolicyDefault,
|
||||||
"image": PolicyNewImage,
|
"image": PolicyRegistryImage,
|
||||||
|
"registry": PolicyRegistryImage,
|
||||||
|
"local": PolicyLocalImage,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct for tying a container to it's autoupdate policy
|
||||||
|
type PolicyContainer struct {
|
||||||
|
p Policy
|
||||||
|
ctr *libpod.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupPolicy looks up the corresponding Policy for the specified
|
// LookupPolicy looks up the corresponding Policy for the specified
|
||||||
@ -99,11 +109,17 @@ func ValidateImageReference(imageName string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AutoUpdate looks up containers with a specified auto-update policy and acts
|
// AutoUpdate looks up containers with a specified auto-update policy and acts
|
||||||
// accordingly. If the policy is set to PolicyNewImage, it checks if the image
|
// accordingly.
|
||||||
|
//
|
||||||
|
// If the policy is set to PolicyRegistryImage, it checks if the image
|
||||||
// on the remote registry is different than the local one. If the image digests
|
// on the remote registry is different than the local one. If the image digests
|
||||||
// differ, it pulls the remote image and restarts the systemd unit running the
|
// differ, it pulls the remote image and restarts the systemd unit running the
|
||||||
// container.
|
// container.
|
||||||
//
|
//
|
||||||
|
// If the policy is set to PolicyLocalImage, it checks if the image
|
||||||
|
// of a running container is different than the local one. If the image digests
|
||||||
|
// differ, it restarts the systemd unit with the new image.
|
||||||
|
//
|
||||||
// It returns a slice of successfully restarted systemd units and a slice of
|
// It returns a slice of successfully restarted systemd units and a slice of
|
||||||
// errors encountered during auto update.
|
// errors encountered during auto update.
|
||||||
func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) {
|
func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) {
|
||||||
@ -134,7 +150,7 @@ func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) {
|
|||||||
// Update images.
|
// Update images.
|
||||||
containersToRestart := []*libpod.Container{}
|
containersToRestart := []*libpod.Container{}
|
||||||
updatedRawImages := make(map[string]bool)
|
updatedRawImages := make(map[string]bool)
|
||||||
for imageID, containers := range containerMap {
|
for imageID, policyContainers := range containerMap {
|
||||||
image, exists := imageMap[imageID]
|
image, exists := imageMap[imageID]
|
||||||
if !exists {
|
if !exists {
|
||||||
errs = append(errs, errors.Errorf("container image ID %q not found in local storage", imageID))
|
errs = append(errs, errors.Errorf("container image ID %q not found in local storage", imageID))
|
||||||
@ -143,34 +159,53 @@ func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) {
|
|||||||
// Now we have to check if the image of any containers must be updated.
|
// Now we have to check if the image of any containers must be updated.
|
||||||
// Note that the image ID is NOT enough for this check as a given image
|
// Note that the image ID is NOT enough for this check as a given image
|
||||||
// may have multiple tags.
|
// may have multiple tags.
|
||||||
for i, ctr := range containers {
|
for i, pc := range policyContainers {
|
||||||
rawImageName := ctr.RawImageName()
|
cid := pc.ctr.ID()
|
||||||
|
rawImageName := pc.ctr.RawImageName()
|
||||||
if rawImageName == "" {
|
if rawImageName == "" {
|
||||||
errs = append(errs, errors.Errorf("error auto-updating container %q: raw-image name is empty", ctr.ID()))
|
errs = append(errs, errors.Errorf("error auto-updating container %q: raw-image name is empty", pc.ctr.ID()))
|
||||||
}
|
}
|
||||||
labels := ctr.Labels()
|
|
||||||
authFilePath, exists := labels[AuthfileLabel]
|
switch pc.p {
|
||||||
if exists {
|
// Sanity Check, should be unreachable code
|
||||||
options.Authfile = authFilePath
|
case PolicyDefault:
|
||||||
}
|
errs = append(errs, errors.Errorf("error auto-updating container %q: invalid policy", cid))
|
||||||
needsUpdate, err := newerImageAvailable(runtime, image, rawImageName, options)
|
|
||||||
|
// Registry Autoupdate Containers pull new images and are flagged for restart.
|
||||||
|
case PolicyRegistryImage:
|
||||||
|
readAuthenticationPath(pc.ctr, options)
|
||||||
|
needsUpdate, err := newerRemoteImageAvailable(runtime, image, rawImageName, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image check for %q failed", ctr.ID(), rawImageName))
|
errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image check for %q failed", cid, rawImageName))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !needsUpdate {
|
|
||||||
continue
|
if needsUpdate {
|
||||||
}
|
logrus.Infof("Auto-updating container %q using registry image %q", cid, rawImageName)
|
||||||
logrus.Infof("Auto-updating container %q using image %q", ctr.ID(), rawImageName)
|
|
||||||
if _, updated := updatedRawImages[rawImageName]; !updated {
|
if _, updated := updatedRawImages[rawImageName]; !updated {
|
||||||
_, err = updateImage(runtime, rawImageName, options)
|
_, err = updateImage(runtime, rawImageName, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image update for %q failed", ctr.ID(), rawImageName))
|
errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image update for %q failed", cid, rawImageName))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
updatedRawImages[rawImageName] = true
|
updatedRawImages[rawImageName] = true
|
||||||
}
|
}
|
||||||
containersToRestart = append(containersToRestart, containers[i])
|
containersToRestart = append(containersToRestart, policyContainers[i].ctr)
|
||||||
|
}
|
||||||
|
// Local Autoupdate Containers with an update are flagged for restart.
|
||||||
|
case PolicyLocalImage:
|
||||||
|
// This avoids restarting containers unnecessarily.
|
||||||
|
needsUpdate, err := newerLocalImageAvailable(image, rawImageName)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image check for %q failed", cid, rawImageName))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if needsUpdate {
|
||||||
|
logrus.Infof("Auto-updating container %q using local image %q", cid, rawImageName)
|
||||||
|
containersToRestart = append(containersToRestart, policyContainers[i].ctr)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,14 +233,14 @@ func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) {
|
|||||||
|
|
||||||
// imageContainersMap generates a map[image ID] -> [containers using the image]
|
// imageContainersMap generates a map[image ID] -> [containers using the image]
|
||||||
// of all containers with a valid auto-update policy.
|
// of all containers with a valid auto-update policy.
|
||||||
func imageContainersMap(runtime *libpod.Runtime) (map[string][]*libpod.Container, []error) {
|
func imageContainersMap(runtime *libpod.Runtime) (map[string][]PolicyContainer, []error) {
|
||||||
allContainers, err := runtime.GetAllContainers()
|
allContainers, err := runtime.GetAllContainers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, []error{err}
|
return nil, []error{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
errors := []error{}
|
errors := []error{}
|
||||||
imageMap := make(map[string][]*libpod.Container)
|
containerMap := make(map[string][]PolicyContainer)
|
||||||
for i, ctr := range allContainers {
|
for i, ctr := range allContainers {
|
||||||
state, err := ctr.State()
|
state, err := ctr.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -230,22 +265,35 @@ func imageContainersMap(runtime *libpod.Runtime) (map[string][]*libpod.Container
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip non-image labels (could be explicitly disabled).
|
// Skip labels not related to autoupdate
|
||||||
if policy != PolicyNewImage {
|
if policy != PolicyDefault {
|
||||||
|
id, _ := ctr.Image()
|
||||||
|
pc := PolicyContainer{
|
||||||
|
p: policy,
|
||||||
|
ctr: allContainers[i],
|
||||||
|
}
|
||||||
|
containerMap[id] = append(containerMap[id], pc)
|
||||||
|
// Now we know that `ctr` is configured for auto updates.
|
||||||
|
} else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we know that `ctr` is configured for auto updates.
|
|
||||||
id, _ := ctr.Image()
|
|
||||||
imageMap[id] = append(imageMap[id], allContainers[i])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageMap, errors
|
return containerMap, errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// newerImageAvailable returns true if there corresponding image on the remote
|
// readAuthenticationPath reads a container's labels and reads authentication path into options
|
||||||
|
func readAuthenticationPath(ctr *libpod.Container, options Options) {
|
||||||
|
labels := ctr.Labels()
|
||||||
|
authFilePath, exists := labels[AuthfileLabel]
|
||||||
|
if exists {
|
||||||
|
options.Authfile = authFilePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newerRemoteImageAvailable returns true if there corresponding image on the remote
|
||||||
// registry is newer.
|
// registry is newer.
|
||||||
func newerImageAvailable(runtime *libpod.Runtime, img *image.Image, origName string, options Options) (bool, error) {
|
func newerRemoteImageAvailable(runtime *libpod.Runtime, img *image.Image, origName string, options Options) (bool, error) {
|
||||||
remoteRef, err := docker.ParseReference("//" + origName)
|
remoteRef, err := docker.ParseReference("//" + origName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -282,6 +330,25 @@ func newerImageAvailable(runtime *libpod.Runtime, img *image.Image, origName str
|
|||||||
return img.Digest().String() != remoteDigest.String(), nil
|
return img.Digest().String() != remoteDigest.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newerLocalImageAvailable returns true if the container and local image have different digests
|
||||||
|
func newerLocalImageAvailable(img *image.Image, rawImageName string) (bool, error) {
|
||||||
|
rt, err := libpod.NewRuntime(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
localImg, err := rt.ImageRuntime().NewFromLocal(rawImageName)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
localDigest := localImg.Digest().String()
|
||||||
|
|
||||||
|
ctrDigest := img.Digest().String()
|
||||||
|
|
||||||
|
return localDigest != ctrDigest, nil
|
||||||
|
}
|
||||||
|
|
||||||
// updateImage pulls the specified image.
|
// updateImage pulls the specified image.
|
||||||
func updateImage(runtime *libpod.Runtime, name string, options Options) (*image.Image, error) {
|
func updateImage(runtime *libpod.Runtime, name string, options Options) (*image.Image, error) {
|
||||||
sys := runtime.SystemContext()
|
sys := runtime.SystemContext()
|
||||||
|
Reference in New Issue
Block a user