// ControlWorkSpec makes changes to a work spec that are not directly // reflected in the work spec definition. This allows work specs to // be paused or to stop generating new continuous jobs. // ControlWorkSpecOptions has a complete listing of what can be done. func (jobs *JobServer) ControlWorkSpec(workSpecName string, options map[string]interface{}) (bool, string, error) { var ( cwsOptions ControlWorkSpecOptions decoder *mapstructure.Decoder err error metadata mapstructure.Metadata workSpec coordinate.WorkSpec wsMeta coordinate.WorkSpecMeta ) workSpec, err = jobs.Namespace.WorkSpec(workSpecName) if err == nil { // We care a lot about "false" vs. not present for // these things. Manually create the decoder. config := mapstructure.DecoderConfig{ Result: &cwsOptions, Metadata: &metadata, } decoder, err = mapstructure.NewDecoder(&config) } if err == nil { err = decoder.Decode(options) } // Get the existing metadata, then change it based on what // we got provided if err == nil { wsMeta, err = workSpec.Meta(false) } if err == nil { for _, key := range metadata.Keys { switch key { case "Continuous": wsMeta.Continuous = cwsOptions.Continuous case "Status": wsMeta.Paused = cwsOptions.Status == Paused case "Weight": wsMeta.Weight = cwsOptions.Weight case "Interval": wsMeta.Interval = time.Duration(cwsOptions.Interval) * time.Second case "MaxRunning": wsMeta.MaxRunning = cwsOptions.MaxRunning } } } if err == nil { err = workSpec.SetMeta(wsMeta) } return err == nil, "", err }
// TestMetaContinuous specifically checks that you cannot enable the // "continuous" flag on non-continuous work specs. func TestMetaContinuous(t *testing.T) { var ( err error namespace coordinate.Namespace spec coordinate.WorkSpec meta coordinate.WorkSpecMeta ) namespace, err = Coordinate.Namespace("TestMetaContinuous") if !assert.NoError(t, err) { return } defer namespace.Destroy() spec, err = namespace.SetWorkSpec(map[string]interface{}{ "name": "spec", "min_gb": 1, }) if !assert.NoError(t, err) { return } meta, err = spec.Meta(false) if assert.NoError(t, err) { assert.False(t, meta.Continuous) assert.False(t, meta.CanBeContinuous) } meta.Continuous = true err = spec.SetMeta(meta) assert.NoError(t, err) meta, err = spec.Meta(false) if assert.NoError(t, err) { // Cannot set the "continuous" flag assert.False(t, meta.Continuous) assert.False(t, meta.CanBeContinuous) } }
// GetWorkSpecMeta returns a set of control options for a given work // spec. The returned map has the full set of keys that // ControlWorkSpec() will accept. func (jobs *JobServer) GetWorkSpecMeta(workSpecName string) (result map[string]interface{}, _ string, err error) { var ( workSpec coordinate.WorkSpec meta coordinate.WorkSpecMeta ) workSpec, err = jobs.Namespace.WorkSpec(workSpecName) if err == nil { meta, err = workSpec.Meta(false) } if err == nil { result = make(map[string]interface{}) if meta.Paused { result["status"] = Paused } else { result["status"] = Runnable } result["continuous"] = meta.Continuous result["interval"] = meta.Interval.Seconds() result["max_running"] = meta.MaxRunning result["weight"] = meta.Weight } return }
// TestSetMeta tests the basic SetMeta() call and a couple of its // documented oddities. func TestSetMeta(t *testing.T) { var ( err error namespace coordinate.Namespace spec coordinate.WorkSpec meta coordinate.WorkSpecMeta ) namespace, err = Coordinate.Namespace("TestSetMeta") if !assert.NoError(t, err) { return } defer namespace.Destroy() spec, err = namespace.SetWorkSpec(map[string]interface{}{ "name": "spec", "min_gb": 1, "continuous": true, }) if !assert.NoError(t, err) { return } meta, err = spec.Meta(false) if assert.NoError(t, err) { assert.Equal(t, 0, meta.Priority) assert.Equal(t, 20, meta.Weight) assert.False(t, meta.Paused) assert.True(t, meta.Continuous) assert.True(t, meta.CanBeContinuous) assert.Zero(t, meta.Interval) assert.WithinDuration(t, time.Time{}, meta.NextContinuous, 1*time.Microsecond) assert.Equal(t, 0, meta.MaxRunning) assert.Equal(t, 0, meta.MaxAttemptsReturned) assert.Equal(t, "", meta.NextWorkSpecName) assert.Equal(t, 0, meta.AvailableCount) assert.Equal(t, 0, meta.PendingCount) assert.Equal(t, "", meta.Runtime) } err = spec.SetMeta(coordinate.WorkSpecMeta{ Priority: 10, Weight: 100, Paused: true, Continuous: false, CanBeContinuous: false, Interval: time.Duration(60) * time.Second, MaxRunning: 10, MaxAttemptsReturned: 1, NextWorkSpecName: "then", AvailableCount: 100, PendingCount: 50, Runtime: "go", }) assert.NoError(t, err) meta, err = spec.Meta(false) if assert.NoError(t, err) { assert.Equal(t, 10, meta.Priority) assert.Equal(t, 100, meta.Weight) assert.True(t, meta.Paused) assert.False(t, meta.Continuous) // Cannot clear "can be continuous" flag assert.True(t, meta.CanBeContinuous) assert.Equal(t, 60*time.Second, meta.Interval) assert.WithinDuration(t, time.Time{}, meta.NextContinuous, 1*time.Microsecond) assert.Equal(t, 10, meta.MaxRunning) assert.Equal(t, 1, meta.MaxAttemptsReturned) // Cannot change following work spec assert.Equal(t, "", meta.NextWorkSpecName) // Cannot set the counts assert.Equal(t, 0, meta.AvailableCount) assert.Equal(t, 0, meta.PendingCount) // Cannot change the language runtime assert.Equal(t, "", meta.Runtime) } }