launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/cmd/plugins/juju-metadata/imagemetadata.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package main
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"launchpad.net/gnuflag"
    12  
    13  	"launchpad.net/juju-core/cmd"
    14  	"launchpad.net/juju-core/environs"
    15  	"launchpad.net/juju-core/environs/config"
    16  	"launchpad.net/juju-core/environs/configstore"
    17  	"launchpad.net/juju-core/environs/filestorage"
    18  	"launchpad.net/juju-core/environs/imagemetadata"
    19  	"launchpad.net/juju-core/environs/simplestreams"
    20  	"launchpad.net/juju-core/environs/storage"
    21  )
    22  
    23  // ImageMetadataCommand is used to write out simplestreams image metadata information.
    24  type ImageMetadataCommand struct {
    25  	cmd.EnvCommandBase
    26  	Dir            string
    27  	Series         string
    28  	Arch           string
    29  	ImageId        string
    30  	Region         string
    31  	Endpoint       string
    32  	privateStorage string
    33  }
    34  
    35  var imageMetadataDoc = `
    36  generate-image creates simplestreams image metadata for the specified cloud.
    37  
    38  The cloud specification comes from the current Juju environment, as specified in
    39  the usual way from either ~/.juju/environments.yaml, the -e option, or JUJU_ENV.
    40  
    41  Using command arguments, it is possible to override cloud attributes region, endpoint, and series.
    42  By default, "amd64" is used for the architecture but this may also be changed.
    43  `
    44  
    45  func (c *ImageMetadataCommand) Info() *cmd.Info {
    46  	return &cmd.Info{
    47  		Name:    "generate-image",
    48  		Purpose: "generate simplestreams image metadata",
    49  		Doc:     imageMetadataDoc,
    50  	}
    51  }
    52  
    53  func (c *ImageMetadataCommand) SetFlags(f *gnuflag.FlagSet) {
    54  	c.EnvCommandBase.SetFlags(f)
    55  	f.StringVar(&c.Series, "s", "", "the charm series")
    56  	f.StringVar(&c.Arch, "a", "amd64", "the image achitecture")
    57  	f.StringVar(&c.Dir, "d", "", "the destination directory in which to place the metadata files")
    58  	f.StringVar(&c.ImageId, "i", "", "the image id")
    59  	f.StringVar(&c.Region, "r", "", "the region")
    60  	f.StringVar(&c.Endpoint, "u", "", "the cloud endpoint (for Openstack, this is the Identity Service endpoint)")
    61  }
    62  
    63  func (c *ImageMetadataCommand) Init(args []string) error {
    64  	c.privateStorage = "<private storage name>"
    65  	var environ environs.Environ
    66  	if store, err := configstore.Default(); err == nil {
    67  		if environ, err = environs.PrepareFromName(c.EnvName, store); err == nil {
    68  			logger.Infof("creating image metadata for environment %q", environ.Name())
    69  			// If the user has not specified region and endpoint, try and get it from the environment.
    70  			if c.Region == "" || c.Endpoint == "" {
    71  				var cloudSpec simplestreams.CloudSpec
    72  				if inst, ok := environ.(simplestreams.HasRegion); ok {
    73  					if cloudSpec, err = inst.Region(); err != nil {
    74  						return err
    75  					}
    76  				} else {
    77  					return fmt.Errorf("environment %q cannot provide region and endpoint", environ.Name())
    78  				}
    79  				// If only one of region or endpoint is provided, that is a problem.
    80  				if cloudSpec.Region != cloudSpec.Endpoint && (cloudSpec.Region == "" || cloudSpec.Endpoint == "") {
    81  					return fmt.Errorf("cannot generate metadata without a complete cloud configuration")
    82  				}
    83  				if c.Region == "" {
    84  					c.Region = cloudSpec.Region
    85  				}
    86  				if c.Endpoint == "" {
    87  					c.Endpoint = cloudSpec.Endpoint
    88  				}
    89  			}
    90  			cfg := environ.Config()
    91  			if c.Series == "" {
    92  				c.Series = cfg.DefaultSeries()
    93  			}
    94  			if v, ok := cfg.AllAttrs()["control-bucket"]; ok {
    95  				c.privateStorage = v.(string)
    96  			}
    97  		} else {
    98  			logger.Warningf("environment %q could not be opened: %v", c.EnvName, err)
    99  		}
   100  	}
   101  	if environ == nil {
   102  		logger.Infof("no environment found, creating image metadata using user supplied data")
   103  	}
   104  	if c.Series == "" {
   105  		c.Series = config.DefaultSeries
   106  	}
   107  	if c.ImageId == "" {
   108  		return fmt.Errorf("image id must be specified")
   109  	}
   110  	if c.Region == "" {
   111  		return fmt.Errorf("image region must be specified")
   112  	}
   113  	if c.Endpoint == "" {
   114  		return fmt.Errorf("cloud endpoint URL must be specified")
   115  	}
   116  	if c.Dir == "" {
   117  		logger.Infof("no destination directory specified, using current directory")
   118  		var err error
   119  		if c.Dir, err = os.Getwd(); err != nil {
   120  			return err
   121  		}
   122  	}
   123  
   124  	return cmd.CheckEmpty(args)
   125  }
   126  
   127  var helpDoc = `
   128  image metadata files have been written to:
   129  %s.
   130  For Juju to use this metadata, the files need to be put into the
   131  image metadata search path. There are 2 options:
   132  
   133  1. Use the --metadata-source parameter when bootstrapping:
   134     juju bootstrap --metadata-source %s
   135  
   136  2. Use image-metadata-url in $JUJU_HOME/environments.yaml
   137  Configure a http server to serve the contents of
   138  %s
   139  and set the value of image-metadata-url accordingly.
   140  
   141  "
   142  
   143  `
   144  
   145  func (c *ImageMetadataCommand) Run(context *cmd.Context) error {
   146  	out := context.Stdout
   147  
   148  	im := &imagemetadata.ImageMetadata{
   149  		Id:   c.ImageId,
   150  		Arch: c.Arch,
   151  	}
   152  	cloudSpec := simplestreams.CloudSpec{
   153  		Region:   c.Region,
   154  		Endpoint: c.Endpoint,
   155  	}
   156  	targetStorage, err := filestorage.NewFileStorageWriter(c.Dir, filestorage.UseDefaultTmpDir)
   157  	if err != nil {
   158  		return err
   159  	}
   160  	err = imagemetadata.MergeAndWriteMetadata(c.Series, []*imagemetadata.ImageMetadata{im}, &cloudSpec, targetStorage)
   161  	if err != nil {
   162  		return fmt.Errorf("image metadata files could not be created: %v", err)
   163  	}
   164  	dir := context.AbsPath(c.Dir)
   165  	dest := filepath.Join(dir, storage.BaseImagesPath, "streams", "v1")
   166  	fmt.Fprintf(out, fmt.Sprintf(helpDoc, dest, dir, dir))
   167  	return nil
   168  }