"Fossies" - the Fresh Open Source Software Archive

Member "buildah-1.27.2/internal/source/add.go" (20 Sep 2022, 3886 Bytes) of package /linux/misc/buildah-1.27.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "add.go": 1.26.4_vs_1.27.0.

    1 package source
    2 
    3 import (
    4     "context"
    5     "encoding/json"
    6     "fmt"
    7     "io/ioutil"
    8     "os"
    9     "path/filepath"
   10     "strings"
   11 
   12     "github.com/containers/image/v5/types"
   13     "github.com/containers/storage/pkg/archive"
   14     specV1 "github.com/opencontainers/image-spec/specs-go/v1"
   15 )
   16 
   17 // AddOptions include data to alter certain knobs when adding a source artifact
   18 // to a source image.
   19 type AddOptions struct {
   20     // Annotations for the source artifact.
   21     Annotations []string
   22 }
   23 
   24 // annotations parses the specified annotations and transforms them into a map.
   25 // A given annotation can be specified only once.
   26 func (o *AddOptions) annotations() (map[string]string, error) {
   27     annotations := make(map[string]string)
   28 
   29     for _, unparsed := range o.Annotations {
   30         parsed := strings.SplitN(unparsed, "=", 2)
   31         if len(parsed) != 2 {
   32             return nil, fmt.Errorf("invalid annotation %q (expected format is \"key=value\")", unparsed)
   33         }
   34         if _, exists := annotations[parsed[0]]; exists {
   35             return nil, fmt.Errorf("annotation %q specified more than once", parsed[0])
   36         }
   37         annotations[parsed[0]] = parsed[1]
   38     }
   39 
   40     return annotations, nil
   41 }
   42 
   43 // Add adds the specified source artifact at `artifactPath` to the source image
   44 // at `sourcePath`.  Note that the artifact will be added as a gzip-compressed
   45 // tar ball.  Add attempts to auto-tar and auto-compress only if necessary.
   46 func Add(ctx context.Context, sourcePath string, artifactPath string, options AddOptions) error {
   47     // Let's first make sure `sourcePath` exists and that we can access it.
   48     if _, err := os.Stat(sourcePath); err != nil {
   49         return err
   50     }
   51 
   52     annotations, err := options.annotations()
   53     if err != nil {
   54         return err
   55     }
   56 
   57     ociDest, err := openOrCreateSourceImage(ctx, sourcePath)
   58     if err != nil {
   59         return err
   60     }
   61     defer ociDest.Close()
   62 
   63     tarStream, err := archive.TarWithOptions(artifactPath, &archive.TarOptions{Compression: archive.Gzip})
   64     if err != nil {
   65         return fmt.Errorf("error creating compressed tar stream: %w", err)
   66     }
   67 
   68     info := types.BlobInfo{
   69         Size: -1, // "unknown": we'll get that information *after* adding
   70     }
   71     addedBlob, err := ociDest.PutBlob(ctx, tarStream, info, nil, false)
   72     if err != nil {
   73         return fmt.Errorf("error adding source artifact: %w", err)
   74     }
   75 
   76     // Add the new layers to the source image's manifest.
   77     manifest, oldManifestDigest, _, err := readManifestFromOCIPath(ctx, sourcePath)
   78     if err != nil {
   79         return err
   80     }
   81     manifest.Layers = append(manifest.Layers,
   82         specV1.Descriptor{
   83             MediaType:   specV1.MediaTypeImageLayerGzip,
   84             Digest:      addedBlob.Digest,
   85             Size:        addedBlob.Size,
   86             Annotations: annotations,
   87         },
   88     )
   89     manifestDigest, manifestSize, err := writeManifest(ctx, manifest, ociDest)
   90     if err != nil {
   91         return err
   92     }
   93 
   94     // Now, as we've written the updated manifest, we can delete the
   95     // previous one.  `types.ImageDestination` doesn't expose a high-level
   96     // API to manage multi-manifest destination, so we need to do it
   97     // manually.  Not an issue, since paths are predictable for an OCI
   98     // layout.
   99     if err := removeBlob(oldManifestDigest, sourcePath); err != nil {
  100         return fmt.Errorf("error removing old manifest: %w", err)
  101     }
  102 
  103     manifestDescriptor := specV1.Descriptor{
  104         MediaType: specV1.MediaTypeImageManifest,
  105         Digest:    *manifestDigest,
  106         Size:      manifestSize,
  107     }
  108     if err := updateIndexWithNewManifestDescriptor(&manifestDescriptor, sourcePath); err != nil {
  109         return err
  110     }
  111 
  112     return nil
  113 }
  114 
  115 func updateIndexWithNewManifestDescriptor(manifest *specV1.Descriptor, sourcePath string) error {
  116     index := specV1.Index{}
  117     indexPath := filepath.Join(sourcePath, "index.json")
  118 
  119     rawData, err := ioutil.ReadFile(indexPath)
  120     if err != nil {
  121         return err
  122     }
  123     if err := json.Unmarshal(rawData, &index); err != nil {
  124         return err
  125     }
  126 
  127     index.Manifests = []specV1.Descriptor{*manifest}
  128     rawData, err = json.Marshal(&index)
  129     if err != nil {
  130         return err
  131     }
  132 
  133     return ioutil.WriteFile(indexPath, rawData, 0644)
  134 }