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