2018-08-27 19:10:55 +02:00
|
|
|
package job
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2018-09-23 21:08:03 +02:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2019-03-20 23:01:24 +01:00
|
|
|
|
2018-09-08 07:03:41 +02:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
2019-03-22 19:41:12 +01:00
|
|
|
|
2018-08-27 19:10:55 +02:00
|
|
|
"github.com/zrepl/zrepl/logger"
|
2019-03-20 23:01:24 +01:00
|
|
|
"github.com/zrepl/zrepl/zfs"
|
2018-08-27 19:10:55 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type Logger = logger.Logger
|
|
|
|
|
|
|
|
type contextKey int
|
|
|
|
|
|
|
|
const (
|
|
|
|
contextKeyLog contextKey = iota
|
|
|
|
)
|
|
|
|
|
|
|
|
func GetLogger(ctx context.Context) Logger {
|
|
|
|
if l, ok := ctx.Value(contextKeyLog).(Logger); ok {
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
return logger.NewNullLogger()
|
|
|
|
}
|
|
|
|
|
|
|
|
func WithLogger(ctx context.Context, l Logger) context.Context {
|
|
|
|
return context.WithValue(ctx, contextKeyLog, l)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Job interface {
|
|
|
|
Name() string
|
|
|
|
Run(ctx context.Context)
|
2018-09-23 21:08:03 +02:00
|
|
|
Status() *Status
|
2018-09-08 07:03:41 +02:00
|
|
|
RegisterMetrics(registerer prometheus.Registerer)
|
2019-03-20 23:01:24 +01:00
|
|
|
// Jobs that return a subtree of the dataset hierarchy
|
|
|
|
// must return the root of that subtree as rfs and ok = true
|
|
|
|
OwnedDatasetSubtreeRoot() (rfs *zfs.DatasetPath, ok bool)
|
2018-08-27 19:10:55 +02:00
|
|
|
}
|
|
|
|
|
2018-09-23 21:08:03 +02:00
|
|
|
type Type string
|
|
|
|
|
|
|
|
const (
|
|
|
|
TypeInternal Type = "internal"
|
2019-03-22 19:41:12 +01:00
|
|
|
TypeSnap Type = "snap"
|
|
|
|
TypePush Type = "push"
|
|
|
|
TypeSink Type = "sink"
|
|
|
|
TypePull Type = "pull"
|
|
|
|
TypeSource Type = "source"
|
2018-09-23 21:08:03 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type Status struct {
|
2019-03-22 19:41:12 +01:00
|
|
|
Type Type
|
2018-09-23 21:08:03 +02:00
|
|
|
JobSpecific interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Status) MarshalJSON() ([]byte, error) {
|
|
|
|
typeJson, err := json.Marshal(s.Type)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
jobJSON, err := json.Marshal(s.JobSpecific)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-22 19:41:12 +01:00
|
|
|
m := map[string]json.RawMessage{
|
|
|
|
"type": typeJson,
|
2018-09-23 21:08:03 +02:00
|
|
|
string(s.Type): jobJSON,
|
|
|
|
}
|
|
|
|
return json.Marshal(m)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Status) UnmarshalJSON(in []byte) (err error) {
|
|
|
|
var m map[string]json.RawMessage
|
|
|
|
if err := json.Unmarshal(in, &m); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
tJSON, ok := m["type"]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("field 'type' not found")
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(tJSON, &s.Type); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
key := string(s.Type)
|
|
|
|
jobJSON, ok := m[key]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("field '%s', not found", key)
|
|
|
|
}
|
|
|
|
switch s.Type {
|
2018-11-20 19:30:15 +01:00
|
|
|
case TypeSnap:
|
2018-11-21 03:27:39 +01:00
|
|
|
var st SnapJobStatus
|
|
|
|
err = json.Unmarshal(jobJSON, &st)
|
|
|
|
s.JobSpecific = &st
|
2019-03-22 20:45:27 +01:00
|
|
|
|
2019-03-22 19:41:12 +01:00
|
|
|
case TypePull:
|
|
|
|
fallthrough
|
2018-09-23 21:08:03 +02:00
|
|
|
case TypePush:
|
2018-09-23 23:04:31 +02:00
|
|
|
var st ActiveSideStatus
|
2018-09-23 21:08:03 +02:00
|
|
|
err = json.Unmarshal(jobJSON, &st)
|
|
|
|
s.JobSpecific = &st
|
2019-03-22 20:45:27 +01:00
|
|
|
|
2019-03-22 19:41:12 +01:00
|
|
|
case TypeSource:
|
|
|
|
fallthrough
|
2018-09-23 21:08:03 +02:00
|
|
|
case TypeSink:
|
2018-09-23 23:04:31 +02:00
|
|
|
var st PassiveStatus
|
2018-09-23 21:08:03 +02:00
|
|
|
err = json.Unmarshal(jobJSON, &st)
|
|
|
|
s.JobSpecific = &st
|
2019-03-22 20:45:27 +01:00
|
|
|
|
2018-09-23 21:08:03 +02:00
|
|
|
case TypeInternal:
|
|
|
|
// internal jobs do not report specifics
|
|
|
|
default:
|
|
|
|
err = fmt.Errorf("unknown job type '%s'", key)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|