github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/plugins/juju-metadata/toolsmetadata.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 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 "github.com/juju/gnuflag" 12 "github.com/juju/loggo" 13 "github.com/juju/utils" 14 15 "github.com/juju/juju/cmd/modelcmd" 16 "github.com/juju/juju/environs/filestorage" 17 "github.com/juju/juju/environs/simplestreams" 18 "github.com/juju/juju/environs/storage" 19 envtools "github.com/juju/juju/environs/tools" 20 "github.com/juju/juju/juju/keys" 21 "github.com/juju/juju/juju/osenv" 22 coretools "github.com/juju/juju/tools" 23 ) 24 25 func newToolsMetadataCommand() cmd.Command { 26 return modelcmd.Wrap(&toolsMetadataCommand{}) 27 } 28 29 // toolsMetadataCommand is used to generate simplestreams metadata for juju tools. 30 type toolsMetadataCommand struct { 31 modelcmd.ModelCommandBase 32 fetch bool 33 metadataDir string 34 stream string 35 clean bool 36 public bool 37 } 38 39 const toolsMetadataDoc = ` 40 generate-tools creates simplestreams tools metadata. 41 42 This command works by scanning a directory for tools tarballs from which to generate 43 simplestreams tools metadata. The working directory is specified using the -d argument 44 (defaults to $JUJU_DATA or if not defined $XDG_DATA_HOME/juju or if that is not defined 45 ~/.local/share/juju). The working directory is expected to contain a named subdirectory 46 containing tools tarballs, and is where the resulting metadata is written. 47 48 The stream for which metadata is generated is specified using the --stream parameter 49 (default is "released"). Metadata can be generated for any supported stream - released, 50 proposed, testing, devel. 51 52 Tools tarballs can are located in either a sub directory called "releases" (legacy), 53 or a directory named after the stream. By default, if no --stream argument is provided, 54 metadata for tools in the "released" stream is generated by scanning for tool tarballs 55 in the "releases" directory. By specifying a stream explcitly, tools tarballs are 56 expected to be located in a directory named after the stream. 57 58 Newly generated metadata will be merged with any exisitng metadata that is already there. 59 To first remove metadata for the specified stream before generating new metadata, 60 use the --clean option. 61 62 Examples: 63 64 # generate metadata for "released": 65 juju metadata generate-tools -d <workingdir> 66 67 # generate metadata for "released": 68 juju metadata generate-tools -d <workingdir> --stream released 69 70 # generate metadata for "proposed": 71 juju metadata generate-tools -d <workingdir> --stream proposed 72 73 # generate metadata for "proposed", first removing existing "proposed" metadata: 74 juju metadata generate-tools -d <workingdir> --stream proposed --clean 75 ` 76 77 func (c *toolsMetadataCommand) Info() *cmd.Info { 78 return &cmd.Info{ 79 Name: "generate-tools", 80 Purpose: "generate simplestreams tools metadata", 81 Doc: toolsMetadataDoc, 82 } 83 } 84 85 func (c *toolsMetadataCommand) SetFlags(f *gnuflag.FlagSet) { 86 f.StringVar(&c.metadataDir, "d", "", "local directory in which to store metadata") 87 f.StringVar(&c.stream, "stream", envtools.ReleasedStream, 88 "simplestreams stream for which to generate the metadata") 89 f.BoolVar(&c.clean, "clean", false, 90 "remove any existing metadata for the specified stream before generating new metadata") 91 f.BoolVar(&c.public, "public", false, 92 "tools are for a public cloud, so generate mirrors information") 93 } 94 95 func (c *toolsMetadataCommand) Run(context *cmd.Context) error { 96 writer := loggo.NewMinimumLevelWriter( 97 cmd.NewCommandLogWriter("juju.environs.tools", context.Stdout, context.Stderr), 98 loggo.INFO) 99 loggo.RegisterWriter("toolsmetadata", writer) 100 defer loggo.RemoveWriter("toolsmetadata") 101 if c.metadataDir == "" { 102 c.metadataDir = osenv.JujuXDGDataHomeDir() 103 } else { 104 c.metadataDir = context.AbsPath(c.metadataDir) 105 } 106 107 sourceStorage, err := filestorage.NewFileStorageReader(c.metadataDir) 108 if err != nil { 109 return errors.Trace(err) 110 } 111 112 fmt.Fprintf(context.Stdout, "Finding tools in %s for stream %s.\n", c.metadataDir, c.stream) 113 toolsList, err := envtools.ReadList(sourceStorage, c.stream, -1, -1) 114 if err == envtools.ErrNoTools { 115 var source string 116 source, err = envtools.ToolsURL(envtools.DefaultBaseURL) 117 if err != nil { 118 return errors.Trace(err) 119 } 120 toolsList, err = envtools.FindToolsForCloud(toolsDataSources(source), simplestreams.CloudSpec{}, c.stream, -1, -1, coretools.Filter{}) 121 } 122 if err != nil { 123 return errors.Trace(err) 124 } 125 126 targetStorage, err := filestorage.NewFileStorageWriter(c.metadataDir) 127 if err != nil { 128 return errors.Trace(err) 129 } 130 writeMirrors := envtools.DoNotWriteMirrors 131 if c.public { 132 writeMirrors = envtools.WriteMirrors 133 } 134 return errors.Trace(mergeAndWriteMetadata(targetStorage, c.stream, c.stream, c.clean, toolsList, writeMirrors)) 135 } 136 137 func toolsDataSources(urls ...string) []simplestreams.DataSource { 138 dataSources := make([]simplestreams.DataSource, len(urls)) 139 for i, url := range urls { 140 dataSources[i] = simplestreams.NewURLSignedDataSource( 141 "local source", 142 url, 143 keys.JujuPublicKey, 144 utils.VerifySSLHostnames, 145 simplestreams.CUSTOM_CLOUD_DATA, 146 false) 147 } 148 return dataSources 149 } 150 151 // This is essentially the same as tools.MergeAndWriteMetadata, but also 152 // resolves metadata for existing tools by fetching them and computing 153 // size/sha256 locally. 154 func mergeAndWriteMetadata( 155 stor storage.Storage, toolsDir, stream string, clean bool, toolsList coretools.List, writeMirrors envtools.ShouldWriteMirrors, 156 ) error { 157 existing, err := envtools.ReadAllMetadata(stor) 158 if err != nil { 159 return err 160 } 161 if clean { 162 delete(existing, stream) 163 } 164 metadata := envtools.MetadataFromTools(toolsList, toolsDir) 165 var mergedMetadata []*envtools.ToolsMetadata 166 if mergedMetadata, err = envtools.MergeMetadata(metadata, existing[stream]); err != nil { 167 return err 168 } 169 if err = envtools.ResolveMetadata(stor, toolsDir, mergedMetadata); err != nil { 170 return err 171 } 172 existing[stream] = mergedMetadata 173 return envtools.WriteMetadata(stor, existing, []string{stream}, writeMirrors) 174 }