concourse/atc/worker/image/image.go

271 lines
6.4 KiB
Go

package image
import (
"context"
"github.com/concourse/concourse/tracing"
"io"
"net/url"
"path"
"code.cloudfoundry.org/lager"
"github.com/concourse/baggageclaim"
"github.com/concourse/concourse/atc"
"github.com/concourse/concourse/atc/db"
"github.com/concourse/concourse/atc/worker"
)
const RawRootFSScheme = "raw"
type imageProvidedByPreviousStepOnSameWorker struct {
artifactVolume worker.Volume
imageSpec worker.ImageSpec
teamID int
volumeClient worker.VolumeClient
}
func (i *imageProvidedByPreviousStepOnSameWorker) FetchForContainer(
ctx context.Context,
logger lager.Logger,
container db.CreatingContainer,
) (worker.FetchedImage, error) {
imageVolume, err := i.volumeClient.FindOrCreateCOWVolumeForContainer(
logger,
worker.VolumeSpec{
Strategy: i.artifactVolume.COWStrategy(),
Privileged: i.imageSpec.Privileged,
},
container,
i.artifactVolume,
i.teamID,
"/",
)
if err != nil {
logger.Error("failed-to-create-image-artifact-cow-volume", err)
return worker.FetchedImage{}, err
}
imageMetadataReader, err := i.imageSpec.ImageArtifactSource.StreamFile(ctx, logger, ImageMetadataFile)
if err != nil {
logger.Error("failed-to-stream-metadata-file", err)
return worker.FetchedImage{}, err
}
metadata, err := loadMetadata(imageMetadataReader)
if err != nil {
return worker.FetchedImage{}, err
}
imageURL := url.URL{
Scheme: RawRootFSScheme,
Path: path.Join(imageVolume.Path(), "rootfs"),
}
return worker.FetchedImage{
Metadata: metadata,
URL: imageURL.String(),
Privileged: i.imageSpec.Privileged,
}, nil
}
type imageProvidedByPreviousStepOnDifferentWorker struct {
imageSpec worker.ImageSpec
teamID int
volumeClient worker.VolumeClient
}
func (i *imageProvidedByPreviousStepOnDifferentWorker) FetchForContainer(
ctx context.Context,
logger lager.Logger,
container db.CreatingContainer,
) (worker.FetchedImage, error) {
ctx, span := tracing.StartSpan(ctx, "imageProvidedByPreviousStepOnDifferentWorker.FetchForContainer", tracing.Attrs{"container_id": container.Handle()})
defer span.End()
imageVolume, err := i.volumeClient.FindOrCreateVolumeForContainer(
logger,
worker.VolumeSpec{
Strategy: baggageclaim.EmptyStrategy{},
Privileged: i.imageSpec.Privileged,
},
container,
i.teamID,
"/",
)
if err != nil {
logger.Error("failed-to-create-image-artifact-replicated-volume", err)
return worker.FetchedImage{}, err
}
dest := artifactDestination{
destination: imageVolume,
}
err = i.imageSpec.ImageArtifactSource.StreamTo(ctx, logger, &dest)
if err != nil {
logger.Error("failed-to-stream-image-artifact-source", err)
return worker.FetchedImage{}, err
}
logger.Debug("streamed-non-local-image-volume")
imageMetadataReader, err := i.imageSpec.ImageArtifactSource.StreamFile(ctx, logger, ImageMetadataFile)
if err != nil {
logger.Error("failed-to-stream-metadata-file", err)
return worker.FetchedImage{}, err
}
metadata, err := loadMetadata(imageMetadataReader)
if err != nil {
return worker.FetchedImage{}, err
}
imageURL := url.URL{
Scheme: RawRootFSScheme,
Path: path.Join(imageVolume.Path(), "rootfs"),
}
return worker.FetchedImage{
Metadata: metadata,
URL: imageURL.String(),
Privileged: i.imageSpec.Privileged,
}, nil
}
type imageFromResource struct {
privileged bool
teamID int
volumeClient worker.VolumeClient
imageResourceFetcher ImageResourceFetcher
}
func (i *imageFromResource) FetchForContainer(
ctx context.Context,
logger lager.Logger,
container db.CreatingContainer,
) (worker.FetchedImage, error) {
imageParentVolume, imageMetadataReader, version, err := i.imageResourceFetcher.Fetch(
ctx,
logger.Session("image"),
container,
i.privileged,
)
if err != nil {
logger.Error("failed-to-fetch-image", err)
return worker.FetchedImage{}, err
}
imageVolume, err := i.volumeClient.FindOrCreateCOWVolumeForContainer(
logger.Session("create-cow-volume"),
worker.VolumeSpec{
Strategy: imageParentVolume.COWStrategy(),
Privileged: i.privileged,
},
container,
imageParentVolume,
i.teamID,
"/",
)
if err != nil {
logger.Error("failed-to-create-image-resource-volume", err)
return worker.FetchedImage{}, err
}
metadata, err := loadMetadata(imageMetadataReader)
if err != nil {
return worker.FetchedImage{}, err
}
imageURL := url.URL{
Scheme: RawRootFSScheme,
Path: path.Join(imageVolume.Path(), "rootfs"),
}
return worker.FetchedImage{
Metadata: metadata,
Version: version,
URL: imageURL.String(),
Privileged: i.privileged,
}, nil
}
type imageFromBaseResourceType struct {
worker worker.Worker
resourceTypeName string
teamID int
volumeClient worker.VolumeClient
}
func (i *imageFromBaseResourceType) FetchForContainer(
ctx context.Context,
logger lager.Logger,
container db.CreatingContainer,
) (worker.FetchedImage, error) {
for _, t := range i.worker.ResourceTypes() {
if t.Type == i.resourceTypeName {
importVolume, err := i.volumeClient.FindOrCreateVolumeForBaseResourceType(
logger,
worker.VolumeSpec{
Strategy: baggageclaim.ImportStrategy{Path: t.Image},
Privileged: t.Privileged,
},
i.teamID,
i.resourceTypeName,
)
if err != nil {
return worker.FetchedImage{}, err
}
cowVolume, err := i.volumeClient.FindOrCreateCOWVolumeForContainer(
logger,
worker.VolumeSpec{
Strategy: importVolume.COWStrategy(),
Privileged: t.Privileged,
},
container,
importVolume,
i.teamID,
"/",
)
if err != nil {
return worker.FetchedImage{}, err
}
rootFSURL := url.URL{
Scheme: RawRootFSScheme,
Path: cowVolume.Path(),
}
return worker.FetchedImage{
Metadata: worker.ImageMetadata{},
Version: atc.Version{i.resourceTypeName: t.Version},
URL: rootFSURL.String(),
Privileged: t.Privileged,
}, nil
}
}
return worker.FetchedImage{}, ErrUnsupportedResourceType
}
type imageFromRootfsURI struct {
url string
}
func (i *imageFromRootfsURI) FetchForContainer(
ctx context.Context,
logger lager.Logger,
container db.CreatingContainer,
) (worker.FetchedImage, error) {
return worker.FetchedImage{
URL: i.url,
}, nil
}
type artifactDestination struct {
destination worker.Volume
}
func (wad *artifactDestination) StreamIn(ctx context.Context, path string, encoding baggageclaim.Encoding, tarStream io.Reader) error {
return wad.destination.StreamIn(ctx, path, encoding, tarStream)
}