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