+func (*EC2InstanceSetSuite) TestInstancePriceHistory(c *check.C) {
+ ap, img, cluster, _ := GetInstanceSet(c, "{}")
+ pk, _ := test.LoadTestKey(c, "../../dispatchcloud/test/sshkey_dispatch")
+ tags := cloud.InstanceTags{"arvados-ec2-driver": "test"}
+
+ defer func() {
+ instances, err := ap.Instances(tags)
+ c.Assert(err, check.IsNil)
+ for _, inst := range instances {
+ c.Logf("cleanup: destroy instance %s", inst)
+ c.Check(inst.Destroy(), check.IsNil)
+ }
+ }()
+
+ ap.ec2config.SpotPriceUpdateInterval = arvados.Duration(time.Hour)
+ ap.ec2config.EBSPrice = 0.1 // $/GiB/month
+ inst1, err := ap.Create(cluster.InstanceTypes["tiny-preemptible"], img, tags, "true", pk)
+ c.Assert(err, check.IsNil)
+ defer inst1.Destroy()
+ inst2, err := ap.Create(cluster.InstanceTypes["tiny-preemptible"], img, tags, "true", pk)
+ c.Assert(err, check.IsNil)
+ defer inst2.Destroy()
+
+ // in live mode, we need to wait for the instances to reach
+ // running state before we can discover their availability
+ // zones and look up the appropriate prices.
+ var instances []cloud.Instance
+ for deadline := time.Now().Add(5 * time.Minute); ; {
+ if deadline.Before(time.Now()) {
+ c.Fatal("timed out")
+ }
+ instances, err = ap.Instances(tags)
+ running := 0
+ for _, inst := range instances {
+ ec2i := inst.(*ec2Instance).instance
+ if *ec2i.InstanceLifecycle == "spot" && *ec2i.State.Code&16 != 0 {
+ running++
+ }
+ }
+ if running >= 2 {
+ c.Logf("instances are running, and identifiable as spot instances")
+ break
+ }
+ c.Logf("waiting for instances to reach running state so their availability zone becomes visible...")
+ time.Sleep(10 * time.Second)
+ }
+
+ for _, inst := range instances {
+ hist := inst.PriceHistory(arvados.InstanceType{})
+ c.Logf("%s price history: %v", inst.ID(), hist)
+ c.Check(len(hist) > 0, check.Equals, true)
+
+ histWithScratch := inst.PriceHistory(arvados.InstanceType{AddedScratch: 640 << 30})
+ c.Logf("%s price history with 640 GiB scratch: %v", inst.ID(), histWithScratch)
+
+ for i, ip := range hist {
+ c.Check(ip.Price, check.Not(check.Equals), 0.0)
+ if i > 0 {
+ c.Check(ip.StartTime.Before(hist[i-1].StartTime), check.Equals, true)
+ }
+ c.Check(ip.Price < histWithScratch[i].Price, check.Equals, true)
+ }
+ }
+}
+
+func (*EC2InstanceSetSuite) TestWrapError(c *check.C) {
+ retryError := awserr.New("Throttling", "", nil)
+ wrapped := wrapError(retryError, &atomic.Value{})
+ _, ok := wrapped.(cloud.RateLimitError)
+ c.Check(ok, check.Equals, true)
+
+ quotaError := awserr.New("InstanceLimitExceeded", "", nil)
+ wrapped = wrapError(quotaError, nil)
+ _, ok = wrapped.(cloud.QuotaError)
+ c.Check(ok, check.Equals, true)
+
+ for _, trial := range []struct {
+ code string
+ msg string
+ }{
+ {"InsufficientInstanceCapacity", ""},
+ {"Unsupported", "Your requested instance type (t3.micro) is not supported in your requested Availability Zone (us-east-1e). Please retry your request by not specifying an Availability Zone or choosing us-east-1a, us-east-1b, us-east-1c, us-east-1d, us-east-1f."},
+ } {
+ capacityError := awserr.New(trial.code, trial.msg, nil)
+ wrapped = wrapError(capacityError, nil)
+ caperr, ok := wrapped.(cloud.CapacityError)
+ c.Check(ok, check.Equals, true)
+ c.Check(caperr.IsCapacityError(), check.Equals, true)
+ c.Check(caperr.IsInstanceTypeSpecific(), check.Equals, true)
+ }
+}