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