Exemple #1
0
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
}
Exemple #2
0
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)
}
Exemple #3
0
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)
}
Exemple #4
0
// 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)
}
Exemple #5
0
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)
		}
	}
}
Exemple #6
0
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)
}
Exemple #7
0
// 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())
}
Exemple #8
0
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
}
Exemple #9
0
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)

				}
			}
		}
	}
}
Exemple #10
0
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))
	}
}
Exemple #11
0
			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)