mirror of
https://github.com/containers/podman.git
synced 2025-09-28 01:04:28 +08:00

Also, do a general cleanup of all the timeout code. Changes include: - Convert from int to *uint where possible. Timeouts cannot be negative, hence the uint change; and a timeout of 0 is valid, so we need a new way to detect that the user set a timeout (hence, pointer). - Change name in the database to avoid conflicts between new data type and old one. This will cause timeouts set with 4.2.0 to be lost, but considering nobody is using the feature at present (and the lack of validation means we could have invalid, negative timeouts in the DB) this feels safe. - Ensure volume plugin timeouts can only be used with volumes created using a plugin. Timeouts on the local driver are nonsensical. - Remove the existing test, as it did not use a volume plugin. Write a new test that does. The actual plumbing of the containers.conf timeout in is one line in volume_api.go; the remainder are the above-described cleanups. Signed-off-by: Matthew Heon <mheon@redhat.com>
233 lines
6.3 KiB
Go
233 lines
6.3 KiB
Go
package libimage
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/containers/image/v5/manifest"
|
|
"github.com/containers/image/v5/types"
|
|
"github.com/opencontainers/go-digest"
|
|
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ImageData contains the inspected data of an image.
|
|
type ImageData struct {
|
|
ID string `json:"Id"`
|
|
Digest digest.Digest `json:"Digest"`
|
|
RepoTags []string `json:"RepoTags"`
|
|
RepoDigests []string `json:"RepoDigests"`
|
|
Parent string `json:"Parent"`
|
|
Comment string `json:"Comment"`
|
|
Created *time.Time `json:"Created"`
|
|
Config *ociv1.ImageConfig `json:"Config"`
|
|
Version string `json:"Version"`
|
|
Author string `json:"Author"`
|
|
Architecture string `json:"Architecture"`
|
|
Os string `json:"Os"`
|
|
Size int64 `json:"Size"`
|
|
VirtualSize int64 `json:"VirtualSize"`
|
|
GraphDriver *DriverData `json:"GraphDriver"`
|
|
RootFS *RootFS `json:"RootFS"`
|
|
Labels map[string]string `json:"Labels"`
|
|
Annotations map[string]string `json:"Annotations"`
|
|
ManifestType string `json:"ManifestType"`
|
|
User string `json:"User"`
|
|
History []ociv1.History `json:"History"`
|
|
NamesHistory []string `json:"NamesHistory"`
|
|
HealthCheck *manifest.Schema2HealthConfig `json:"Healthcheck,omitempty"`
|
|
}
|
|
|
|
// DriverData includes data on the storage driver of the image.
|
|
type DriverData struct {
|
|
Name string `json:"Name"`
|
|
Data map[string]string `json:"Data"`
|
|
}
|
|
|
|
// RootFS includes data on the root filesystem of the image.
|
|
type RootFS struct {
|
|
Type string `json:"Type"`
|
|
Layers []digest.Digest `json:"Layers"`
|
|
}
|
|
|
|
// InspectOptions allow for customizing inspecting images.
|
|
type InspectOptions struct {
|
|
// Compute the size of the image (expensive).
|
|
WithSize bool
|
|
// Compute the parent of the image (expensive).
|
|
WithParent bool
|
|
}
|
|
|
|
// Inspect inspects the image.
|
|
func (i *Image) Inspect(ctx context.Context, options *InspectOptions) (*ImageData, error) {
|
|
logrus.Debugf("Inspecting image %s", i.ID())
|
|
|
|
if options == nil {
|
|
options = &InspectOptions{}
|
|
}
|
|
|
|
if i.cached.completeInspectData != nil {
|
|
if options.WithSize && i.cached.completeInspectData.Size == int64(-1) {
|
|
size, err := i.Size()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
i.cached.completeInspectData.Size = size
|
|
}
|
|
if options.WithParent && i.cached.completeInspectData.Parent == "" {
|
|
parentImage, err := i.Parent(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if parentImage != nil {
|
|
i.cached.completeInspectData.Parent = parentImage.ID()
|
|
}
|
|
}
|
|
return i.cached.completeInspectData, nil
|
|
}
|
|
|
|
// First assemble data that does not depend on the format of the image.
|
|
info, err := i.inspectInfo(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ociImage, err := i.toOCI(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
repoTags, err := i.RepoTags()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
repoDigests, err := i.RepoDigests()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
driverData, err := i.driverData()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
size := int64(-1)
|
|
if options.WithSize {
|
|
size, err = i.Size()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
data := &ImageData{
|
|
ID: i.ID(),
|
|
RepoTags: repoTags,
|
|
RepoDigests: repoDigests,
|
|
Created: ociImage.Created,
|
|
Author: ociImage.Author,
|
|
Architecture: ociImage.Architecture,
|
|
Os: ociImage.OS,
|
|
Config: &ociImage.Config,
|
|
Version: info.DockerVersion,
|
|
Size: size,
|
|
VirtualSize: size, // NOTE: same as size. Inherited from Docker where it's scheduled for deprecation.
|
|
Digest: i.Digest(),
|
|
Labels: info.Labels,
|
|
RootFS: &RootFS{
|
|
Type: ociImage.RootFS.Type,
|
|
Layers: ociImage.RootFS.DiffIDs,
|
|
},
|
|
GraphDriver: driverData,
|
|
User: ociImage.Config.User,
|
|
History: ociImage.History,
|
|
NamesHistory: i.NamesHistory(),
|
|
}
|
|
|
|
if options.WithParent {
|
|
parentImage, err := i.Parent(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if parentImage != nil {
|
|
data.Parent = parentImage.ID()
|
|
}
|
|
}
|
|
|
|
// Determine the format of the image. How we determine certain data
|
|
// depends on the format (e.g., Docker v2s2, OCI v1).
|
|
src, err := i.source(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
manifestRaw, manifestType, err := src.GetManifest(ctx, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data.ManifestType = manifestType
|
|
|
|
switch manifestType {
|
|
// OCI image
|
|
case ociv1.MediaTypeImageManifest:
|
|
var ociManifest ociv1.Manifest
|
|
if err := json.Unmarshal(manifestRaw, &ociManifest); err != nil {
|
|
return nil, err
|
|
}
|
|
data.Annotations = ociManifest.Annotations
|
|
if len(ociImage.History) > 0 {
|
|
data.Comment = ociImage.History[0].Comment
|
|
}
|
|
|
|
// Docker image
|
|
case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema2MediaType:
|
|
rawConfig, err := i.rawConfigBlob(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var dockerManifest manifest.Schema2V1Image
|
|
if err := json.Unmarshal(rawConfig, &dockerManifest); err != nil {
|
|
return nil, err
|
|
}
|
|
data.Comment = dockerManifest.Comment
|
|
// NOTE: Health checks may be listed in the container config or
|
|
// the config.
|
|
data.HealthCheck = dockerManifest.ContainerConfig.Healthcheck
|
|
if data.HealthCheck == nil && dockerManifest.Config != nil {
|
|
data.HealthCheck = dockerManifest.Config.Healthcheck
|
|
}
|
|
}
|
|
|
|
if data.Annotations == nil {
|
|
// Podman compat
|
|
data.Annotations = make(map[string]string)
|
|
}
|
|
|
|
i.cached.completeInspectData = data
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// inspectInfo returns the image inspect info.
|
|
func (i *Image) inspectInfo(ctx context.Context) (*types.ImageInspectInfo, error) {
|
|
if i.cached.partialInspectData != nil {
|
|
return i.cached.partialInspectData, nil
|
|
}
|
|
|
|
ref, err := i.StorageReference()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
img, err := ref.NewImage(ctx, &i.runtime.systemContext)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer img.Close()
|
|
|
|
data, err := img.Inspect(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
i.cached.partialInspectData = data
|
|
return data, nil
|
|
}
|