// MustTimestampProto converts time.Time to a google.protobuf.Timestamp proto. // It panics if input timestamp is invalid. func MustTimestampProto(t time.Time) *gogotypes.Timestamp { ts, err := gogotypes.TimestampProto(t) if err != nil { panic(err.Error()) } return ts }
// touchMeta updates an object's timestamps when necessary and bumps the version // if provided. func touchMeta(meta *api.Meta, version *api.Version) error { // Skip meta update if version is not defined as it means we're applying // from raft or restoring from a snapshot. if version == nil { return nil } now, err := gogotypes.TimestampProto(time.Now()) if err != nil { return err } meta.Version = *version // Updated CreatedAt if not defined if meta.CreatedAt == nil { meta.CreatedAt = now } meta.UpdatedAt = now return nil }
// marshalValue writes the value to the Writer. func (m *Marshaler) marshalValue(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error { v = reflect.Indirect(v) // Handle repeated elements. if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 { out.write("[") comma := "" for i := 0; i < v.Len(); i++ { sliceVal := v.Index(i) out.write(comma) if m.Indent != "" { out.write("\n") out.write(indent) out.write(m.Indent) out.write(m.Indent) } if err := m.marshalValue(out, prop, sliceVal, indent+m.Indent); err != nil { return err } comma = "," } if m.Indent != "" { out.write("\n") out.write(indent) out.write(m.Indent) } out.write("]") return out.err } // Handle well-known types. // Most are handled up in marshalObject (because 99% are messages). if wkt, ok := v.Interface().(isWkt); ok { switch wkt.XXX_WellKnownType() { case "NullValue": out.write("null") return out.err } } if t, ok := v.Interface().(time.Time); ok { ts, err := types.TimestampProto(t) if err != nil { return err } return m.marshalValue(out, prop, reflect.ValueOf(ts), indent) } if d, ok := v.Interface().(time.Duration); ok { dur := types.DurationProto(d) return m.marshalValue(out, prop, reflect.ValueOf(dur), indent) } // Handle enumerations. if !m.EnumsAsInts && prop.Enum != "" { // Unknown enum values will are stringified by the proto library as their // value. Such values should _not_ be quoted or they will be interpreted // as an enum string instead of their value. enumStr := v.Interface().(fmt.Stringer).String() var valStr string if v.Kind() == reflect.Ptr { valStr = strconv.Itoa(int(v.Elem().Int())) } else { valStr = strconv.Itoa(int(v.Int())) } if m, ok := v.Interface().(interface { MarshalJSON() ([]byte, error) }); ok { data, err := m.MarshalJSON() if err != nil { return err } enumStr = string(data) enumStr, err = strconv.Unquote(enumStr) if err != nil { return err } } isKnownEnum := enumStr != valStr if isKnownEnum { out.write(`"`) } out.write(enumStr) if isKnownEnum { out.write(`"`) } return out.err } // Handle nested messages. if v.Kind() == reflect.Struct { i := v if v.CanAddr() { i = v.Addr() } else { i = reflect.New(v.Type()) i.Elem().Set(v) } iface := i.Interface() if iface == nil { out.write(`null`) return out.err } pm, ok := iface.(proto.Message) if !ok { if prop.CustomType == "" { return fmt.Errorf("%v does not implement proto.Message", v.Type()) } t := proto.MessageType(prop.CustomType) if t == nil || !i.Type().ConvertibleTo(t) { return fmt.Errorf("%v declared custom type %s but it is not convertible to %v", v.Type(), prop.CustomType, t) } pm = i.Convert(t).Interface().(proto.Message) } return m.marshalObject(out, pm, indent+m.Indent, "") } // Handle maps. // Since Go randomizes map iteration, we sort keys for stable output. if v.Kind() == reflect.Map { out.write(`{`) keys := v.MapKeys() sort.Sort(mapKeys(keys)) for i, k := range keys { if i > 0 { out.write(`,`) } if m.Indent != "" { out.write("\n") out.write(indent) out.write(m.Indent) out.write(m.Indent) } b, err := json.Marshal(k.Interface()) if err != nil { return err } s := string(b) // If the JSON is not a string value, encode it again to make it one. if !strings.HasPrefix(s, `"`) { b, err := json.Marshal(s) if err != nil { return err } s = string(b) } out.write(s) out.write(`:`) if m.Indent != "" { out.write(` `) } if err := m.marshalValue(out, prop, v.MapIndex(k), indent+m.Indent); err != nil { return err } } if m.Indent != "" { out.write("\n") out.write(indent) out.write(m.Indent) } out.write(`}`) return out.err } // Default handling defers to the encoding/json library. b, err := json.Marshal(v.Interface()) if err != nil { return err } needToQuote := string(b[0]) != `"` && (v.Kind() == reflect.Int64 || v.Kind() == reflect.Uint64) if needToQuote { out.write(`"`) } out.write(string(b)) if needToQuote { out.write(`"`) } return out.err }
func (r *controller) Logs(ctx context.Context, publisher exec.LogPublisher, options api.LogSubscriptionOptions) error { if err := r.checkClosed(); err != nil { return err } if err := r.waitReady(ctx); err != nil { return errors.Wrap(err, "container not ready for logs") } rc, err := r.adapter.logs(ctx, options) if err != nil { return errors.Wrap(err, "failed getting container logs") } defer rc.Close() var ( // use a rate limiter to keep things under control but also provides some // ability coalesce messages. limiter = rate.NewLimiter(rate.Every(time.Second), 10<<20) // 10 MB/s msgctx = api.LogContext{ NodeID: r.task.NodeID, ServiceID: r.task.ServiceID, TaskID: r.task.ID, } ) brd := bufio.NewReader(rc) for { // so, message header is 8 bytes, treat as uint64, pull stream off MSB var header uint64 if err := binary.Read(brd, binary.BigEndian, &header); err != nil { if err == io.EOF { return nil } return errors.Wrap(err, "failed reading log header") } stream, size := (header>>(7<<3))&0xFF, header & ^(uint64(0xFF)<<(7<<3)) // limit here to decrease allocation back pressure. if err := limiter.WaitN(ctx, int(size)); err != nil { return errors.Wrap(err, "failed rate limiter") } buf := make([]byte, size) _, err := io.ReadFull(brd, buf) if err != nil { return errors.Wrap(err, "failed reading buffer") } // Timestamp is RFC3339Nano with 1 space after. Lop, parse, publish parts := bytes.SplitN(buf, []byte(" "), 2) if len(parts) != 2 { return fmt.Errorf("invalid timestamp in log message: %v", buf) } ts, err := time.Parse(time.RFC3339Nano, string(parts[0])) if err != nil { return errors.Wrap(err, "failed to parse timestamp") } tsp, err := gogotypes.TimestampProto(ts) if err != nil { return errors.Wrap(err, "failed to convert timestamp") } if err := publisher.Publish(ctx, api.LogMessage{ Context: msgctx, Timestamp: tsp, Stream: api.LogStream(stream), Data: parts[1], }); err != nil { return errors.Wrap(err, "failed to publish log message") } } }
// RemoveNode removes a Node referenced by NodeID with the given NodeSpec. // - Returns NotFound if the Node is not found. // - Returns FailedPrecondition if the Node has manager role (and is part of the memberlist) or is not shut down. // - Returns InvalidArgument if NodeID or NodeVersion is not valid. // - Returns an error if the delete fails. func (s *Server) RemoveNode(ctx context.Context, request *api.RemoveNodeRequest) (*api.RemoveNodeResponse, error) { if request.NodeID == "" { return nil, grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error()) } err := s.store.Update(func(tx store.Tx) error { node := store.GetNode(tx, request.NodeID) if node == nil { return grpc.Errorf(codes.NotFound, "node %s not found", request.NodeID) } if node.Spec.DesiredRole == api.NodeRoleManager { if s.raft == nil { return grpc.Errorf(codes.FailedPrecondition, "node %s is a manager but cannot access node information from the raft memberlist", request.NodeID) } if member := s.raft.GetMemberByNodeID(request.NodeID); member != nil { return grpc.Errorf(codes.FailedPrecondition, "node %s is a cluster manager and is a member of the raft cluster. It must be demoted to worker before removal", request.NodeID) } } if !request.Force && node.Status.State == api.NodeStatus_READY { return grpc.Errorf(codes.FailedPrecondition, "node %s is not down and can't be removed", request.NodeID) } // lookup the cluster clusters, err := store.FindClusters(tx, store.ByName("default")) if err != nil { return err } if len(clusters) != 1 { return grpc.Errorf(codes.Internal, "could not fetch cluster object") } cluster := clusters[0] blacklistedCert := &api.BlacklistedCertificate{} // Set an expiry time for this RemovedNode if a certificate // exists and can be parsed. if len(node.Certificate.Certificate) != 0 { certBlock, _ := pem.Decode(node.Certificate.Certificate) if certBlock != nil { X509Cert, err := x509.ParseCertificate(certBlock.Bytes) if err == nil && !X509Cert.NotAfter.IsZero() { expiry, err := gogotypes.TimestampProto(X509Cert.NotAfter) if err == nil { blacklistedCert.Expiry = expiry } } } } if cluster.BlacklistedCertificates == nil { cluster.BlacklistedCertificates = make(map[string]*api.BlacklistedCertificate) } cluster.BlacklistedCertificates[node.ID] = blacklistedCert expireBlacklistedCerts(cluster) if err := store.UpdateCluster(tx, cluster); err != nil { return err } return store.DeleteNode(tx, request.NodeID) }) if err != nil { return nil, err } return &api.RemoveNodeResponse{}, nil }