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