github.com/dctrud/umoci@v0.4.3-0.20191016193643-05a1d37de015/cmd/umoci/new.go (about)

     1  /*
     2   * umoci: Umoci Modifies Open Containers' Images
     3   * Copyright (C) 2016, 2017, 2018 SUSE LLC.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *    http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package main
    19  
    20  import (
    21  	"runtime"
    22  	"time"
    23  
    24  	"github.com/apex/log"
    25  	"github.com/openSUSE/umoci/oci/cas/dir"
    26  	"github.com/openSUSE/umoci/oci/casext"
    27  	igen "github.com/openSUSE/umoci/oci/config/generate"
    28  	imeta "github.com/opencontainers/image-spec/specs-go"
    29  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
    30  	"github.com/pkg/errors"
    31  	"github.com/urfave/cli"
    32  	"golang.org/x/net/context"
    33  )
    34  
    35  var newCommand = cli.Command{
    36  	Name:  "new",
    37  	Usage: "creates a blank tagged OCI image",
    38  	ArgsUsage: `--image <image-path>:<new-tag>
    39  
    40  Where "<image-path>" is the path to the OCI image, and "<new-tag>" is the name
    41  of the tag for the empty manifest.
    42  
    43  Once you create a new image with umoci-new(1) you can directly use the image
    44  with umoci-unpack(1), umoci-repack(1), and umoci-config(1) to modify the new
    45  manifest as you see fit. This allows you to create entirely new images without
    46  needing a base image to start from.`,
    47  
    48  	// new modifies an image layout.
    49  	Category: "image",
    50  
    51  	Action: newImage,
    52  }
    53  
    54  func newImage(ctx *cli.Context) error {
    55  	imagePath := ctx.App.Metadata["--image-path"].(string)
    56  	tagName := ctx.App.Metadata["--image-tag"].(string)
    57  
    58  	// Get a reference to the CAS.
    59  	engine, err := dir.Open(imagePath)
    60  	if err != nil {
    61  		return errors.Wrap(err, "open CAS")
    62  	}
    63  	engineExt := casext.NewEngine(engine)
    64  	defer engine.Close()
    65  
    66  	// Create a new manifest.
    67  	log.WithFields(log.Fields{
    68  		"tag": tagName,
    69  	}).Debugf("creating new manifest")
    70  
    71  	// Create a new image config.
    72  	g := igen.New()
    73  	createTime := time.Now()
    74  
    75  	// Set all of the defaults we need.
    76  	g.SetCreated(createTime)
    77  	g.SetOS(runtime.GOOS)
    78  	g.SetArchitecture(runtime.GOARCH)
    79  	g.ClearHistory()
    80  
    81  	// Make sure we have no diffids.
    82  	g.SetRootfsType("layers")
    83  	g.ClearRootfsDiffIDs()
    84  
    85  	// Update config and create a new blob for it.
    86  	config := g.Image()
    87  	configDigest, configSize, err := engineExt.PutBlobJSON(context.Background(), config)
    88  	if err != nil {
    89  		return errors.Wrap(err, "put config blob")
    90  	}
    91  
    92  	log.WithFields(log.Fields{
    93  		"digest": configDigest,
    94  		"size":   configSize,
    95  	}).Debugf("umoci: added new config")
    96  
    97  	// Create a new manifest that just points to the config and has an
    98  	// empty layer set. FIXME: Implement ManifestList support.
    99  	manifest := ispec.Manifest{
   100  		Versioned: imeta.Versioned{
   101  			SchemaVersion: 2, // FIXME: This is hardcoded at the moment.
   102  		},
   103  		Config: ispec.Descriptor{
   104  			MediaType: ispec.MediaTypeImageConfig,
   105  			Digest:    configDigest,
   106  			Size:      configSize,
   107  		},
   108  		Layers: []ispec.Descriptor{},
   109  	}
   110  
   111  	manifestDigest, manifestSize, err := engineExt.PutBlobJSON(context.Background(), manifest)
   112  	if err != nil {
   113  		return errors.Wrap(err, "put manifest blob")
   114  	}
   115  
   116  	log.WithFields(log.Fields{
   117  		"digest": manifestDigest,
   118  		"size":   manifestSize,
   119  	}).Debugf("umoci: added new manifest")
   120  
   121  	// Now create a new reference, and either add it to the engine or spew it
   122  	// to stdout.
   123  
   124  	descriptor := ispec.Descriptor{
   125  		// FIXME: Support manifest lists.
   126  		MediaType: ispec.MediaTypeImageManifest,
   127  		Digest:    manifestDigest,
   128  		Size:      manifestSize,
   129  	}
   130  
   131  	log.Infof("new image manifest created: %s", descriptor.Digest)
   132  
   133  	if err := engineExt.UpdateReference(context.Background(), tagName, descriptor); err != nil {
   134  		return errors.Wrap(err, "add new tag")
   135  	}
   136  
   137  	log.Infof("created new tag for image manifest: %s", tagName)
   138  
   139  	return nil
   140  }