func checkDo(ctx context.Context, t *testing.T, task *api.Task, ctlr Controller, expected *api.TaskStatus, expectedErr ...error) *api.TaskStatus { status, err := Do(ctx, task, ctlr) if len(expectedErr) > 0 { assert.Equal(t, expectedErr[0], err) } else { assert.NoError(t, err) } if task.Status.Timestamp != nil { // crazy timestamp validation follows previous, err := gogotypes.TimestampFromProto(task.Status.Timestamp) assert.Nil(t, err) current, err := gogotypes.TimestampFromProto(status.Timestamp) assert.Nil(t, err) if current.Before(previous) { // ensure that the timestamp always proceeds forward t.Fatalf("timestamp must proceed forward: %v < %v", current, previous) } } // if the status and task.Status are different, make sure new timestamp is greater copy := status.Copy() copy.Timestamp = nil // don't check against timestamp assert.Equal(t, expected, copy) return status }
func (k secretSorter) Less(i, j int) bool { iTime, err := gogotypes.TimestampFromProto(k[i].Meta.CreatedAt) if err != nil { panic(err) } jTime, err := gogotypes.TimestampFromProto(k[j].Meta.CreatedAt) if err != nil { panic(err) } return jTime.Before(iTime) }
func (t tasksBySlot) Less(i, j int) bool { // Sort by slot. if t[i].Slot != t[j].Slot { return t[i].Slot < t[j].Slot } // If same slot, sort by most recent. it, err := gogotypes.TimestampFromProto(t[i].Meta.CreatedAt) if err != nil { panic(err) } jt, err := gogotypes.TimestampFromProto(t[j].Meta.CreatedAt) if err != nil { panic(err) } return jt.Before(it) }
// TimestampAgo returns a relatime time string from a timestamp (e.g. "12 seconds ago"). func TimestampAgo(ts *gogotypes.Timestamp) string { if ts == nil { return "" } t, err := gogotypes.TimestampFromProto(ts) if err != nil { panic(err) } return humanize.Time(t) }
func expireBlacklistedCerts(cluster *api.Cluster) { nowMinusGrace := time.Now().Add(-expiredCertGrace) for cn, blacklistedCert := range cluster.BlacklistedCertificates { if blacklistedCert.Expiry == nil { continue } expiry, err := gogotypes.TimestampFromProto(blacklistedCert.Expiry) if err == nil && nowMinusGrace.After(expiry) { delete(cluster.BlacklistedCertificates, cn) } } }
func (c *containerAdapter) logs(ctx context.Context, options api.LogSubscriptionOptions) (io.ReadCloser, error) { apiOptions := types.ContainerLogsOptions{ Follow: options.Follow, Timestamps: true, Details: false, } if options.Since != nil { since, err := gogotypes.TimestampFromProto(options.Since) if err != nil { return nil, err } apiOptions.Since = since.Format(time.RFC3339Nano) } if options.Tail < 0 { // See protobuf documentation for details of how this works. apiOptions.Tail = fmt.Sprint(-options.Tail - 1) } else if options.Tail > 0 { return nil, fmt.Errorf("tail relative to start of logs not supported via docker API") } if len(options.Streams) == 0 { // empty == all apiOptions.ShowStdout, apiOptions.ShowStderr = true, true } else { for _, stream := range options.Streams { switch stream { case api.LogStreamStdout: apiOptions.ShowStdout = true case api.LogStreamStderr: apiOptions.ShowStderr = true } } } return c.client.ContainerLogs(ctx, c.container.name(), apiOptions) }
// unmarshalValue converts/copies a value into the target. // prop may be nil. func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *proto.Properties) error { targetType := target.Type() // Allocate memory for pointer fields. if targetType.Kind() == reflect.Ptr { target.Set(reflect.New(targetType.Elem())) return u.unmarshalValue(target.Elem(), inputValue, prop) } // Handle well-known types. if wkt, ok := target.Addr().Interface().(isWkt); ok { switch wkt.XXX_WellKnownType() { case "DoubleValue", "FloatValue", "Int64Value", "UInt64Value", "Int32Value", "UInt32Value", "BoolValue", "StringValue", "BytesValue": // "Wrappers use the same representation in JSON // as the wrapped primitive type, except that null is allowed." // encoding/json will turn JSON `null` into Go `nil`, // so we don't have to do any extra work. return u.unmarshalValue(target.Field(0), inputValue, prop) case "Any": return fmt.Errorf("unmarshaling Any not supported yet") case "Duration": unq, err := strconv.Unquote(string(inputValue)) if err != nil { return err } d, err := time.ParseDuration(unq) if err != nil { return fmt.Errorf("bad Duration: %v", err) } ns := d.Nanoseconds() s := ns / 1e9 ns %= 1e9 target.Field(0).SetInt(s) target.Field(1).SetInt(ns) return nil case "Timestamp": unq, err := strconv.Unquote(string(inputValue)) if err != nil { return err } t, err := time.Parse(time.RFC3339Nano, unq) if err != nil { return fmt.Errorf("bad Timestamp: %v", err) } target.Field(0).SetInt(int64(t.Unix())) target.Field(1).SetInt(int64(t.Nanosecond())) return nil } } if t, ok := target.Addr().Interface().(*time.Time); ok { ts := &types.Timestamp{} if err := u.unmarshalValue(reflect.ValueOf(ts).Elem(), inputValue, prop); err != nil { return err } tt, err := types.TimestampFromProto(ts) if err != nil { return err } *t = tt return nil } if d, ok := target.Addr().Interface().(*time.Duration); ok { dur := &types.Duration{} if err := u.unmarshalValue(reflect.ValueOf(dur).Elem(), inputValue, prop); err != nil { return err } dd, err := types.DurationFromProto(dur) if err != nil { return err } *d = dd return nil } // Handle enums, which have an underlying type of int32, // and may appear as strings. // The case of an enum appearing as a number is handled // at the bottom of this function. if inputValue[0] == '"' && prop != nil && prop.Enum != "" { vmap := proto.EnumValueMap(prop.Enum) // Don't need to do unquoting; valid enum names // are from a limited character set. s := inputValue[1 : len(inputValue)-1] n, ok := vmap[string(s)] if !ok { return fmt.Errorf("unknown value %q for enum %s", s, prop.Enum) } if target.Kind() == reflect.Ptr { // proto2 target.Set(reflect.New(targetType.Elem())) target = target.Elem() } target.SetInt(int64(n)) return nil } // Handle nested messages. if targetType.Kind() == reflect.Struct { var jsonFields map[string]json.RawMessage if err := json.Unmarshal(inputValue, &jsonFields); err != nil { return err } consumeField := func(prop *proto.Properties) (json.RawMessage, bool) { // Be liberal in what names we accept; both orig_name and camelName are okay. fieldNames := acceptedJSONFieldNames(prop) vOrig, okOrig := jsonFields[fieldNames.orig] vCamel, okCamel := jsonFields[fieldNames.camel] if !okOrig && !okCamel { return nil, false } // If, for some reason, both are present in the data, favour the camelName. var raw json.RawMessage if okOrig { raw = vOrig delete(jsonFields, fieldNames.orig) } if okCamel { raw = vCamel delete(jsonFields, fieldNames.camel) } return raw, true } sprops := proto.GetProperties(targetType) for i := 0; i < target.NumField(); i++ { ft := target.Type().Field(i) if strings.HasPrefix(ft.Name, "XXX_") { continue } valueForField, ok := consumeField(sprops.Prop[i]) if !ok { continue } if err := u.unmarshalValue(target.Field(i), valueForField, sprops.Prop[i]); err != nil { return err } } // Check for any oneof fields. if len(jsonFields) > 0 { for _, oop := range sprops.OneofTypes { raw, ok := consumeField(oop.Prop) if !ok { continue } nv := reflect.New(oop.Type.Elem()) target.Field(oop.Field).Set(nv) if err := u.unmarshalValue(nv.Elem().Field(0), raw, oop.Prop); err != nil { return err } } } if !u.AllowUnknownFields && len(jsonFields) > 0 { // Pick any field to be the scapegoat. var f string for fname := range jsonFields { f = fname break } return fmt.Errorf("unknown field %q in %v", f, targetType) } return nil } // Handle arrays if targetType.Kind() == reflect.Slice { if targetType.Elem().Kind() == reflect.Uint8 { outRef := reflect.New(targetType) outVal := outRef.Interface() //CustomType with underlying type []byte if _, ok := outVal.(interface { UnmarshalJSON([]byte) error }); ok { if err := json.Unmarshal(inputValue, outVal); err != nil { return err } target.Set(outRef.Elem()) return nil } // Special case for encoded bytes. Pre-go1.5 doesn't support unmarshalling // strings into aliased []byte types. // https://github.com/golang/go/commit/4302fd0409da5e4f1d71471a6770dacdc3301197 // https://github.com/golang/go/commit/c60707b14d6be26bf4213114d13070bff00d0b0a var out []byte if err := json.Unmarshal(inputValue, &out); err != nil { return err } target.SetBytes(out) return nil } var slc []json.RawMessage if err := json.Unmarshal(inputValue, &slc); err != nil { return err } len := len(slc) target.Set(reflect.MakeSlice(targetType, len, len)) for i := 0; i < len; i++ { if err := u.unmarshalValue(target.Index(i), slc[i], prop); err != nil { return err } } return nil } // Handle maps (whose keys are always strings) if targetType.Kind() == reflect.Map { var mp map[string]json.RawMessage if err := json.Unmarshal(inputValue, &mp); err != nil { return err } target.Set(reflect.MakeMap(targetType)) var keyprop, valprop *proto.Properties if prop != nil { // These could still be nil if the protobuf metadata is broken somehow. // TODO: This won't work because the fields are unexported. // We should probably just reparse them. //keyprop, valprop = prop.mkeyprop, prop.mvalprop } for ks, raw := range mp { // Unmarshal map key. The core json library already decoded the key into a // string, so we handle that specially. Other types were quoted post-serialization. var k reflect.Value if targetType.Key().Kind() == reflect.String { k = reflect.ValueOf(ks) } else { k = reflect.New(targetType.Key()).Elem() if err := u.unmarshalValue(k, json.RawMessage(ks), keyprop); err != nil { return err } } if !k.Type().AssignableTo(targetType.Key()) { k = k.Convert(targetType.Key()) } // Unmarshal map value. v := reflect.New(targetType.Elem()).Elem() if err := u.unmarshalValue(v, raw, valprop); err != nil { return err } target.SetMapIndex(k, v) } return nil } // 64-bit integers can be encoded as strings. In this case we drop // the quotes and proceed as normal. isNum := targetType.Kind() == reflect.Int64 || targetType.Kind() == reflect.Uint64 if isNum && strings.HasPrefix(string(inputValue), `"`) { inputValue = inputValue[1 : len(inputValue)-1] } // Use the encoding/json for parsing other value types. return json.Unmarshal(inputValue, target.Addr().Interface()) }
func (r *Orchestrator) initTasks(ctx context.Context, readTx store.ReadTx) error { tasks, err := store.FindTasks(readTx, store.All) if err != nil { return err } for _, t := range tasks { if t.NodeID != "" { n := store.GetNode(readTx, t.NodeID) if invalidNode(n) && t.Status.State <= api.TaskStateRunning && t.DesiredState <= api.TaskStateRunning { r.restartTasks[t.ID] = struct{}{} } } } _, err = r.store.Batch(func(batch *store.Batch) error { for _, t := range tasks { if t.ServiceID == "" { continue } // TODO(aluzzardi): We should NOT retrieve the service here. service := store.GetService(readTx, t.ServiceID) if service == nil { // Service was deleted err := batch.Update(func(tx store.Tx) error { return store.DeleteTask(tx, t.ID) }) if err != nil { log.G(ctx).WithError(err).Error("failed to set task desired state to dead") } continue } // TODO(aluzzardi): This is shady. We should have a more generic condition. if t.DesiredState != api.TaskStateReady || !orchestrator.IsReplicatedService(service) { continue } restartDelay := orchestrator.DefaultRestartDelay if t.Spec.Restart != nil && t.Spec.Restart.Delay != nil { var err error restartDelay, err = gogotypes.DurationFromProto(t.Spec.Restart.Delay) if err != nil { log.G(ctx).WithError(err).Error("invalid restart delay") restartDelay = orchestrator.DefaultRestartDelay } } if restartDelay != 0 { timestamp, err := gogotypes.TimestampFromProto(t.Status.Timestamp) if err == nil { restartTime := timestamp.Add(restartDelay) calculatedRestartDelay := restartTime.Sub(time.Now()) if calculatedRestartDelay < restartDelay { restartDelay = calculatedRestartDelay } if restartDelay > 0 { _ = batch.Update(func(tx store.Tx) error { t := store.GetTask(tx, t.ID) // TODO(aluzzardi): This is shady as well. We should have a more generic condition. if t == nil || t.DesiredState != api.TaskStateReady { return nil } r.restarts.DelayStart(ctx, tx, nil, t.ID, restartDelay, true) return nil }) continue } } else { log.G(ctx).WithError(err).Error("invalid status timestamp") } } // Start now err := batch.Update(func(tx store.Tx) error { return r.restarts.StartNow(tx, t.ID) }) if err != nil { log.G(ctx).WithError(err).WithField("task.id", t.ID).Error("moving task out of delayed state failed") } } return nil }) return err }
func printServiceSummary(service *api.Service, running int) { w := tabwriter.NewWriter(os.Stdout, 8, 8, 8, ' ', 0) defer w.Flush() task := service.Spec.Task common.FprintfIfNotEmpty(w, "ID\t: %s\n", service.ID) common.FprintfIfNotEmpty(w, "Name\t: %s\n", service.Spec.Annotations.Name) if len(service.Spec.Annotations.Labels) > 0 { fmt.Fprintln(w, "Labels\t") for k, v := range service.Spec.Annotations.Labels { fmt.Fprintf(w, " %s\t: %s\n", k, v) } } common.FprintfIfNotEmpty(w, "Replicas\t: %s\n", getServiceReplicasTxt(service, running)) if service.UpdateStatus != nil { fmt.Fprintln(w, "Update Status\t") fmt.Fprintln(w, " State\t:", service.UpdateStatus.State) started, err := gogotypes.TimestampFromProto(service.UpdateStatus.StartedAt) if err == nil { fmt.Fprintln(w, " Started\t:", humanize.Time(started)) } if service.UpdateStatus.State == api.UpdateStatus_COMPLETED { completed, err := gogotypes.TimestampFromProto(service.UpdateStatus.CompletedAt) if err == nil { fmt.Fprintln(w, " Completed\t:", humanize.Time(completed)) } } fmt.Fprintln(w, " Message\t:", service.UpdateStatus.Message) } fmt.Fprintln(w, "Template\t") fmt.Fprintln(w, " Container\t") ctr := service.Spec.Task.GetContainer() common.FprintfIfNotEmpty(w, " Image\t: %s\n", ctr.Image) common.FprintfIfNotEmpty(w, " Command\t: %q\n", strings.Join(ctr.Command, " ")) common.FprintfIfNotEmpty(w, " Args\t: [%s]\n", strings.Join(ctr.Args, ", ")) common.FprintfIfNotEmpty(w, " Env\t: [%s]\n", strings.Join(ctr.Env, ", ")) if task.Placement != nil { common.FprintfIfNotEmpty(w, " Constraints\t: %s\n", strings.Join(task.Placement.Constraints, ", ")) } if task.Resources != nil { res := task.Resources fmt.Fprintln(w, " Resources\t") printResources := func(w io.Writer, r *api.Resources) { if r.NanoCPUs != 0 { fmt.Fprintf(w, " CPU\t: %g\n", float64(r.NanoCPUs)/1e9) } if r.MemoryBytes != 0 { fmt.Fprintf(w, " Memory\t: %s\n", humanize.IBytes(uint64(r.MemoryBytes))) } } if res.Reservations != nil { fmt.Fprintln(w, " Reservations:\t") printResources(w, res.Reservations) } if res.Limits != nil { fmt.Fprintln(w, " Limits:\t") printResources(w, res.Limits) } } if len(service.Spec.Task.Networks) > 0 { fmt.Fprintf(w, " Networks:\t") for _, n := range service.Spec.Task.Networks { fmt.Fprintf(w, " %s", n.Target) } } if service.Endpoint != nil && len(service.Endpoint.Ports) > 0 { fmt.Fprintln(w, "\nPorts:") for _, port := range service.Endpoint.Ports { fmt.Fprintf(w, " - Name\t= %s\n", port.Name) fmt.Fprintf(w, " Protocol\t= %s\n", port.Protocol) fmt.Fprintf(w, " Port\t= %d\n", port.TargetPort) fmt.Fprintf(w, " SwarmPort\t= %d\n", port.PublishedPort) } } if len(ctr.Mounts) > 0 { fmt.Fprintln(w, " Mounts:") for _, v := range ctr.Mounts { fmt.Fprintf(w, " - target = %s\n", v.Target) fmt.Fprintf(w, " source = %s\n", v.Source) fmt.Fprintf(w, " readonly = %v\n", v.ReadOnly) fmt.Fprintf(w, " type = %v\n", strings.ToLower(v.Type.String())) } } if len(ctr.Secrets) > 0 { fmt.Fprintln(w, " Secrets:") for _, sr := range ctr.Secrets { var targetName, mode string if sr.GetFile() != nil { targetName = sr.GetFile().Name mode = "FILE" } fmt.Fprintf(w, " [%s] %s@%s:%s\n", mode, sr.SecretName, sr.SecretID, targetName) } } if task.LogDriver != nil { fmt.Fprintf(w, " LogDriver\t: %s\n", task.LogDriver.Name) var keys []string if task.LogDriver.Options != nil { for k := range task.LogDriver.Options { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { v := task.LogDriver.Options[k] if v != "" { fmt.Fprintf(w, " %s\t: %s\n", k, v) } else { fmt.Fprintf(w, " %s\t\n", k) } } } } }
func printLogMessages(msgs ...api.LogMessage) { for _, msg := range msgs { ts, _ := gogotypes.TimestampFromProto(msg.Timestamp) fmt.Printf("%v %v %s\n", msg.Context, ts, string(msg.Data)) } }
resp, err := client.ListSecrets(common.Context(cmd), &api.ListSecretsRequest{}) if err != nil { return err } var output func(*api.Secret) if !quiet { w := tabwriter.NewWriter(os.Stdout, 0, 4, 4, ' ', 0) defer func() { // Ignore flushing errors - there's nothing we can do. _ = w.Flush() }() common.PrintHeader(w, "ID", "Name", "Created") output = func(s *api.Secret) { created, err := gogotypes.TimestampFromProto(s.Meta.CreatedAt) if err != nil { panic(err) } fmt.Fprintf(w, "%s\t%s\t%s\n", s.ID, s.Spec.Annotations.Name, humanize.Time(created), ) } } else { output = func(s *api.Secret) { fmt.Println(s.ID) } } sorted := secretSorter(resp.Secrets)