func TestModifyResources(t *testing.T) { assert := assert.New(t) version := "1.2.3-test" pair := baseDeployablePair() pair.Prior.Deployment.SourceID.Version = semv.MustParse(version) pair.Prior.Deployment.Resources["memory"] = "100" pair.Post.Deployment.SourceID.Version = semv.MustParse(version) pair.Post.Deployment.Resources["memory"] = "500" pair.Post.BuildArtifact.Name = "1.2.3" mods := make(chan *sous.DeployablePair, 1) errs := make(chan error, 10) client := sous.NewDummyRectificationClient() deployer := NewDeployer(client) mods <- pair close(mods) deployer.RectifyModifies(mods, errs) close(errs) for e := range errs { t.Error(e) } assert.Len(client.Created, 0) if assert.Len(client.Deployed, 1) { assert.Regexp("1.2.3", client.Deployed[0].ImageName) assert.Regexp("500", client.Deployed[0].Res["memory"]) } }
func TestModifyImage(t *testing.T) { assert := assert.New(t) sous.Log.Warn.SetOutput(os.Stderr) Log.Debug.SetOutput(os.Stderr) before := "1.2.3-test" after := "2.3.4-new" pair := baseDeployablePair() pair.Prior.Deployment.SourceID.Version = semv.MustParse(before) pair.Post.Deployment.SourceID.Version = semv.MustParse(after) pair.Post.BuildArtifact.Name = "2.3.4" mods := make(chan *sous.DeployablePair, 1) errs := make(chan error, 10) client := sous.NewDummyRectificationClient() deployer := NewDeployer(client) mods <- pair close(mods) deployer.RectifyModifies(mods, errs) close(errs) for e := range errs { t.Error(e) } assert.Len(client.Created, 0) if assert.Len(client.Deployed, 1) { assert.Regexp("2.3.4", client.Deployed[0].ImageName) } }
func TestModifyScale(t *testing.T) { log.SetFlags(log.Flags() | log.Lshortfile) assert := assert.New(t) mods := make(chan *sous.DeployablePair, 1) errs := make(chan error, 10) pair := baseDeployablePair() pair.Prior.Deployment.DeployConfig.NumInstances = 12 pair.Post.Deployment.DeployConfig.NumInstances = 24 client := sous.NewDummyRectificationClient() deployer := NewDeployer(client) mods <- pair close(mods) deployer.RectifyModifies(mods, errs) close(errs) for e := range errs { t.Error(e) } assert.Len(client.Deployed, 0) if assert.Len(client.Created, 1) { assert.Equal(24, client.Created[0].Count) } }
func TestValidateRepairResources(t *testing.T) { empty := make(Resources) flaws := empty.Validate() assert.Len(t, flaws, 3) flaws, es := RepairAll(flaws) assert.Len(t, flaws, 0) assert.Len(t, es, 0) assert.Equal(t, empty["cpus"], "0.1") assert.Equal(t, empty["memory"], "100") assert.Equal(t, empty["ports"], "1") }
func TestCreates(t *testing.T) { assert := assert.New(t) created := &sous.Deployable{ BuildArtifact: &sous.BuildArtifact{ Type: "docker", Name: "reqid,0.0.0", }, Deployment: &sous.Deployment{ SourceID: sous.SourceID{ Location: sous.SourceLocation{ Repo: "reqid", }, }, DeployConfig: sous.DeployConfig{ NumInstances: 12, }, Cluster: &sous.Cluster{BaseURL: "cluster"}, ClusterName: "nick", }, } crts := make(chan *sous.Deployable, 1) errs := make(chan error, 10) client := sous.NewDummyRectificationClient() deployer := NewDeployer(client) crts <- created close(crts) deployer.RectifyCreates(crts, errs) close(errs) for e := range errs { t.Error(e) } if assert.Len(client.Deployed, 1) { dep := client.Deployed[0] assert.Equal("cluster", dep.Cluster) assert.Equal("reqid,0.0.0", dep.ImageName) } if assert.Len(client.Created, 1) { req := client.Created[0] assert.Equal("cluster", req.Cluster) assert.Equal("reqid::nick", req.ID) assert.Equal(12, req.Count) } }
func TestDeletes(t *testing.T) { assert := assert.New(t) deleted := &sous.Deployable{ Deployment: &sous.Deployment{ SourceID: sous.SourceID{ Location: sous.SourceLocation{ Repo: "reqid", }, }, DeployConfig: sous.DeployConfig{ NumInstances: 12, }, ClusterName: "", Cluster: &sous.Cluster{ BaseURL: "cluster", }, }, } dels := make(chan *sous.Deployable, 1) errs := make(chan error, 10) client := sous.NewDummyRectificationClient() deployer := NewDeployer(client) dels <- deleted close(dels) deployer.RectifyDeletes(dels, errs) close(errs) for e := range errs { t.Error(e) } assert.Len(client.Deployed, 0) assert.Len(client.Created, 0) // We no longer expect any deletions; See deployer.RectifySingleDelete. //expectedDeletions := 1 expectedDeletions := 0 if assert.Len(client.Deleted, expectedDeletions) { // We no longer expect any deletions; See deployer.RectifySingleDelete. //req := client.Deleted[0] //assert.Equal("cluster", req.Cluster) //assert.Equal("reqid::", req.Reqid) } }
func TestRealDiff(t *testing.T) { log.SetFlags(log.Flags() | log.Lshortfile) assert := assert.New(t) intended := NewDeployments() existing := NewDeployments() repoOne := "https://github.com/opentable/one" repoTwo := "https://github.com/opentable/two" repoThree := "https://github.com/opentable/three" repoFour := "https://github.com/opentable/four" intended.MustAdd(makeDepl(repoOne, 1)) //remove existing.MustAdd(makeDepl(repoTwo, 1)) //same intended.MustAdd(makeDepl(repoTwo, 1)) //same existing.MustAdd(makeDepl(repoThree, 1)) //changed intended.MustAdd(makeDepl(repoThree, 2)) //changed existing.MustAdd(makeDepl(repoFour, 1)) //create dc := intended.Diff(existing) ds := dc.collect() if assert.Len(ds.Gone.Snapshot(), 1, "Should have one deleted item.") { it, _ := ds.Gone.Any(func(*Deployment) bool { return true }) assert.Equal(string(it.SourceID.Location.Repo), repoOne) } if assert.Len(ds.Same.Snapshot(), 1, "Should have one unchanged item.") { it, _ := ds.Same.Any(func(*Deployment) bool { return true }) assert.Equal(string(it.SourceID.Location.Repo), repoTwo) } if assert.Len(ds.Changed, 1, "Should have one modified item.") { assert.Equal(repoThree, string(ds.Changed[0].name.ManifestID.Source.Repo)) assert.Equal(repoThree, string(ds.Changed[0].Prior.SourceID.Location.Repo)) assert.Equal(repoThree, string(ds.Changed[0].Post.SourceID.Location.Repo)) assert.Equal(ds.Changed[0].Post.NumInstances, 1) assert.Equal(ds.Changed[0].Prior.NumInstances, 2) } if assert.Equal(ds.New.Len(), 1, "Should have one added item.") { it, _ := ds.New.Any(func(*Deployment) bool { return true }) assert.Equal(string(it.SourceID.Location.Repo), repoFour) } }
func TestBuildDeployment(t *testing.T) { assert := assert.New(t) m := &Manifest{ Source: SourceLocation{}, Owners: []string{"*****@*****.**"}, Kind: ManifestKindService, } sp := DeploySpec{ DeployConfig: DeployConfig{ Resources: Resources{}, Args: []string{}, Env: Env{}, NumInstances: 3, Volumes: Volumes{ &Volume{"h", "c", "RO"}, }, }, Version: semv.MustParse("1.2.3"), clusterName: "cluster.name", } var ih []DeploySpec nick := "cn" state := &State{Defs: Defs{Clusters: Clusters{nick: &Cluster{BaseURL: "http://not"}}}} d, err := BuildDeployment(state, m, nick, sp, ih) if assert.NoError(err) { if assert.Len(d.DeployConfig.Volumes, 1) { assert.Equal("c", d.DeployConfig.Volumes[0].Container) } assert.Equal(nick, d.ClusterName) } }
func TestInvokeHarvest(t *testing.T) { assert := assert.New(t) exe := justCommand(t, []string{`sous`, `harvest`, `sms-continual-test`}) assert.NotNil(exe) assert.Len(exe.Args, 1) }
/* usage: sous config Invoking sous config with no arguments lists all configuration key/value pairs. If you pass just a single argument (a key) sous config will output just the value of that key. You can set a key by providing both a key and a value. usage: sous config [<key> [value]] */ func TestInvokeConfig(t *testing.T) { assert := assert.New(t) exe := justCommand(t, []string{`sous`, `config`}) assert.NotNil(exe) assert.Len(exe.Args, 0) exe = justCommand(t, []string{`sous`, `config`, `x`}) assert.NotNil(exe) assert.Len(exe.Args, 1) exe = justCommand(t, []string{`sous`, `config`, `x`, `7`}) assert.NotNil(exe) assert.Len(exe.Args, 2) }
func TestInvokeUpdate(t *testing.T) { assert := assert.New(t) exe := justCommand(t, []string{`sous`, `update`}) assert.NotNil(exe) assert.Len(exe.Args, 0) }
func TestProductionReady(t *testing.T) { assert := assert.New(t) bc := BuildConfig{ Strict: true, Tag: "1.2.3", Repo: "github.com/opentable/present", Revision: "abcdef", Context: &BuildContext{ Sh: &shell.Sh{}, Source: SourceContext{ RemoteURL: "github.com/opentable/present", RemoteURLs: []string{ "github.com/opentable/present", "github.com/opentable/also", }, Revision: "abcdef", NearestTagName: "1.2.3", NearestTagRevision: "abcdef", Tags: []Tag{ Tag{Name: "1.2.3"}, }, }, }, } ctx := bc.NewContext() assert.Len(ctx.Advisories, 0) assert.NoError(bc.GuardStrict(ctx)) }
/* usage: sous context context prints out sous's view of your current context */ func TestInvokeContext(t *testing.T) { assert := assert.New(t) exe := justCommand(t, []string{`sous`, `context`}) assert.NotNil(exe) assert.Len(exe.Args, 0) }
func TestInvokeRectifyDryruns(t *testing.T) { assert := assert.New(t) require := require.New(t) testDryRun := func(which string) (sous.Deployer, sous.Registry) { exe := justCommand(t, []string{`sous`, `rectify`, `-dry-run`, which, `-repo`, `github.com/somewhere`}) assert.Len(exe.Args, 0) require.IsType(&SousRectify{}, exe.Cmd) rect := exe.Cmd.(*SousRectify) // currently no easy way to tell if the deploy client is live or dummy return nil, rect.Resolver.Registry } _, r := testDryRun("both") assert.IsType(&sous.DummyRegistry{}, r) _, r = testDryRun("none") assert.IsType(&docker.NameCache{}, r) _, r = testDryRun("scheduler") assert.IsType(&docker.NameCache{}, r) _, r = testDryRun("registry") assert.IsType(&sous.DummyRegistry{}, r) }
func TestValidateRepair(t *testing.T) { dc := DeployConfig{ Volumes: Volumes{nil, &Volume{}}, Resources: make(Resources), } dc.Resources["cpus"] = "0.25" dc.Resources["memory"] = "356" dc.Resources["ports"] = "2" assert.Len(t, dc.Volumes, 2) flaws := dc.Validate() assert.Len(t, flaws, 1) fs, es := RepairAll(flaws) assert.Len(t, fs, 0) assert.Len(t, es, 0) assert.Len(t, dc.Volumes, 1) }
func TestModify(t *testing.T) { Log.Debug.SetOutput(os.Stderr) defer Log.Debug.SetOutput(ioutil.Discard) assert := assert.New(t) before := "1.2.3-test" after := "2.3.4-new" pair := baseDeployablePair() pair.Prior.Deployment.SourceID.Version = semv.MustParse(before) pair.Prior.Deployment.DeployConfig.NumInstances = 1 pair.Prior.Deployment.DeployConfig.Volumes = sous.Volumes{{"host", "container", "RO"}} pair.Post.Deployment.SourceID.Version = semv.MustParse(after) pair.Post.Deployment.DeployConfig.NumInstances = 24 pair.Post.Deployment.DeployConfig.Volumes = sous.Volumes{{"host", "container", "RW"}} pair.Post.BuildArtifact.Name = "2.3.4" mods := make(chan *sous.DeployablePair, 1) errs := make(chan error, 10) client := sous.NewDummyRectificationClient() deployer := NewDeployer(client) mods <- pair close(mods) deployer.RectifyModifies(mods, errs) close(errs) for e := range errs { t.Error(e) } if assert.Len(client.Created, 1) { assert.Equal(24, client.Created[0].Count) } if assert.Len(client.Deployed, 1) { assert.Regexp("2.3.4", client.Deployed[0].ImageName) log.Print(client.Deployed[0].Vols) assert.Equal("RW", string(client.Deployed[0].Vols[0].Mode)) } }
func TestOwnerSet(t *testing.T) { set := NewOwnerSet("one") set.Add("two") set.Add("one") slice := set.Slice() assert.Len(t, slice, 2) assert.Contains(t, slice, "one") assert.Contains(t, slice, "two") assert.True(t, set.Equal(NewOwnerSet("two", "one"))) }
func TestHandlesManifestPut(t *testing.T) { assert := assert.New(t) require := require.New(t) q, err := url.ParseQuery("repo=gh") require.NoError(err) state := sous.NewState() state.Manifests.Add(&sous.Manifest{ Source: sous.SourceLocation{Repo: "gh"}, Kind: sous.ManifestKindService, }) writer := graph.StateWriter{StateWriter: &sous.DummyStateManager{State: state}} manifest := &sous.Manifest{ Source: sous.SourceLocation{Repo: "gh"}, Owners: []string{"sam", "judson"}, Kind: sous.ManifestKindService, } buf := &bytes.Buffer{} enc := json.NewEncoder(buf) enc.Encode(manifest) req, err := http.NewRequest("PUT", "", buf) require.NoError(err) th := &PUTManifestHandler{ Request: req, StateWriter: writer, State: state, QueryValues: &QueryValues{q}, } data, status := th.Exchange() assert.Equal(status, 200) require.IsType(&sous.Manifest{}, data) assert.Len(data.(*sous.Manifest).Owners, 2) assert.Equal(data.(*sous.Manifest).Owners[1], "judson") changed, found := state.Manifests.Get(sous.ManifestID{Source: sous.SourceLocation{Repo: "gh"}}) require.True(found) assert.Len(changed.Owners, 2) assert.Equal(changed.Owners[1], "judson") }
func TestHandlesGDMGet(t *testing.T) { assert := assert.New(t) th := &GDMHandler{graph.CurrentGDM{ Deployments: sous.NewDeployments(), }} data, status := th.Exchange() assert.Equal(status, 200) assert.Len(data.(gdmWrapper).Deployments, 0) }
func TestInvokeDeploy(t *testing.T) { assert := assert.New(t) require := require.New(t) exe := justCommand(t, []string{`sous`, `deploy`, `-cluster`, `ci-sf`, `-tag`, `1.2.3`}) assert.NotNil(exe) assert.Len(exe.Args, 0) deploy, good := exe.Cmd.(*SousDeploy) require.True(good) assert.Equal(deploy.DeployFilterFlags.Cluster, `ci-sf`) assert.Equal(deploy.DeployFilterFlags.Tag, `1.2.3`) }
func TestReharvest(t *testing.T) { assert := assert.New(t) dc := docker_registry.NewDummyClient() _, err := dc.GetImageMetadata("", "") assert.Error(err) //because channel starved host := "docker.repo.io" base := "ot/wackadoo" nc := NewNameCache(host, dc, inMemoryDB("reharvest")) vstr := "1.2.3" sv := sous.MustNewSourceID("https://github.com/opentable/wackadoo", "nested/there", vstr) in := base + ":version-" + vstr digest := "sha256:012345678901234567890123456789AB012345678901234567890123456789AB" cn := base + "@" + digest dc.FeedMetadata(docker_registry.Metadata{ Registry: host, Labels: Labels(sv), Etag: digest, CanonicalName: cn, AllNames: []string{cn, in}, }) gotSV, err := nc.GetSourceID(NewBuildArtifact(host+"/"+in, nil)) // XXX Really prefix with host? if assert.Nil(err) { assert.Equal(gotSV, sv) } nc.dump(os.Stderr) nc.DB.Exec("update _database_metadata_ set value='' where name='fingerprint'") dc.FeedTags([]string{"version" + vstr}) dc.FeedMetadata(docker_registry.Metadata{ Registry: host, Labels: Labels(sv), Etag: digest, CanonicalName: cn, AllNames: []string{cn, in}, }) nc.dump(os.Stderr) Log.Debug.SetOutput(os.Stderr) Log.Vomit.SetOutput(os.Stderr) err = nc.GroomDatabase() assert.NoError(err) nc.dump(os.Stderr) _, err = dc.GetImageMetadata("", "") assert.Error(err) //because channel starved list, _ := dc.AllTags("") assert.Len(list, 0) //because channel starved }
/* usage: sous <command> sous is a tool to help speed up the build/test/deploy cycle at your organisation subcommands: build build your project config view and edit sous configuration context show the current build context deploy initialise a new sous project help get help with sous init initialise a new sous project query build your project rectify force Sous to make the deployment match the contents of the local state directory version print the version of sous options: -d debug: output detailed logs of internal operations -q quiet: output only essential error messages -s silent: silence all non-essential output -v loud: output extra info, including all shell commands */ func TestInvokeBareSous(t *testing.T) { assert := assert.New(t) require := require.New(t) log.SetFlags(log.Flags() | log.Lshortfile) c, exe, _, _ := prepareCommand(t, []string{`sous`}) assert.Len(exe.Args, 0) var r cmdr.Result c.InvokeWithoutPrinting([]string{"sous", "help"}) require.NotPanics(func() { r = c.InvokeWithoutPrinting([]string{"sous", "help"}) }) assert.IsType(cmdr.SuccessResult{}, r) }
/* usage: sous build [path] build builds the project in your current directory by default. If you pass it a path, it will instead build the project at that path. options: -offset string source code relative repository offset -repo string source code repository location -revision string source code revision ID -strict require that the build be pristine -tag string source code revision tag */ func TestInvokeBuildWithRepoSelector(t *testing.T) { assert := assert.New(t) _, exe, _, _ := prepareCommand(t, []string{`sous`, `build`, `-repo`, `github.com/opentable/sous`}) assert.Len(exe.Args, 0) build := exe.Cmd.(*SousBuild) assert.NotNil(build.Labeller) assert.NotNil(build.Registrar) assert.Equal(build.DeployFilterFlags.Repo, `github.com/opentable/sous`) }
func TestInvokeRectifyWithoutFilterFlags(t *testing.T) { assert := assert.New(t) require := require.New(t) _, exe, _, _ := prepareCommand(t, []string{`sous`, `rectify`}) assert.Len(exe.Args, 0) require.IsType(&SousRectify{}, exe.Cmd) rect := exe.Cmd.(*SousRectify) assert.NotNil(rect.Config) assert.NotNil(rect.GDM) require.NotNil(rect.SourceFlags) assert.Equal(rect.SourceFlags.All, false) require.NotNil(rect.Resolver.ResolveFilter) assert.Equal(rect.Resolver.ResolveFilter.All(), true) }
func TestParseTags(t *testing.T) { assert := assert.New(t) lines := []string{ "2f381f35ffcf57b21b4c2991635f7f825c29f003 2016-08-02T11:16:04-04:00 HEAD -> master, tag: 0.2.0, origin/master, origin/HEAD", "0a271b999974db8f37326e16c9027436098b251f 2016-08-01T10:54:53-04:00 tag: 0.1.6", "a83cebbdb1e06bc325f88d953120ac60dead7268 2016-07-26T15:32:24-04:00 tag: 0.1.5", "65017e8bcbeaf10d48ea677113a3d5d99ed03c45 2016-07-12T14:23:03-04:00 tag: 0.1.4", "6013d74276ac6a62f8fec3aee7c06162496b25a5 2016-07-12T13:14:52-04:00 tag: 0.1.3", "b779b60a13f0a3b8edbecf81b0b24b252202b3a2 2016-07-11T16:00:14-04:00 tag: 0.1.2", "612eec227ccdcdbe9d182ce830adb566930ca0c0 2016-07-07T14:41:18-04:00 2f381f35ffcf57b21b4c2991635f7f825c29f003", } tags := (&Client{}).parseTags(lines) assert.Len(tags, 6) assert.Contains(tags, sous.Tag{Name: "0.2.0", Revision: "2f381f35ffcf57b21b4c2991635f7f825c29f003"}) }
func TestInvokeRectifyWithDebugFlags(t *testing.T) { assert := assert.New(t) require := require.New(t) _, exe, _, stderr := prepareCommand(t, []string{`sous`, `rectify`, `-d`, `-v`, `-all`}) assert.Len(exe.Args, 0) require.IsType(&SousRectify{}, exe.Cmd) rect := exe.Cmd.(*SousRectify) assert.NotNil(rect.Config) assert.NotNil(rect.GDM) require.NotNil(rect.SourceFlags) assert.Equal(rect.SourceFlags.All, true) assert.Regexp(`Verbose debugging`, stderr.String()) assert.Regexp(`Regular debugging`, stderr.String()) }
func TestClusterMap(t *testing.T) { assert := assert.New(t) s := State{ Defs: Defs{ Clusters: Clusters{ "one": &Cluster{}, "two": &Cluster{}, }, }, } m := s.ClusterMap() assert.Len(m, 2) assert.Contains(m, "one") assert.Contains(m, "two") }
func TestHarvestGuessedRepo(t *testing.T) { assert := assert.New(t) dc := docker_registry.NewDummyClient() host := "docker.repo.io" nc := NewNameCache(host, dc, inMemoryDB("guessed_repo")) sl := sous.SourceLocation{ Repo: "https://github.com/opentable/wackadoo", Dir: "nested/there", } dc.FeedTags([]string{"something", "the other"}) nc.harvest(sl) remainingTags, err := dc.AllTags("") assert.NoError(err) assert.Len(remainingTags, 0) // because all consumed by harvest }
func sameYAML(t *testing.T, actual *sous.State, expected *sous.State) { assert := assert.New(t) require := require.New(t) actualManifests := actual.Manifests.Snapshot() expectedManifests := expected.Manifests.Snapshot() assert.Len(actualManifests, len(expectedManifests)) for mid, manifest := range expectedManifests { actual := *actualManifests[mid] assert.Contains(actualManifests, mid) if !assert.Equal(actual, *manifest) { _, differences := actual.Diff(manifest) t.Logf("DIFFERENCES (%q): %#v", mid, differences) } } actualYAML, err := yaml.Marshal(actual) require.NoError(err) expectedYAML, err := yaml.Marshal(expected) require.NoError(err) assert.Equal(actualYAML, expectedYAML) }
func TestBuildDeployments(t *testing.T) { t.Skipf("Failing test on master preventing progress on other stories.") assert := assert.New(t) sous.Log.Debug.SetOutput(os.Stdout) ResetSingularity() defer ResetSingularity() drc := docker_registry.NewClient() drc.BecomeFoolishlyTrusting() db, err := docker.GetDatabase(&docker.DBConfig{ Driver: "sqlite3_sous", Connection: docker.InMemoryConnection("testresolve"), }) if err != nil { panic(err) } appLocation := "testhelloreq" clusterNick := "tcluster" reqID := appLocation + clusterNick nc := docker.NewNameCache("", drc, db) singCl := sing.NewClient(SingularityURL) //singCl.Debug = true sr, err := singReqDep( SingularityURL, whitespace.CleanWS(` { "instances": 1, "id": "`+reqID+`", "requestType": "SERVICE", "owners": ["*****@*****.**", "*****@*****.**"] }`), whitespace.CleanWS(` { "deploy": { "id": "`+singularity.MakeDeployID(uuid.NewV4().String())+`", "requestId": "`+reqID+`", "resources": { "cpus": 0.1, "memoryMb": 32, "numPorts": 1 }, "containerInfo": { "type": "DOCKER", "docker": { "image": "`+BuildImageName("hello-server-labels", "latest")+`" }, "volumes": [{"hostPath":"/tmp", "containerPath":"/tmp","mode":"RO"}] }, "env": { "TEST": "yes" } } }`), ) req := singularity.SingReq{ SourceURL: SingularityURL, Sing: singCl, ReqParent: sr, } if assert.NoError(err) { clusters := sous.Clusters{clusterNick: {BaseURL: SingularityURL}} dep, err := singularity.BuildDeployment(nc, clusters, req) if assert.NoError(err) { if assert.Len(dep.DeployConfig.Volumes, 1) { assert.Equal(dep.DeployConfig.Volumes[0].Host, "/tmp") } assert.Equal("github.com/docker/dockercloud-hello-world", dep.SourceID.Location.Repo) } } }