github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/plugins/juju-metadata/toolsmetadata_test.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  	"path/filepath"
    11  	"regexp"
    12  	"runtime"
    13  	"sort"
    14  	"strings"
    15  	"text/template"
    16  
    17  	"github.com/juju/cmd"
    18  	"github.com/juju/loggo"
    19  	jc "github.com/juju/testing/checkers"
    20  	gc "gopkg.in/check.v1"
    21  
    22  	"github.com/juju/juju/cmd/modelcmd"
    23  	"github.com/juju/juju/environs"
    24  	"github.com/juju/juju/environs/bootstrap"
    25  	"github.com/juju/juju/environs/config"
    26  	"github.com/juju/juju/environs/tools"
    27  	toolstesting "github.com/juju/juju/environs/tools/testing"
    28  	"github.com/juju/juju/juju/keys"
    29  	"github.com/juju/juju/juju/osenv"
    30  	"github.com/juju/juju/jujuclient/jujuclienttesting"
    31  	"github.com/juju/juju/provider/dummy"
    32  	coretesting "github.com/juju/juju/testing"
    33  	jujuversion "github.com/juju/juju/version"
    34  )
    35  
    36  type ToolsMetadataSuite struct {
    37  	coretesting.FakeJujuXDGDataHomeSuite
    38  	env              environs.Environ
    39  	publicStorageDir string
    40  }
    41  
    42  var _ = gc.Suite(&ToolsMetadataSuite{})
    43  
    44  func (s *ToolsMetadataSuite) SetUpTest(c *gc.C) {
    45  	s.FakeJujuXDGDataHomeSuite.SetUpTest(c)
    46  	s.AddCleanup(dummy.Reset)
    47  	cfg, err := config.New(config.UseDefaults, map[string]interface{}{
    48  		"name":            "erewhemos",
    49  		"type":            "dummy",
    50  		"uuid":            coretesting.ModelTag.Id(),
    51  		"controller-uuid": coretesting.ControllerTag.Id(),
    52  		"conroller":       true,
    53  	})
    54  	c.Assert(err, jc.ErrorIsNil)
    55  	env, err := bootstrap.Prepare(
    56  		modelcmd.BootstrapContextNoVerify(coretesting.Context(c)),
    57  		jujuclienttesting.NewMemStore(),
    58  		bootstrap.PrepareParams{
    59  			ControllerConfig: coretesting.FakeControllerConfig(),
    60  			ControllerName:   cfg.Name(),
    61  			ModelConfig:      cfg.AllAttrs(),
    62  			Cloud:            dummy.SampleCloudSpec(),
    63  			AdminSecret:      "admin-secret",
    64  		},
    65  	)
    66  	c.Assert(err, jc.ErrorIsNil)
    67  	s.env = env
    68  	loggo.GetLogger("").SetLogLevel(loggo.INFO)
    69  
    70  	// Switch the default tools location.
    71  	s.publicStorageDir = c.MkDir()
    72  	s.PatchValue(&tools.DefaultBaseURL, s.publicStorageDir)
    73  }
    74  
    75  var currentVersionStrings = []string{
    76  	// only these ones will make it into the JSON files.
    77  	jujuversion.Current.String() + "-quantal-amd64",
    78  	jujuversion.Current.String() + "-quantal-armhf",
    79  	jujuversion.Current.String() + "-quantal-i386",
    80  }
    81  
    82  var versionStrings = append([]string{
    83  	fmt.Sprintf("%d.12.0-precise-amd64", jujuversion.Current.Major),
    84  	fmt.Sprintf("%d.12.0-precise-i386", jujuversion.Current.Major),
    85  	fmt.Sprintf("%d.12.0-raring-amd64", jujuversion.Current.Major),
    86  	fmt.Sprintf("%d.12.0-raring-i386", jujuversion.Current.Major),
    87  	fmt.Sprintf("%d.13.0-precise-amd64", jujuversion.Current.Major+1),
    88  }, currentVersionStrings...)
    89  
    90  var expectedOutputCommon = makeExpectedOutputCommon()
    91  
    92  func makeExpectedOutputCommon() string {
    93  	expected := "Finding tools in .*\n"
    94  	f := `.*Fetching tools from dir "{{.ToolsDir}}" to generate hash: %s` + "\n"
    95  
    96  	// Sort the global versionStrings
    97  	sort.Strings(versionStrings)
    98  	for _, v := range versionStrings {
    99  		expected += fmt.Sprintf(f, regexp.QuoteMeta(v))
   100  	}
   101  	return strings.TrimSpace(expected)
   102  }
   103  
   104  func makeExpectedOutput(templ, stream, toolsDir string) string {
   105  	t := template.Must(template.New("").Parse(templ))
   106  
   107  	var buf bytes.Buffer
   108  	err := t.Execute(&buf, map[string]interface{}{"Stream": stream, "ToolsDir": toolsDir})
   109  	if err != nil {
   110  		panic(err)
   111  	}
   112  	return buf.String()
   113  }
   114  
   115  var expectedOutputDirectoryReleasedTemplate = expectedOutputCommon + `
   116  .*Writing tools/streams/v1/index2\.json
   117  .*Writing tools/streams/v1/index\.json
   118  .*Writing tools/streams/v1/com\.ubuntu\.juju-{{.Stream}}-tools\.json
   119  `
   120  
   121  var expectedOutputDirectoryTemplate = expectedOutputCommon + `
   122  .*Writing tools/streams/v1/index2\.json
   123  .*Writing tools/streams/v1/com\.ubuntu\.juju-{{.Stream}}-tools\.json
   124  `
   125  
   126  var expectedOutputMirrorsTemplate = expectedOutputCommon + `
   127  .*Writing tools/streams/v1/index2\.json
   128  .*Writing tools/streams/v1/index\.json
   129  .*Writing tools/streams/v1/com\.ubuntu\.juju-{{.Stream}}-tools\.json
   130  .*Writing tools/streams/v1/mirrors\.json
   131  `
   132  
   133  var expectedOutputDirectoryLegacyReleased = "No stream specified, defaulting to released tools in the releases directory.\n" +
   134  	makeExpectedOutput(expectedOutputDirectoryReleasedTemplate, "released", "releases")
   135  
   136  var expectedOutputMirrorsReleased = makeExpectedOutput(expectedOutputMirrorsTemplate, "released", "released")
   137  
   138  func (s *ToolsMetadataSuite) TestGenerateLegacyRelease(c *gc.C) {
   139  	metadataDir := osenv.JujuXDGDataHomeDir() // default metadata dir
   140  	toolstesting.MakeTools(c, metadataDir, "releases", versionStrings)
   141  	ctx := coretesting.Context(c)
   142  	code := cmd.Main(newToolsMetadataCommand(), ctx, nil)
   143  	c.Assert(code, gc.Equals, 0)
   144  	output := ctx.Stdout.(*bytes.Buffer).String()
   145  	c.Assert(output, gc.Matches, expectedOutputDirectoryLegacyReleased)
   146  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   147  	c.Assert(metadata, gc.HasLen, len(versionStrings))
   148  	obtainedVersionStrings := make([]string, len(versionStrings))
   149  	for i, metadata := range metadata {
   150  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   151  		obtainedVersionStrings[i] = s
   152  	}
   153  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   154  }
   155  
   156  func (s *ToolsMetadataSuite) TestGenerateToDirectory(c *gc.C) {
   157  	metadataDir := c.MkDir()
   158  	toolstesting.MakeTools(c, metadataDir, "releases", versionStrings)
   159  	ctx := coretesting.Context(c)
   160  	code := cmd.Main(newToolsMetadataCommand(), ctx, []string{"-d", metadataDir})
   161  	c.Assert(code, gc.Equals, 0)
   162  	output := ctx.Stdout.(*bytes.Buffer).String()
   163  	c.Assert(output, gc.Matches, expectedOutputDirectoryLegacyReleased)
   164  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   165  	c.Assert(metadata, gc.HasLen, len(versionStrings))
   166  	obtainedVersionStrings := make([]string, len(versionStrings))
   167  	for i, metadata := range metadata {
   168  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   169  		obtainedVersionStrings[i] = s
   170  	}
   171  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   172  }
   173  
   174  func (s *ToolsMetadataSuite) TestGenerateStream(c *gc.C) {
   175  	metadataDir := c.MkDir()
   176  	toolstesting.MakeTools(c, metadataDir, "proposed", versionStrings)
   177  	ctx := coretesting.Context(c)
   178  	code := cmd.Main(newToolsMetadataCommand(), ctx, []string{"-d", metadataDir, "--stream", "proposed"})
   179  	c.Assert(code, gc.Equals, 0)
   180  	output := ctx.Stdout.(*bytes.Buffer).String()
   181  	c.Assert(output, gc.Matches, makeExpectedOutput(expectedOutputDirectoryTemplate, "proposed", "proposed"))
   182  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "proposed", false)
   183  	c.Assert(metadata, gc.HasLen, len(versionStrings))
   184  	obtainedVersionStrings := make([]string, len(versionStrings))
   185  	for i, metadata := range metadata {
   186  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   187  		obtainedVersionStrings[i] = s
   188  	}
   189  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   190  }
   191  
   192  func (s *ToolsMetadataSuite) TestGenerateMultipleStreams(c *gc.C) {
   193  	metadataDir := c.MkDir()
   194  	toolstesting.MakeTools(c, metadataDir, "proposed", versionStrings)
   195  	toolstesting.MakeTools(c, metadataDir, "released", currentVersionStrings)
   196  
   197  	ctx := coretesting.Context(c)
   198  	code := cmd.Main(newToolsMetadataCommand(), ctx, []string{"-d", metadataDir, "--stream", "proposed"})
   199  	c.Assert(code, gc.Equals, 0)
   200  	code = cmd.Main(newToolsMetadataCommand(), ctx, []string{"-d", metadataDir, "--stream", "released"})
   201  	c.Assert(code, gc.Equals, 0)
   202  
   203  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "proposed", false)
   204  	c.Assert(metadata, gc.HasLen, len(versionStrings))
   205  	obtainedVersionStrings := make([]string, len(versionStrings))
   206  	for i, metadata := range metadata {
   207  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   208  		obtainedVersionStrings[i] = s
   209  	}
   210  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   211  
   212  	metadata = toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   213  	c.Assert(metadata, gc.HasLen, len(currentVersionStrings))
   214  	obtainedVersionStrings = make([]string, len(currentVersionStrings))
   215  	for i, metadata := range metadata {
   216  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   217  		obtainedVersionStrings[i] = s
   218  	}
   219  	c.Assert(obtainedVersionStrings, gc.DeepEquals, currentVersionStrings)
   220  
   221  	toolstesting.MakeTools(c, metadataDir, "released", versionStrings)
   222  	metadata = toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   223  	c.Assert(metadata, gc.HasLen, len(versionStrings))
   224  	obtainedVersionStrings = make([]string, len(versionStrings))
   225  	for i, metadata := range metadata {
   226  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   227  		obtainedVersionStrings[i] = s
   228  	}
   229  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   230  }
   231  
   232  func (s *ToolsMetadataSuite) TestGenerateDeleteExisting(c *gc.C) {
   233  	metadataDir := c.MkDir()
   234  	toolstesting.MakeTools(c, metadataDir, "proposed", versionStrings)
   235  	toolstesting.MakeTools(c, metadataDir, "released", currentVersionStrings)
   236  
   237  	ctx := coretesting.Context(c)
   238  	code := cmd.Main(newToolsMetadataCommand(), ctx, []string{"-d", metadataDir, "--stream", "proposed"})
   239  	c.Assert(code, gc.Equals, 0)
   240  	code = cmd.Main(newToolsMetadataCommand(), ctx, []string{"-d", metadataDir, "--stream", "released"})
   241  	c.Assert(code, gc.Equals, 0)
   242  
   243  	// Remove existing proposed tarballs, and create some different ones.
   244  	err := os.RemoveAll(filepath.Join(metadataDir, "tools", "proposed"))
   245  	c.Assert(err, jc.ErrorIsNil)
   246  	toolstesting.MakeTools(c, metadataDir, "proposed", currentVersionStrings)
   247  
   248  	// Generate proposed metadata again, using --clean.
   249  	code = cmd.Main(newToolsMetadataCommand(), ctx, []string{"-d", metadataDir, "--stream", "proposed", "--clean"})
   250  	c.Assert(code, gc.Equals, 0)
   251  
   252  	// Proposed metadata should just list the tarballs that were there, not the merged set.
   253  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "proposed", false)
   254  	c.Assert(metadata, gc.HasLen, len(currentVersionStrings))
   255  	obtainedVersionStrings := make([]string, len(currentVersionStrings))
   256  	for i, metadata := range metadata {
   257  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   258  		obtainedVersionStrings[i] = s
   259  	}
   260  	c.Assert(obtainedVersionStrings, gc.DeepEquals, currentVersionStrings)
   261  
   262  	// Released metadata should be untouched.
   263  	metadata = toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   264  	c.Assert(metadata, gc.HasLen, len(currentVersionStrings))
   265  	obtainedVersionStrings = make([]string, len(currentVersionStrings))
   266  	for i, metadata := range metadata {
   267  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   268  		obtainedVersionStrings[i] = s
   269  	}
   270  	c.Assert(obtainedVersionStrings, gc.DeepEquals, currentVersionStrings)
   271  }
   272  
   273  func (s *ToolsMetadataSuite) TestGenerateWithPublicFallback(c *gc.C) {
   274  	// Write tools and metadata to the public tools location.
   275  	toolstesting.MakeToolsWithCheckSum(c, s.publicStorageDir, "released", versionStrings)
   276  
   277  	// Run the command with no local metadata.
   278  	ctx := coretesting.Context(c)
   279  	metadataDir := c.MkDir()
   280  	code := cmd.Main(newToolsMetadataCommand(), ctx, []string{"-d", metadataDir, "--stream", "released"})
   281  	c.Assert(code, gc.Equals, 0)
   282  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   283  	c.Assert(metadata, gc.HasLen, len(versionStrings))
   284  	obtainedVersionStrings := make([]string, len(versionStrings))
   285  	for i, metadata := range metadata {
   286  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   287  		obtainedVersionStrings[i] = s
   288  	}
   289  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   290  }
   291  
   292  func (s *ToolsMetadataSuite) TestGenerateWithMirrors(c *gc.C) {
   293  	metadataDir := c.MkDir()
   294  	toolstesting.MakeTools(c, metadataDir, "released", versionStrings)
   295  	ctx := coretesting.Context(c)
   296  	code := cmd.Main(newToolsMetadataCommand(), ctx, []string{"--public", "-d", metadataDir, "--stream", "released"})
   297  	c.Assert(code, gc.Equals, 0)
   298  	output := ctx.Stdout.(*bytes.Buffer).String()
   299  	c.Assert(output, gc.Matches, expectedOutputMirrorsReleased)
   300  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "released", true)
   301  	c.Assert(metadata, gc.HasLen, len(versionStrings))
   302  	obtainedVersionStrings := make([]string, len(versionStrings))
   303  	for i, metadata := range metadata {
   304  		s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch)
   305  		obtainedVersionStrings[i] = s
   306  	}
   307  	c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings)
   308  }
   309  
   310  func (s *ToolsMetadataSuite) TestNoTools(c *gc.C) {
   311  	if runtime.GOOS == "windows" {
   312  		c.Skip("Skipping on windows, test only set up for Linux tools")
   313  	}
   314  	ctx := coretesting.Context(c)
   315  	code := cmd.Main(newToolsMetadataCommand(), ctx, nil)
   316  	c.Assert(code, gc.Equals, 1)
   317  	stdout := ctx.Stdout.(*bytes.Buffer).String()
   318  	c.Assert(stdout, gc.Matches, ".*\nFinding tools in .*\n")
   319  	stderr := ctx.Stderr.(*bytes.Buffer).String()
   320  	c.Assert(stderr, gc.Matches, "error: no tools available\n")
   321  }
   322  
   323  func (s *ToolsMetadataSuite) TestPatchLevels(c *gc.C) {
   324  	if runtime.GOOS == "windows" {
   325  		c.Skip("Skipping on windows, test only set up for Linux tools")
   326  	}
   327  	currentVersion := jujuversion.Current
   328  	currentVersion.Build = 0
   329  	versionStrings := []string{
   330  		currentVersion.String() + "-precise-amd64",
   331  		currentVersion.String() + ".1-precise-amd64",
   332  	}
   333  	metadataDir := osenv.JujuXDGDataHomeDir() // default metadata dir
   334  	toolstesting.MakeTools(c, metadataDir, "released", versionStrings)
   335  	ctx := coretesting.Context(c)
   336  	code := cmd.Main(newToolsMetadataCommand(), ctx, []string{"--stream", "released"})
   337  	c.Assert(code, gc.Equals, 0)
   338  	output := ctx.Stdout.(*bytes.Buffer).String()
   339  	expectedOutput := fmt.Sprintf(`
   340  Finding tools in .*
   341  .*Fetching tools from dir "released" to generate hash: %s
   342  .*Fetching tools from dir "released" to generate hash: %s
   343  .*Writing tools/streams/v1/index2\.json
   344  .*Writing tools/streams/v1/index\.json
   345  .*Writing tools/streams/v1/com\.ubuntu\.juju-released-tools\.json
   346  `[1:], regexp.QuoteMeta(versionStrings[0]), regexp.QuoteMeta(versionStrings[1]))
   347  	c.Assert(output, gc.Matches, expectedOutput)
   348  	metadata := toolstesting.ParseMetadataFromDir(c, metadataDir, "released", false)
   349  	c.Assert(metadata, gc.HasLen, 2)
   350  
   351  	filename := fmt.Sprintf("juju-%s-precise-amd64.tgz", currentVersion)
   352  	size, sha256 := toolstesting.SHA256sum(c, filepath.Join(metadataDir, "tools", "released", filename))
   353  	c.Assert(metadata[0], gc.DeepEquals, &tools.ToolsMetadata{
   354  		Release:  "precise",
   355  		Version:  currentVersion.String(),
   356  		Arch:     "amd64",
   357  		Size:     size,
   358  		Path:     "released/" + filename,
   359  		FileType: "tar.gz",
   360  		SHA256:   sha256,
   361  	})
   362  
   363  	filename = fmt.Sprintf("juju-%s.1-precise-amd64.tgz", currentVersion)
   364  	size, sha256 = toolstesting.SHA256sum(c, filepath.Join(metadataDir, "tools", "released", filename))
   365  	c.Assert(metadata[1], gc.DeepEquals, &tools.ToolsMetadata{
   366  		Release:  "precise",
   367  		Version:  currentVersion.String() + ".1",
   368  		Arch:     "amd64",
   369  		Size:     size,
   370  		Path:     "released/" + filename,
   371  		FileType: "tar.gz",
   372  		SHA256:   sha256,
   373  	})
   374  }
   375  
   376  func (s *ToolsMetadataSuite) TestToolsDataSourceHasKey(c *gc.C) {
   377  	ds := toolsDataSources("test.me")
   378  	// This data source does not require to contain signed data.
   379  	// However, it may still contain it.
   380  	// Since we will always try to read signed data first,
   381  	// we want to be able to try to read this signed data
   382  	// with public key with Juju-known public key for tools.
   383  	// Bugs #1542127, #1542131
   384  	c.Assert(ds[0].PublicSigningKey(), gc.DeepEquals, keys.JujuPublicKey)
   385  }