github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/plugins/juju-metadata/validatetoolsmetadata.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  	"bytes"
     8  	"fmt"
     9  	"os"
    10  	"strings"
    11  
    12  	"github.com/juju/cmd"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/gnuflag"
    15  	"github.com/juju/utils/arch"
    16  	"github.com/juju/version"
    17  
    18  	"github.com/juju/juju/cmd/modelcmd"
    19  	"github.com/juju/juju/cmd/output"
    20  	"github.com/juju/juju/environs"
    21  	"github.com/juju/juju/environs/simplestreams"
    22  	"github.com/juju/juju/environs/tools"
    23  	jujuversion "github.com/juju/juju/version"
    24  )
    25  
    26  func newValidateToolsMetadataCommand() cmd.Command {
    27  	return modelcmd.Wrap(&validateToolsMetadataCommand{})
    28  }
    29  
    30  // validateToolsMetadataCommand
    31  type validateToolsMetadataCommand struct {
    32  	imageMetadataCommandBase
    33  	out          cmd.Output
    34  	providerType string
    35  	metadataDir  string
    36  	stream       string
    37  	series       string
    38  	region       string
    39  	endpoint     string
    40  	exactVersion string
    41  	partVersion  string
    42  	major        int
    43  	minor        int
    44  }
    45  
    46  var validateToolsMetadataDoc = `
    47  validate-tools loads simplestreams metadata and validates the contents by
    48  looking for tools belonging to the specified series, architecture, for the
    49  specified cloud. If version is specified, tools matching the exact specified
    50  version are found. It is also possible to just specify the major (and optionally
    51  minor) version numbers to search for.
    52  
    53  The cloud specification comes from the current Juju model, as specified in
    54  the usual way from either the -m option, or JUJU_MODEL. Series, Region, and
    55  Endpoint are the key attributes.
    56  
    57  It is possible to specify a local directory containing tools metadata, in which
    58  case cloud attributes like provider type, region etc are optional.
    59  
    60  The key model attributes may be overridden using command arguments, so
    61  that the validation may be peformed on arbitary metadata.
    62  
    63  Examples:
    64  
    65   - validate using the current model settings but with series raring
    66    
    67    juju metadata validate-tools -s raring
    68  
    69   - validate using the current model settings but with Juju version 1.11.4
    70    
    71    juju metadata validate-tools -j 1.11.4
    72  
    73   - validate using the current model settings but with Juju major version 2
    74    
    75    juju metadata validate-tools -m 2
    76  
    77   - validate using the current model settings but with Juju major.minor version 2.1
    78   
    79    juju metadata validate-tools -m 2.1
    80  
    81   - validate using the current model settings and list all tools found for any series
    82   
    83    juju metadata validate-tools --series=
    84  
    85   - validate with series raring and using metadata from local directory
    86   
    87    juju metadata validate-tools -s raring -d <some directory>
    88  
    89   - validate for the proposed stream
    90  
    91    juju metadata validate-tools --stream proposed
    92  
    93  A key use case is to validate newly generated metadata prior to deployment to
    94  production. In this case, the metadata is placed in a local directory, a cloud
    95  provider type is specified (ec2, openstack etc), and the validation is performed
    96  for each supported series, version, and arcgitecture.
    97  
    98  Example bash snippet:
    99  
   100  #!/bin/bash
   101  
   102  juju metadata validate-tools -p ec2 -r us-east-1 -s precise --juju-version 1.12.0 -d <some directory>
   103  RETVAL=$?
   104  [ $RETVAL -eq 0 ] && echo Success
   105  [ $RETVAL -ne 0 ] && echo Failure
   106  `
   107  
   108  func (c *validateToolsMetadataCommand) Info() *cmd.Info {
   109  	return &cmd.Info{
   110  		Name:    "validate-tools",
   111  		Purpose: "validate tools metadata and ensure tools tarball(s) exist for Juju version(s)",
   112  		Doc:     validateToolsMetadataDoc,
   113  	}
   114  }
   115  
   116  func (c *validateToolsMetadataCommand) SetFlags(f *gnuflag.FlagSet) {
   117  	c.out.AddFlags(f, "yaml", output.DefaultFormatters)
   118  	f.StringVar(&c.providerType, "p", "", "the provider type eg ec2, openstack")
   119  	f.StringVar(&c.metadataDir, "d", "", "directory where metadata files are found")
   120  	f.StringVar(&c.series, "s", "", "the series for which to validate (overrides env config series)")
   121  	f.StringVar(&c.series, "series", "", "")
   122  	f.StringVar(&c.region, "r", "", "the region for which to validate (overrides env config region)")
   123  	f.StringVar(&c.endpoint, "u", "", "the cloud endpoint URL for which to validate (overrides env config endpoint)")
   124  	f.StringVar(&c.exactVersion, "j", "current", "the Juju version (use 'current' for current version)")
   125  	f.StringVar(&c.exactVersion, "juju-version", "", "")
   126  	f.StringVar(&c.partVersion, "majorminor-version", "", "")
   127  	f.StringVar(&c.stream, "stream", tools.ReleasedStream, "simplestreams stream for which to generate the metadata")
   128  }
   129  
   130  func (c *validateToolsMetadataCommand) Init(args []string) error {
   131  	if c.providerType != "" {
   132  		if c.region == "" {
   133  			return errors.Errorf("region required if provider type is specified")
   134  		}
   135  		if c.metadataDir == "" {
   136  			return errors.Errorf("metadata directory required if provider type is specified")
   137  		}
   138  	}
   139  	if c.exactVersion == "current" {
   140  		c.exactVersion = jujuversion.Current.String()
   141  	}
   142  	if c.partVersion != "" {
   143  		var err error
   144  		if c.major, c.minor, err = version.ParseMajorMinor(c.partVersion); err != nil {
   145  			return err
   146  		}
   147  	}
   148  	return cmd.CheckEmpty(args)
   149  }
   150  
   151  func (c *validateToolsMetadataCommand) Run(context *cmd.Context) error {
   152  	var params *simplestreams.MetadataLookupParams
   153  
   154  	if c.providerType == "" {
   155  		environ, err := c.prepare(context)
   156  		if err == nil {
   157  			mdLookup, ok := environ.(simplestreams.MetadataValidator)
   158  			if !ok {
   159  				return errors.Errorf("%s provider does not support tools metadata validation", environ.Config().Type())
   160  			}
   161  			params, err = mdLookup.MetadataLookupParams(c.region)
   162  			if err != nil {
   163  				return err
   164  			}
   165  			params.Sources, err = tools.GetMetadataSources(environ)
   166  			if err != nil {
   167  				return err
   168  			}
   169  		} else {
   170  			if c.metadataDir == "" {
   171  				return err
   172  			}
   173  			params = &simplestreams.MetadataLookupParams{}
   174  		}
   175  	} else {
   176  		prov, err := environs.Provider(c.providerType)
   177  		if err != nil {
   178  			return err
   179  		}
   180  		mdLookup, ok := prov.(simplestreams.MetadataValidator)
   181  		if !ok {
   182  			return errors.Errorf("%s provider does not support tools metadata validation", c.providerType)
   183  		}
   184  		params, err = mdLookup.MetadataLookupParams(c.region)
   185  		if err != nil {
   186  			return err
   187  		}
   188  	}
   189  
   190  	if len(params.Architectures) == 0 {
   191  		params.Architectures = arch.AllSupportedArches
   192  	}
   193  
   194  	if c.series != "" {
   195  		params.Series = c.series
   196  	}
   197  	if c.region != "" {
   198  		params.Region = c.region
   199  	}
   200  	if c.endpoint != "" {
   201  		params.Endpoint = c.endpoint
   202  	}
   203  	if c.metadataDir != "" {
   204  		if _, err := os.Stat(c.metadataDir); err != nil {
   205  			return err
   206  		}
   207  		toolsURL, err := tools.ToolsURL(c.metadataDir)
   208  		if err != nil {
   209  			return err
   210  		}
   211  		params.Sources = toolsDataSources(toolsURL)
   212  	}
   213  	params.Stream = c.stream
   214  
   215  	versions, resolveInfo, err := tools.ValidateToolsMetadata(&tools.ToolsMetadataLookupParams{
   216  		MetadataLookupParams: *params,
   217  		Version:              c.exactVersion,
   218  		Major:                c.major,
   219  		Minor:                c.minor,
   220  	})
   221  	if err != nil {
   222  		if resolveInfo != nil {
   223  			metadata := map[string]interface{}{
   224  				"Resolve Metadata": *resolveInfo,
   225  			}
   226  			buff := &bytes.Buffer{}
   227  			if yamlErr := cmd.FormatYaml(buff, metadata); yamlErr == nil {
   228  				err = errors.Errorf("%v\n%v", err, buff.String())
   229  			}
   230  		}
   231  		return err
   232  	}
   233  
   234  	if len(versions) > 0 {
   235  		metadata := map[string]interface{}{
   236  			"Matching Tools Versions": versions,
   237  			"Resolve Metadata":        *resolveInfo,
   238  		}
   239  		c.out.Write(context, metadata)
   240  	} else {
   241  		var sources []string
   242  		for _, s := range params.Sources {
   243  			url, err := s.URL("")
   244  			if err == nil {
   245  				sources = append(sources, fmt.Sprintf("- %s (%s)", s.Description(), url))
   246  			}
   247  		}
   248  		return errors.Errorf("no matching tools using sources:\n%s", strings.Join(sources, "\n"))
   249  	}
   250  	return nil
   251  }