diff --git a/api/Makefile b/api/Makefile index 62def0e..d63ac85 100644 --- a/api/Makefile +++ b/api/Makefile @@ -6,6 +6,7 @@ proto: # client for cli protoc -I meta --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go_out=../cli/repository/meta/v1 --go-grpc_out=../cli/repository/meta/v1 meta/meta.proto protoc -I feature --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go_out=../cli/repository/feature/v1 --go-grpc_out=../cli/repository/feature/v1 feature/feature.proto + protoc -I workload --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go_out=../cli/repository/workload/v1 --go-grpc_out=../cli/repository/workload/v1 workload/workload.proto # client for UI protoc -I meta --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go_out=../ui/repository/meta//v1 --go-grpc_out=../ui/repository/meta/v1 meta/meta.proto protoc -I feature --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go_out=../ui/repository/feature/v1 --go-grpc_out=../ui/repository/feature/v1 feature/feature.proto diff --git a/api/workload/workload.proto b/api/workload/workload.proto index ace8900..3f78878 100644 --- a/api/workload/workload.proto +++ b/api/workload/workload.proto @@ -25,6 +25,23 @@ message RestartResponse { string message = 2; } +// ServiceInfo contains information about the configured restart service +message ServiceInfo { + bool enabled = 1; + WorkloadType type = 2; + string name = 3; +} + +// InfoRequest is an empty request for getting service info +message InfoRequest { +} + +// SimpleRestartRequest is an empty request that uses configured values +message SimpleRestartRequest { +} + service Workload { rpc RestartWorkload(RestartRequest) returns (RestartResponse); + rpc Info(InfoRequest) returns (ServiceInfo); + rpc Restart(SimpleRestartRequest) returns (RestartResponse); } diff --git a/charts/feature/templates/service-configmap.yaml b/charts/feature/templates/service-configmap.yaml index 0562c4e..093c5aa 100644 --- a/charts/feature/templates/service-configmap.yaml +++ b/charts/feature/templates/service-configmap.yaml @@ -15,4 +15,7 @@ data: OPENTELEMETRY_ENDPOINT: {{ .Values.cli.opentelemetry.endpoint | quote }} NOTIFICATION_ENABLED: {{ ternary "true" "false" .Values.service.notification.enabled | quote }} NOTIFICATION_TYPE: {{ .Values.service.notification.type | quote }} + RESTART_ENABLED: {{ ternary "true" "false" .Values.service.restart.enabled | quote }} + RESTART_TYPE: {{ .Values.service.restart.type | quote }} + RESTART_NAME: {{ .Values.service.restart.name | quote }} {{- end }} diff --git a/charts/feature/values.yaml b/charts/feature/values.yaml index 88bd8b5..5f5bd4b 100644 --- a/charts/feature/values.yaml +++ b/charts/feature/values.yaml @@ -53,6 +53,10 @@ service: notification: enabled: false type: "" + restart: + enabled: false + type: deployment + name: "" cli: # Deploy the feature CLI diff --git a/cli/command/command.go b/cli/command/command.go index b3306fa..14c352b 100644 --- a/cli/command/command.go +++ b/cli/command/command.go @@ -3,6 +3,7 @@ package command import ( "github.com/dkrizic/feature/cli/constant" feature "github.com/dkrizic/feature/cli/repository/feature/v1" + workload "github.com/dkrizic/feature/cli/repository/workload/v1" "github.com/urfave/cli/v3" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" @@ -23,3 +24,18 @@ func FeatureClient(cmd *cli.Command) (feature.FeatureClient, error) { fc := feature.NewFeatureClient(gc) return fc, nil } + +func WorkloadClient(cmd *cli.Command) (workload.WorkloadClient, error) { + endpoint := cmd.String(constant.Endpoint) + + gc, err := grpc.NewClient(endpoint, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithStatsHandler(otelgrpc.NewClientHandler()), + ) + if err != nil { + return nil, err + } + + wc := workload.NewWorkloadClient(gc) + return wc, nil +} diff --git a/cli/command/info/info.go b/cli/command/info/info.go new file mode 100644 index 0000000..9d5d766 --- /dev/null +++ b/cli/command/info/info.go @@ -0,0 +1,38 @@ +package info + +import ( + "context" + "fmt" + "log/slog" + + "github.com/dkrizic/feature/cli/command" + workload "github.com/dkrizic/feature/cli/repository/workload/v1" + "github.com/urfave/cli/v3" + "go.opentelemetry.io/otel" +) + +func Info(ctx context.Context, cmd *cli.Command) error { + ctx, span := otel.Tracer("cli/command/info").Start(ctx, "Info") + defer span.End() + + wc, err := command.WorkloadClient(cmd) + if err != nil { + return err + } + + slog.InfoContext(ctx, "Getting service info") + result, err := wc.Info(ctx, &workload.InfoRequest{}) + if err != nil { + return err + } + + // Format output + output := fmt.Sprintf("Restart enabled: %t\n", result.Enabled) + if result.Enabled { + output += fmt.Sprintf("Restart type: %s\n", result.Type.String()) + output += fmt.Sprintf("Restart name: %s\n", result.Name) + } + + cmd.Writer.Write([]byte(output)) + return nil +} diff --git a/cli/command/restart/restart.go b/cli/command/restart/restart.go new file mode 100644 index 0000000..92dde1d --- /dev/null +++ b/cli/command/restart/restart.go @@ -0,0 +1,37 @@ +package restart + +import ( + "context" + "fmt" + "log/slog" + + "github.com/dkrizic/feature/cli/command" + workload "github.com/dkrizic/feature/cli/repository/workload/v1" + "github.com/urfave/cli/v3" + "go.opentelemetry.io/otel" +) + +func Restart(ctx context.Context, cmd *cli.Command) error { + ctx, span := otel.Tracer("cli/command/restart").Start(ctx, "Restart") + defer span.End() + + wc, err := command.WorkloadClient(cmd) + if err != nil { + return err + } + + slog.InfoContext(ctx, "Restarting configured service") + result, err := wc.Restart(ctx, &workload.SimpleRestartRequest{}) + if err != nil { + return err + } + + if result.Success { + cmd.Writer.Write([]byte(fmt.Sprintf("✓ %s\n", result.Message))) + } else { + cmd.Writer.Write([]byte(fmt.Sprintf("✗ %s\n", result.Message))) + return fmt.Errorf("restart failed: %s", result.Message) + } + + return nil +} diff --git a/cli/main.go b/cli/main.go index 13e3fd3..fa2a69b 100644 --- a/cli/main.go +++ b/cli/main.go @@ -11,7 +11,9 @@ import ( "github.com/dkrizic/feature/cli/command/delete" "github.com/dkrizic/feature/cli/command/get" "github.com/dkrizic/feature/cli/command/getall" + "github.com/dkrizic/feature/cli/command/info" "github.com/dkrizic/feature/cli/command/preset" + "github.com/dkrizic/feature/cli/command/restart" "github.com/dkrizic/feature/cli/command/set" "github.com/dkrizic/feature/cli/constant" "github.com/dkrizic/feature/cli/meta" @@ -139,6 +141,16 @@ func main() { }, }, }, + &cli.Command{ + Name: "info", + Usage: "Get service info including restart configuration", + Action: info.Info, + }, + &cli.Command{ + Name: "restart", + Usage: "Restart the configured service", + Action: restart.Restart, + }, }, } diff --git a/cli/repository/feature/v1/feature.pb.go b/cli/repository/feature/v1/feature.pb.go index 37b0134..14fa061 100644 --- a/cli/repository/feature/v1/feature.pb.go +++ b/cli/repository/feature/v1/feature.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.33.2 +// protoc-gen-go v1.36.11 +// protoc v3.21.12 // source: feature.proto package featurev1 diff --git a/cli/repository/feature/v1/feature_grpc.pb.go b/cli/repository/feature/v1/feature_grpc.pb.go index 60893f3..dbada6d 100644 --- a/cli/repository/feature/v1/feature_grpc.pb.go +++ b/cli/repository/feature/v1/feature_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v6.33.2 +// - protoc-gen-go-grpc v1.6.0 +// - protoc v3.21.12 // source: feature.proto package featurev1 @@ -125,19 +125,19 @@ type FeatureServer interface { type UnimplementedFeatureServer struct{} func (UnimplementedFeatureServer) GetAll(*emptypb.Empty, grpc.ServerStreamingServer[KeyValue]) error { - return status.Errorf(codes.Unimplemented, "method GetAll not implemented") + return status.Error(codes.Unimplemented, "method GetAll not implemented") } func (UnimplementedFeatureServer) PreSet(context.Context, *KeyValue) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method PreSet not implemented") + return nil, status.Error(codes.Unimplemented, "method PreSet not implemented") } func (UnimplementedFeatureServer) Set(context.Context, *KeyValue) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Set not implemented") + return nil, status.Error(codes.Unimplemented, "method Set not implemented") } func (UnimplementedFeatureServer) Get(context.Context, *Key) (*Value, error) { - return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") + return nil, status.Error(codes.Unimplemented, "method Get not implemented") } func (UnimplementedFeatureServer) Delete(context.Context, *Key) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") + return nil, status.Error(codes.Unimplemented, "method Delete not implemented") } func (UnimplementedFeatureServer) mustEmbedUnimplementedFeatureServer() {} func (UnimplementedFeatureServer) testEmbeddedByValue() {} @@ -150,7 +150,7 @@ type UnsafeFeatureServer interface { } func RegisterFeatureServer(s grpc.ServiceRegistrar, srv FeatureServer) { - // If the following call pancis, it indicates UnimplementedFeatureServer was + // If the following call panics, it indicates UnimplementedFeatureServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. diff --git a/cli/repository/meta/v1/meta.pb.go b/cli/repository/meta/v1/meta.pb.go index 426df7b..31461fa 100644 --- a/cli/repository/meta/v1/meta.pb.go +++ b/cli/repository/meta/v1/meta.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.33.2 +// protoc-gen-go v1.36.11 +// protoc v3.21.12 // source: meta.proto package metav1 diff --git a/cli/repository/meta/v1/meta_grpc.pb.go b/cli/repository/meta/v1/meta_grpc.pb.go index f383b5c..95d0f4b 100644 --- a/cli/repository/meta/v1/meta_grpc.pb.go +++ b/cli/repository/meta/v1/meta_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v6.33.2 +// - protoc-gen-go-grpc v1.6.0 +// - protoc v3.21.12 // source: meta.proto package metav1 @@ -63,7 +63,7 @@ type MetaServer interface { type UnimplementedMetaServer struct{} func (UnimplementedMetaServer) Meta(context.Context, *MetaRequest) (*MetaResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Meta not implemented") + return nil, status.Error(codes.Unimplemented, "method Meta not implemented") } func (UnimplementedMetaServer) mustEmbedUnimplementedMetaServer() {} func (UnimplementedMetaServer) testEmbeddedByValue() {} @@ -76,7 +76,7 @@ type UnsafeMetaServer interface { } func RegisterMetaServer(s grpc.ServiceRegistrar, srv MetaServer) { - // If the following call pancis, it indicates UnimplementedMetaServer was + // If the following call panics, it indicates UnimplementedMetaServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. diff --git a/cli/repository/workload/v1/workload.pb.go b/cli/repository/workload/v1/workload.pb.go new file mode 100644 index 0000000..b24e767 --- /dev/null +++ b/cli/repository/workload/v1/workload.pb.go @@ -0,0 +1,415 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v3.21.12 +// source: workload.proto + +package workloadv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// WorkloadType represents the type of Kubernetes workload +type WorkloadType int32 + +const ( + WorkloadType_WORKLOAD_TYPE_UNSPECIFIED WorkloadType = 0 + WorkloadType_WORKLOAD_TYPE_DEPLOYMENT WorkloadType = 1 + WorkloadType_WORKLOAD_TYPE_STATEFULSET WorkloadType = 2 + WorkloadType_WORKLOAD_TYPE_DAEMONSET WorkloadType = 3 +) + +// Enum value maps for WorkloadType. +var ( + WorkloadType_name = map[int32]string{ + 0: "WORKLOAD_TYPE_UNSPECIFIED", + 1: "WORKLOAD_TYPE_DEPLOYMENT", + 2: "WORKLOAD_TYPE_STATEFULSET", + 3: "WORKLOAD_TYPE_DAEMONSET", + } + WorkloadType_value = map[string]int32{ + "WORKLOAD_TYPE_UNSPECIFIED": 0, + "WORKLOAD_TYPE_DEPLOYMENT": 1, + "WORKLOAD_TYPE_STATEFULSET": 2, + "WORKLOAD_TYPE_DAEMONSET": 3, + } +) + +func (x WorkloadType) Enum() *WorkloadType { + p := new(WorkloadType) + *p = x + return p +} + +func (x WorkloadType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WorkloadType) Descriptor() protoreflect.EnumDescriptor { + return file_workload_proto_enumTypes[0].Descriptor() +} + +func (WorkloadType) Type() protoreflect.EnumType { + return &file_workload_proto_enumTypes[0] +} + +func (x WorkloadType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WorkloadType.Descriptor instead. +func (WorkloadType) EnumDescriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{0} +} + +// RestartRequest contains the workload type and name to restart +type RestartRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Type WorkloadType `protobuf:"varint,1,opt,name=type,proto3,enum=workload.v1.WorkloadType" json:"type,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RestartRequest) Reset() { + *x = RestartRequest{} + mi := &file_workload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RestartRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RestartRequest) ProtoMessage() {} + +func (x *RestartRequest) ProtoReflect() protoreflect.Message { + mi := &file_workload_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RestartRequest.ProtoReflect.Descriptor instead. +func (*RestartRequest) Descriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{0} +} + +func (x *RestartRequest) GetType() WorkloadType { + if x != nil { + return x.Type + } + return WorkloadType_WORKLOAD_TYPE_UNSPECIFIED +} + +func (x *RestartRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *RestartRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +// RestartResponse contains the result of the restart operation +type RestartResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RestartResponse) Reset() { + *x = RestartResponse{} + mi := &file_workload_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RestartResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RestartResponse) ProtoMessage() {} + +func (x *RestartResponse) ProtoReflect() protoreflect.Message { + mi := &file_workload_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RestartResponse.ProtoReflect.Descriptor instead. +func (*RestartResponse) Descriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{1} +} + +func (x *RestartResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *RestartResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +// ServiceInfo contains information about the configured restart service +type ServiceInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + Type WorkloadType `protobuf:"varint,2,opt,name=type,proto3,enum=workload.v1.WorkloadType" json:"type,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ServiceInfo) Reset() { + *x = ServiceInfo{} + mi := &file_workload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ServiceInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServiceInfo) ProtoMessage() {} + +func (x *ServiceInfo) ProtoReflect() protoreflect.Message { + mi := &file_workload_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServiceInfo.ProtoReflect.Descriptor instead. +func (*ServiceInfo) Descriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{2} +} + +func (x *ServiceInfo) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *ServiceInfo) GetType() WorkloadType { + if x != nil { + return x.Type + } + return WorkloadType_WORKLOAD_TYPE_UNSPECIFIED +} + +func (x *ServiceInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// InfoRequest is an empty request for getting service info +type InfoRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InfoRequest) Reset() { + *x = InfoRequest{} + mi := &file_workload_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InfoRequest) ProtoMessage() {} + +func (x *InfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_workload_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InfoRequest.ProtoReflect.Descriptor instead. +func (*InfoRequest) Descriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{3} +} + +// SimpleRestartRequest is an empty request that uses configured values +type SimpleRestartRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SimpleRestartRequest) Reset() { + *x = SimpleRestartRequest{} + mi := &file_workload_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SimpleRestartRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SimpleRestartRequest) ProtoMessage() {} + +func (x *SimpleRestartRequest) ProtoReflect() protoreflect.Message { + mi := &file_workload_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SimpleRestartRequest.ProtoReflect.Descriptor instead. +func (*SimpleRestartRequest) Descriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{4} +} + +var File_workload_proto protoreflect.FileDescriptor + +const file_workload_proto_rawDesc = "" + + "\n" + + "\x0eworkload.proto\x12\vworkload.v1\"q\n" + + "\x0eRestartRequest\x12-\n" + + "\x04type\x18\x01 \x01(\x0e2\x19.workload.v1.WorkloadTypeR\x04type\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12\x1c\n" + + "\tnamespace\x18\x03 \x01(\tR\tnamespace\"E\n" + + "\x0fRestartResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess\x12\x18\n" + + "\amessage\x18\x02 \x01(\tR\amessage\"j\n" + + "\vServiceInfo\x12\x18\n" + + "\aenabled\x18\x01 \x01(\bR\aenabled\x12-\n" + + "\x04type\x18\x02 \x01(\x0e2\x19.workload.v1.WorkloadTypeR\x04type\x12\x12\n" + + "\x04name\x18\x03 \x01(\tR\x04name\"\r\n" + + "\vInfoRequest\"\x16\n" + + "\x14SimpleRestartRequest*\x87\x01\n" + + "\fWorkloadType\x12\x1d\n" + + "\x19WORKLOAD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n" + + "\x18WORKLOAD_TYPE_DEPLOYMENT\x10\x01\x12\x1d\n" + + "\x19WORKLOAD_TYPE_STATEFULSET\x10\x02\x12\x1b\n" + + "\x17WORKLOAD_TYPE_DAEMONSET\x10\x032\xe0\x01\n" + + "\bWorkload\x12L\n" + + "\x0fRestartWorkload\x12\x1b.workload.v1.RestartRequest\x1a\x1c.workload.v1.RestartResponse\x12:\n" + + "\x04Info\x12\x18.workload.v1.InfoRequest\x1a\x18.workload.v1.ServiceInfo\x12J\n" + + "\aRestart\x12!.workload.v1.SimpleRestartRequest\x1a\x1c.workload.v1.RestartResponseBKZIgithub.com/dkrizic/feature/service/service/workload/workloadv1;workloadv1b\x06proto3" + +var ( + file_workload_proto_rawDescOnce sync.Once + file_workload_proto_rawDescData []byte +) + +func file_workload_proto_rawDescGZIP() []byte { + file_workload_proto_rawDescOnce.Do(func() { + file_workload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_workload_proto_rawDesc), len(file_workload_proto_rawDesc))) + }) + return file_workload_proto_rawDescData +} + +var file_workload_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_workload_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_workload_proto_goTypes = []any{ + (WorkloadType)(0), // 0: workload.v1.WorkloadType + (*RestartRequest)(nil), // 1: workload.v1.RestartRequest + (*RestartResponse)(nil), // 2: workload.v1.RestartResponse + (*ServiceInfo)(nil), // 3: workload.v1.ServiceInfo + (*InfoRequest)(nil), // 4: workload.v1.InfoRequest + (*SimpleRestartRequest)(nil), // 5: workload.v1.SimpleRestartRequest +} +var file_workload_proto_depIdxs = []int32{ + 0, // 0: workload.v1.RestartRequest.type:type_name -> workload.v1.WorkloadType + 0, // 1: workload.v1.ServiceInfo.type:type_name -> workload.v1.WorkloadType + 1, // 2: workload.v1.Workload.RestartWorkload:input_type -> workload.v1.RestartRequest + 4, // 3: workload.v1.Workload.Info:input_type -> workload.v1.InfoRequest + 5, // 4: workload.v1.Workload.Restart:input_type -> workload.v1.SimpleRestartRequest + 2, // 5: workload.v1.Workload.RestartWorkload:output_type -> workload.v1.RestartResponse + 3, // 6: workload.v1.Workload.Info:output_type -> workload.v1.ServiceInfo + 2, // 7: workload.v1.Workload.Restart:output_type -> workload.v1.RestartResponse + 5, // [5:8] is the sub-list for method output_type + 2, // [2:5] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_workload_proto_init() } +func file_workload_proto_init() { + if File_workload_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_workload_proto_rawDesc), len(file_workload_proto_rawDesc)), + NumEnums: 1, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_workload_proto_goTypes, + DependencyIndexes: file_workload_proto_depIdxs, + EnumInfos: file_workload_proto_enumTypes, + MessageInfos: file_workload_proto_msgTypes, + }.Build() + File_workload_proto = out.File + file_workload_proto_goTypes = nil + file_workload_proto_depIdxs = nil +} diff --git a/cli/repository/workload/v1/workload_grpc.pb.go b/cli/repository/workload/v1/workload_grpc.pb.go new file mode 100644 index 0000000..837da77 --- /dev/null +++ b/cli/repository/workload/v1/workload_grpc.pb.go @@ -0,0 +1,197 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.0 +// - protoc v3.21.12 +// source: workload.proto + +package workloadv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Workload_RestartWorkload_FullMethodName = "/workload.v1.Workload/RestartWorkload" + Workload_Info_FullMethodName = "/workload.v1.Workload/Info" + Workload_Restart_FullMethodName = "/workload.v1.Workload/Restart" +) + +// WorkloadClient is the client API for Workload service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type WorkloadClient interface { + RestartWorkload(ctx context.Context, in *RestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) + Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*ServiceInfo, error) + Restart(ctx context.Context, in *SimpleRestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) +} + +type workloadClient struct { + cc grpc.ClientConnInterface +} + +func NewWorkloadClient(cc grpc.ClientConnInterface) WorkloadClient { + return &workloadClient{cc} +} + +func (c *workloadClient) RestartWorkload(ctx context.Context, in *RestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RestartResponse) + err := c.cc.Invoke(ctx, Workload_RestartWorkload_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *workloadClient) Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*ServiceInfo, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ServiceInfo) + err := c.cc.Invoke(ctx, Workload_Info_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *workloadClient) Restart(ctx context.Context, in *SimpleRestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RestartResponse) + err := c.cc.Invoke(ctx, Workload_Restart_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// WorkloadServer is the server API for Workload service. +// All implementations must embed UnimplementedWorkloadServer +// for forward compatibility. +type WorkloadServer interface { + RestartWorkload(context.Context, *RestartRequest) (*RestartResponse, error) + Info(context.Context, *InfoRequest) (*ServiceInfo, error) + Restart(context.Context, *SimpleRestartRequest) (*RestartResponse, error) + mustEmbedUnimplementedWorkloadServer() +} + +// UnimplementedWorkloadServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedWorkloadServer struct{} + +func (UnimplementedWorkloadServer) RestartWorkload(context.Context, *RestartRequest) (*RestartResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RestartWorkload not implemented") +} +func (UnimplementedWorkloadServer) Info(context.Context, *InfoRequest) (*ServiceInfo, error) { + return nil, status.Error(codes.Unimplemented, "method Info not implemented") +} +func (UnimplementedWorkloadServer) Restart(context.Context, *SimpleRestartRequest) (*RestartResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Restart not implemented") +} +func (UnimplementedWorkloadServer) mustEmbedUnimplementedWorkloadServer() {} +func (UnimplementedWorkloadServer) testEmbeddedByValue() {} + +// UnsafeWorkloadServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to WorkloadServer will +// result in compilation errors. +type UnsafeWorkloadServer interface { + mustEmbedUnimplementedWorkloadServer() +} + +func RegisterWorkloadServer(s grpc.ServiceRegistrar, srv WorkloadServer) { + // If the following call panics, it indicates UnimplementedWorkloadServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Workload_ServiceDesc, srv) +} + +func _Workload_RestartWorkload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RestartRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkloadServer).RestartWorkload(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Workload_RestartWorkload_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkloadServer).RestartWorkload(ctx, req.(*RestartRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Workload_Info_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkloadServer).Info(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Workload_Info_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkloadServer).Info(ctx, req.(*InfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Workload_Restart_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SimpleRestartRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkloadServer).Restart(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Workload_Restart_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkloadServer).Restart(ctx, req.(*SimpleRestartRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Workload_ServiceDesc is the grpc.ServiceDesc for Workload service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Workload_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "workload.v1.Workload", + HandlerType: (*WorkloadServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "RestartWorkload", + Handler: _Workload_RestartWorkload_Handler, + }, + { + MethodName: "Info", + Handler: _Workload_Info_Handler, + }, + { + MethodName: "Restart", + Handler: _Workload_Restart_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "workload.proto", +} diff --git a/service/constant/constant.go b/service/constant/constant.go index 5f5bd43..ae84259 100644 --- a/service/constant/constant.go +++ b/service/constant/constant.go @@ -23,4 +23,7 @@ const ( NotificationTypeRedisTopic = "redis_topic" RedisEndpoint = "redis-endpoint" RedisNotificationTopic = "redis-notification-topic" + RestartEnabled = "restart-enabled" + RestartType = "restart-type" + RestartName = "restart-name" ) diff --git a/service/main.go b/service/main.go index ae2d09b..ae95a2d 100644 --- a/service/main.go +++ b/service/main.go @@ -152,6 +152,33 @@ func main() { Value: "feature_notifications", Sources: cli.EnvVars("REDIS_NOTIFICATION_TOPIC"), }, + &cli.BoolFlag{ + Name: constant.RestartEnabled, + Usage: "Enable workload restart feature", + Value: false, + Category: "restart", + Sources: cli.EnvVars("RESTART_ENABLED"), + }, + &cli.StringFlag{ + Name: constant.RestartType, + Usage: "Type of workload to restart: deployment, statefulset, daemonset", + Value: "deployment", + Category: "restart", + Sources: cli.EnvVars("RESTART_TYPE"), + Action: func(ctx context.Context, cmd *cli.Command, s string) error { + if s != "" && s != "deployment" && s != "statefulset" && s != "daemonset" { + return fmt.Errorf("invalid restart type: %s (must be deployment, statefulset, or daemonset)", s) + } + return nil + }, + }, + &cli.StringFlag{ + Name: constant.RestartName, + Usage: "Name of the workload to restart", + Value: "", + Category: "restart", + Sources: cli.EnvVars("RESTART_NAME"), + }, }, }, }, diff --git a/service/service/feature/v1/feature.pb.go b/service/service/feature/v1/feature.pb.go index 37b0134..14fa061 100644 --- a/service/service/feature/v1/feature.pb.go +++ b/service/service/feature/v1/feature.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.33.2 +// protoc-gen-go v1.36.11 +// protoc v3.21.12 // source: feature.proto package featurev1 diff --git a/service/service/feature/v1/feature_grpc.pb.go b/service/service/feature/v1/feature_grpc.pb.go index 60893f3..dbada6d 100644 --- a/service/service/feature/v1/feature_grpc.pb.go +++ b/service/service/feature/v1/feature_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v6.33.2 +// - protoc-gen-go-grpc v1.6.0 +// - protoc v3.21.12 // source: feature.proto package featurev1 @@ -125,19 +125,19 @@ type FeatureServer interface { type UnimplementedFeatureServer struct{} func (UnimplementedFeatureServer) GetAll(*emptypb.Empty, grpc.ServerStreamingServer[KeyValue]) error { - return status.Errorf(codes.Unimplemented, "method GetAll not implemented") + return status.Error(codes.Unimplemented, "method GetAll not implemented") } func (UnimplementedFeatureServer) PreSet(context.Context, *KeyValue) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method PreSet not implemented") + return nil, status.Error(codes.Unimplemented, "method PreSet not implemented") } func (UnimplementedFeatureServer) Set(context.Context, *KeyValue) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Set not implemented") + return nil, status.Error(codes.Unimplemented, "method Set not implemented") } func (UnimplementedFeatureServer) Get(context.Context, *Key) (*Value, error) { - return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") + return nil, status.Error(codes.Unimplemented, "method Get not implemented") } func (UnimplementedFeatureServer) Delete(context.Context, *Key) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") + return nil, status.Error(codes.Unimplemented, "method Delete not implemented") } func (UnimplementedFeatureServer) mustEmbedUnimplementedFeatureServer() {} func (UnimplementedFeatureServer) testEmbeddedByValue() {} @@ -150,7 +150,7 @@ type UnsafeFeatureServer interface { } func RegisterFeatureServer(s grpc.ServiceRegistrar, srv FeatureServer) { - // If the following call pancis, it indicates UnimplementedFeatureServer was + // If the following call panics, it indicates UnimplementedFeatureServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. diff --git a/service/service/meta/v1/meta.pb.go b/service/service/meta/v1/meta.pb.go index 426df7b..31461fa 100644 --- a/service/service/meta/v1/meta.pb.go +++ b/service/service/meta/v1/meta.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.33.2 +// protoc-gen-go v1.36.11 +// protoc v3.21.12 // source: meta.proto package metav1 diff --git a/service/service/meta/v1/meta_grpc.pb.go b/service/service/meta/v1/meta_grpc.pb.go index f383b5c..95d0f4b 100644 --- a/service/service/meta/v1/meta_grpc.pb.go +++ b/service/service/meta/v1/meta_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v6.33.2 +// - protoc-gen-go-grpc v1.6.0 +// - protoc v3.21.12 // source: meta.proto package metav1 @@ -63,7 +63,7 @@ type MetaServer interface { type UnimplementedMetaServer struct{} func (UnimplementedMetaServer) Meta(context.Context, *MetaRequest) (*MetaResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Meta not implemented") + return nil, status.Error(codes.Unimplemented, "method Meta not implemented") } func (UnimplementedMetaServer) mustEmbedUnimplementedMetaServer() {} func (UnimplementedMetaServer) testEmbeddedByValue() {} @@ -76,7 +76,7 @@ type UnsafeMetaServer interface { } func RegisterMetaServer(s grpc.ServiceRegistrar, srv MetaServer) { - // If the following call pancis, it indicates UnimplementedMetaServer was + // If the following call panics, it indicates UnimplementedMetaServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. diff --git a/service/service/service.go b/service/service/service.go index 11d7a62..cc4e10f 100644 --- a/service/service/service.go +++ b/service/service/service.go @@ -149,12 +149,31 @@ func Service(ctx context.Context, cmd *cli.Command) error { if namespace == "" { namespace = "default" } - workloadService, err := workload.NewWorkloadService(namespace) + + // Get restart configuration from flags + restartEnabled := cmd.Bool(constant.RestartEnabled) + restartTypeStr := cmd.String(constant.RestartType) + restartName := cmd.String(constant.RestartName) + + // Convert restart type string to protobuf enum + var restartType workloadv1.WorkloadType + switch restartTypeStr { + case "deployment": + restartType = workloadv1.WorkloadType_WORKLOAD_TYPE_DEPLOYMENT + case "statefulset": + restartType = workloadv1.WorkloadType_WORKLOAD_TYPE_STATEFULSET + case "daemonset": + restartType = workloadv1.WorkloadType_WORKLOAD_TYPE_DAEMONSET + default: + restartType = workloadv1.WorkloadType_WORKLOAD_TYPE_DEPLOYMENT + } + + workloadService, err := workload.NewWorkloadService(namespace, restartEnabled, restartType, restartName) if err != nil { slog.WarnContext(ctx, "Failed to create workload service (workload restart feature will be disabled)", "error", err) } else { workloadv1.RegisterWorkloadServer(grpcServer, workloadService) - slog.InfoContext(ctx, "Workload service enabled", "namespace", namespace) + slog.InfoContext(ctx, "Workload service enabled", "namespace", namespace, "restartEnabled", restartEnabled, "restartType", restartTypeStr, "restartName", restartName) } cancelChan := make(chan os.Signal, 1) diff --git a/service/service/workload/v1/workload.pb.go b/service/service/workload/v1/workload.pb.go index 2a62d2b..b24e767 100644 --- a/service/service/workload/v1/workload.pb.go +++ b/service/service/workload/v1/workload.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.32.0 +// protoc-gen-go v1.36.11 // protoc v3.21.12 // source: workload.proto @@ -11,6 +11,7 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -75,22 +76,19 @@ func (WorkloadType) EnumDescriptor() ([]byte, []int) { // RestartRequest contains the workload type and name to restart type RestartRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Type WorkloadType `protobuf:"varint,1,opt,name=type,proto3,enum=workload.v1.WorkloadType" json:"type,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` unknownFields protoimpl.UnknownFields - - Type WorkloadType `protobuf:"varint,1,opt,name=type,proto3,enum=workload.v1.WorkloadType" json:"type,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` + sizeCache protoimpl.SizeCache } func (x *RestartRequest) Reset() { *x = RestartRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_workload_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_workload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RestartRequest) String() string { @@ -101,7 +99,7 @@ func (*RestartRequest) ProtoMessage() {} func (x *RestartRequest) ProtoReflect() protoreflect.Message { mi := &file_workload_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -139,21 +137,18 @@ func (x *RestartRequest) GetNamespace() string { // RestartResponse contains the result of the restart operation type RestartResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` unknownFields protoimpl.UnknownFields - - Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + sizeCache protoimpl.SizeCache } func (x *RestartResponse) Reset() { *x = RestartResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_workload_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_workload_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RestartResponse) String() string { @@ -164,7 +159,7 @@ func (*RestartResponse) ProtoMessage() {} func (x *RestartResponse) ProtoReflect() protoreflect.Message { mi := &file_workload_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -193,72 +188,205 @@ func (x *RestartResponse) GetMessage() string { return "" } -var File_workload_proto protoreflect.FileDescriptor +// ServiceInfo contains information about the configured restart service +type ServiceInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + Type WorkloadType `protobuf:"varint,2,opt,name=type,proto3,enum=workload.v1.WorkloadType" json:"type,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ServiceInfo) Reset() { + *x = ServiceInfo{} + mi := &file_workload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} -var file_workload_proto_rawDesc = []byte{ - 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x22, 0x71, 0x0a, - 0x0e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, - 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x22, 0x45, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2a, 0x87, 0x01, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x57, 0x4f, 0x52, 0x4b, - 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x57, 0x4f, 0x52, 0x4b, 0x4c, - 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, - 0x45, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, - 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x46, 0x55, 0x4c, 0x53, - 0x45, 0x54, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x53, 0x45, 0x54, 0x10, - 0x03, 0x32, 0x58, 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x4c, 0x0a, - 0x0f, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x12, 0x1b, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, - 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4b, 0x5a, 0x49, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x6b, 0x72, 0x69, 0x7a, 0x69, - 0x63, 0x2f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x76, 0x31, 0x3b, 0x77, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +func (x *ServiceInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServiceInfo) ProtoMessage() {} + +func (x *ServiceInfo) ProtoReflect() protoreflect.Message { + mi := &file_workload_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServiceInfo.ProtoReflect.Descriptor instead. +func (*ServiceInfo) Descriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{2} +} + +func (x *ServiceInfo) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *ServiceInfo) GetType() WorkloadType { + if x != nil { + return x.Type + } + return WorkloadType_WORKLOAD_TYPE_UNSPECIFIED +} + +func (x *ServiceInfo) GetName() string { + if x != nil { + return x.Name + } + return "" } +// InfoRequest is an empty request for getting service info +type InfoRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InfoRequest) Reset() { + *x = InfoRequest{} + mi := &file_workload_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InfoRequest) ProtoMessage() {} + +func (x *InfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_workload_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InfoRequest.ProtoReflect.Descriptor instead. +func (*InfoRequest) Descriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{3} +} + +// SimpleRestartRequest is an empty request that uses configured values +type SimpleRestartRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SimpleRestartRequest) Reset() { + *x = SimpleRestartRequest{} + mi := &file_workload_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SimpleRestartRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SimpleRestartRequest) ProtoMessage() {} + +func (x *SimpleRestartRequest) ProtoReflect() protoreflect.Message { + mi := &file_workload_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SimpleRestartRequest.ProtoReflect.Descriptor instead. +func (*SimpleRestartRequest) Descriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{4} +} + +var File_workload_proto protoreflect.FileDescriptor + +const file_workload_proto_rawDesc = "" + + "\n" + + "\x0eworkload.proto\x12\vworkload.v1\"q\n" + + "\x0eRestartRequest\x12-\n" + + "\x04type\x18\x01 \x01(\x0e2\x19.workload.v1.WorkloadTypeR\x04type\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12\x1c\n" + + "\tnamespace\x18\x03 \x01(\tR\tnamespace\"E\n" + + "\x0fRestartResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess\x12\x18\n" + + "\amessage\x18\x02 \x01(\tR\amessage\"j\n" + + "\vServiceInfo\x12\x18\n" + + "\aenabled\x18\x01 \x01(\bR\aenabled\x12-\n" + + "\x04type\x18\x02 \x01(\x0e2\x19.workload.v1.WorkloadTypeR\x04type\x12\x12\n" + + "\x04name\x18\x03 \x01(\tR\x04name\"\r\n" + + "\vInfoRequest\"\x16\n" + + "\x14SimpleRestartRequest*\x87\x01\n" + + "\fWorkloadType\x12\x1d\n" + + "\x19WORKLOAD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n" + + "\x18WORKLOAD_TYPE_DEPLOYMENT\x10\x01\x12\x1d\n" + + "\x19WORKLOAD_TYPE_STATEFULSET\x10\x02\x12\x1b\n" + + "\x17WORKLOAD_TYPE_DAEMONSET\x10\x032\xe0\x01\n" + + "\bWorkload\x12L\n" + + "\x0fRestartWorkload\x12\x1b.workload.v1.RestartRequest\x1a\x1c.workload.v1.RestartResponse\x12:\n" + + "\x04Info\x12\x18.workload.v1.InfoRequest\x1a\x18.workload.v1.ServiceInfo\x12J\n" + + "\aRestart\x12!.workload.v1.SimpleRestartRequest\x1a\x1c.workload.v1.RestartResponseBKZIgithub.com/dkrizic/feature/service/service/workload/workloadv1;workloadv1b\x06proto3" + var ( file_workload_proto_rawDescOnce sync.Once - file_workload_proto_rawDescData = file_workload_proto_rawDesc + file_workload_proto_rawDescData []byte ) func file_workload_proto_rawDescGZIP() []byte { file_workload_proto_rawDescOnce.Do(func() { - file_workload_proto_rawDescData = protoimpl.X.CompressGZIP(file_workload_proto_rawDescData) + file_workload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_workload_proto_rawDesc), len(file_workload_proto_rawDesc))) }) return file_workload_proto_rawDescData } var file_workload_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_workload_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_workload_proto_goTypes = []interface{}{ - (WorkloadType)(0), // 0: workload.v1.WorkloadType - (*RestartRequest)(nil), // 1: workload.v1.RestartRequest - (*RestartResponse)(nil), // 2: workload.v1.RestartResponse +var file_workload_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_workload_proto_goTypes = []any{ + (WorkloadType)(0), // 0: workload.v1.WorkloadType + (*RestartRequest)(nil), // 1: workload.v1.RestartRequest + (*RestartResponse)(nil), // 2: workload.v1.RestartResponse + (*ServiceInfo)(nil), // 3: workload.v1.ServiceInfo + (*InfoRequest)(nil), // 4: workload.v1.InfoRequest + (*SimpleRestartRequest)(nil), // 5: workload.v1.SimpleRestartRequest } var file_workload_proto_depIdxs = []int32{ 0, // 0: workload.v1.RestartRequest.type:type_name -> workload.v1.WorkloadType - 1, // 1: workload.v1.Workload.RestartWorkload:input_type -> workload.v1.RestartRequest - 2, // 2: workload.v1.Workload.RestartWorkload:output_type -> workload.v1.RestartResponse - 2, // [2:3] is the sub-list for method output_type - 1, // [1:2] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 0, // 1: workload.v1.ServiceInfo.type:type_name -> workload.v1.WorkloadType + 1, // 2: workload.v1.Workload.RestartWorkload:input_type -> workload.v1.RestartRequest + 4, // 3: workload.v1.Workload.Info:input_type -> workload.v1.InfoRequest + 5, // 4: workload.v1.Workload.Restart:input_type -> workload.v1.SimpleRestartRequest + 2, // 5: workload.v1.Workload.RestartWorkload:output_type -> workload.v1.RestartResponse + 3, // 6: workload.v1.Workload.Info:output_type -> workload.v1.ServiceInfo + 2, // 7: workload.v1.Workload.Restart:output_type -> workload.v1.RestartResponse + 5, // [5:8] is the sub-list for method output_type + 2, // [2:5] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_workload_proto_init() } @@ -266,39 +394,13 @@ func file_workload_proto_init() { if File_workload_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_workload_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RestartRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_workload_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RestartResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_workload_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_workload_proto_rawDesc), len(file_workload_proto_rawDesc)), NumEnums: 1, - NumMessages: 2, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, @@ -308,7 +410,6 @@ func file_workload_proto_init() { MessageInfos: file_workload_proto_msgTypes, }.Build() File_workload_proto = out.File - file_workload_proto_rawDesc = nil file_workload_proto_goTypes = nil file_workload_proto_depIdxs = nil } diff --git a/service/service/workload/v1/workload_grpc.pb.go b/service/service/workload/v1/workload_grpc.pb.go index f7292b0..837da77 100644 --- a/service/service/workload/v1/workload_grpc.pb.go +++ b/service/service/workload/v1/workload_grpc.pb.go @@ -1,4 +1,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.0 +// - protoc v3.21.12 +// source: workload.proto package workloadv1 @@ -11,13 +15,22 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Workload_RestartWorkload_FullMethodName = "/workload.v1.Workload/RestartWorkload" + Workload_Info_FullMethodName = "/workload.v1.Workload/Info" + Workload_Restart_FullMethodName = "/workload.v1.Workload/Restart" +) // WorkloadClient is the client API for Workload service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type WorkloadClient interface { RestartWorkload(ctx context.Context, in *RestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) + Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*ServiceInfo, error) + Restart(ctx context.Context, in *SimpleRestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) } type workloadClient struct { @@ -29,8 +42,29 @@ func NewWorkloadClient(cc grpc.ClientConnInterface) WorkloadClient { } func (c *workloadClient) RestartWorkload(ctx context.Context, in *RestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RestartResponse) - err := c.cc.Invoke(ctx, "/workload.v1.Workload/RestartWorkload", in, out, opts...) + err := c.cc.Invoke(ctx, Workload_RestartWorkload_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *workloadClient) Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*ServiceInfo, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ServiceInfo) + err := c.cc.Invoke(ctx, Workload_Info_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *workloadClient) Restart(ctx context.Context, in *SimpleRestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RestartResponse) + err := c.cc.Invoke(ctx, Workload_Restart_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -39,20 +73,32 @@ func (c *workloadClient) RestartWorkload(ctx context.Context, in *RestartRequest // WorkloadServer is the server API for Workload service. // All implementations must embed UnimplementedWorkloadServer -// for forward compatibility +// for forward compatibility. type WorkloadServer interface { RestartWorkload(context.Context, *RestartRequest) (*RestartResponse, error) + Info(context.Context, *InfoRequest) (*ServiceInfo, error) + Restart(context.Context, *SimpleRestartRequest) (*RestartResponse, error) mustEmbedUnimplementedWorkloadServer() } -// UnimplementedWorkloadServer must be embedded to have forward compatible implementations. -type UnimplementedWorkloadServer struct { -} +// UnimplementedWorkloadServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedWorkloadServer struct{} func (UnimplementedWorkloadServer) RestartWorkload(context.Context, *RestartRequest) (*RestartResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RestartWorkload not implemented") + return nil, status.Error(codes.Unimplemented, "method RestartWorkload not implemented") +} +func (UnimplementedWorkloadServer) Info(context.Context, *InfoRequest) (*ServiceInfo, error) { + return nil, status.Error(codes.Unimplemented, "method Info not implemented") +} +func (UnimplementedWorkloadServer) Restart(context.Context, *SimpleRestartRequest) (*RestartResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Restart not implemented") } func (UnimplementedWorkloadServer) mustEmbedUnimplementedWorkloadServer() {} +func (UnimplementedWorkloadServer) testEmbeddedByValue() {} // UnsafeWorkloadServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to WorkloadServer will @@ -61,8 +107,15 @@ type UnsafeWorkloadServer interface { mustEmbedUnimplementedWorkloadServer() } -func RegisterWorkloadServer(s *grpc.Server, srv WorkloadServer) { - s.RegisterService(&_Workload_serviceDesc, srv) +func RegisterWorkloadServer(s grpc.ServiceRegistrar, srv WorkloadServer) { + // If the following call panics, it indicates UnimplementedWorkloadServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Workload_ServiceDesc, srv) } func _Workload_RestartWorkload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { @@ -75,7 +128,7 @@ func _Workload_RestartWorkload_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/workload.v1.Workload/RestartWorkload", + FullMethod: Workload_RestartWorkload_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WorkloadServer).RestartWorkload(ctx, req.(*RestartRequest)) @@ -83,7 +136,46 @@ func _Workload_RestartWorkload_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -var _Workload_serviceDesc = grpc.ServiceDesc{ +func _Workload_Info_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkloadServer).Info(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Workload_Info_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkloadServer).Info(ctx, req.(*InfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Workload_Restart_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SimpleRestartRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkloadServer).Restart(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Workload_Restart_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkloadServer).Restart(ctx, req.(*SimpleRestartRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Workload_ServiceDesc is the grpc.ServiceDesc for Workload service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Workload_ServiceDesc = grpc.ServiceDesc{ ServiceName: "workload.v1.Workload", HandlerType: (*WorkloadServer)(nil), Methods: []grpc.MethodDesc{ @@ -91,6 +183,14 @@ var _Workload_serviceDesc = grpc.ServiceDesc{ MethodName: "RestartWorkload", Handler: _Workload_RestartWorkload_Handler, }, + { + MethodName: "Info", + Handler: _Workload_Info_Handler, + }, + { + MethodName: "Restart", + Handler: _Workload_Restart_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "workload.proto", diff --git a/service/service/workload/workload.go b/service/service/workload/workload.go index b34ddce..51446cc 100644 --- a/service/service/workload/workload.go +++ b/service/service/workload/workload.go @@ -14,29 +14,35 @@ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // WorkloadService implements the Workload gRPC service type WorkloadService struct { -workloadv1.UnimplementedWorkloadServer -clientset *kubernetes.Clientset -namespace string + workloadv1.UnimplementedWorkloadServer + clientset *kubernetes.Clientset + namespace string + restartEnabled bool + restartType workloadv1.WorkloadType + restartName string } // NewWorkloadService creates a new workload service -func NewWorkloadService(namespace string) (*WorkloadService, error) { -// Create in-cluster config -config, err := rest.InClusterConfig() -if err != nil { -return nil, fmt.Errorf("failed to create in-cluster config: %w", err) -} - -// Create Kubernetes clientset -clientset, err := kubernetes.NewForConfig(config) -if err != nil { -return nil, fmt.Errorf("failed to create kubernetes clientset: %w", err) -} - -return &WorkloadService{ -clientset: clientset, -namespace: namespace, -}, nil +func NewWorkloadService(namespace string, restartEnabled bool, restartType workloadv1.WorkloadType, restartName string) (*WorkloadService, error) { + // Create in-cluster config + config, err := rest.InClusterConfig() + if err != nil { + return nil, fmt.Errorf("failed to create in-cluster config: %w", err) + } + + // Create Kubernetes clientset + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create kubernetes clientset: %w", err) + } + + return &WorkloadService{ + clientset: clientset, + namespace: namespace, + restartEnabled: restartEnabled, + restartType: restartType, + restartName: restartName, + }, nil } // RestartWorkload performs a rollout restart on the specified workload @@ -159,13 +165,50 @@ return fmt.Errorf("failed to get daemonset: %w", err) if daemonSet.Spec.Template.Annotations == nil { daemonSet.Spec.Template.Annotations = make(map[string]string) } -daemonSet.Spec.Template.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339) + daemonSet.Spec.Template.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339) -// Update the daemonset -_, err = daemonSetsClient.Update(ctx, daemonSet, metav1.UpdateOptions{}) -if err != nil { -return fmt.Errorf("failed to update daemonset: %w", err) + // Update the daemonset + _, err = daemonSetsClient.Update(ctx, daemonSet, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("failed to update daemonset: %w", err) + } + + return nil } -return nil +// Info returns the configured restart service information +func (s *WorkloadService) Info(ctx context.Context, req *workloadv1.InfoRequest) (*workloadv1.ServiceInfo, error) { + slog.InfoContext(ctx, "Received info request") + + return &workloadv1.ServiceInfo{ + Enabled: s.restartEnabled, + Type: s.restartType, + Name: s.restartName, + }, nil +} + +// Restart performs a restart using the pre-configured workload settings +func (s *WorkloadService) Restart(ctx context.Context, req *workloadv1.SimpleRestartRequest) (*workloadv1.RestartResponse, error) { + slog.InfoContext(ctx, "Received restart request for configured service") + + if !s.restartEnabled { + return &workloadv1.RestartResponse{ + Success: false, + Message: "Restart feature is not enabled", + }, nil + } + + if s.restartName == "" { + return &workloadv1.RestartResponse{ + Success: false, + Message: "Restart name is not configured", + }, nil + } + + // Call RestartWorkload with the configured values + return s.RestartWorkload(ctx, &workloadv1.RestartRequest{ + Type: s.restartType, + Name: s.restartName, + Namespace: "", // Empty namespace uses the service's configured namespace + }) } diff --git a/service/service/workload/workload_test.go b/service/service/workload/workload_test.go index e10f949..e7d0bc3 100644 --- a/service/service/workload/workload_test.go +++ b/service/service/workload/workload_test.go @@ -8,15 +8,15 @@ workloadv1 "github.com/dkrizic/feature/service/service/workload/v1" // TestWorkloadServiceCreation tests that we can create a workload service func TestWorkloadServiceCreation(t *testing.T) { -// When running outside of a Kubernetes cluster, this should fail -_, err := NewWorkloadService("default") -if err == nil { -t.Skip("Skipping test - running inside Kubernetes cluster") -} -// Expected to fail when not in cluster -if err == nil { -t.Error("Expected error when creating service outside cluster, got nil") -} + // When running outside of a Kubernetes cluster, this should fail + _, err := NewWorkloadService("default", false, workloadv1.WorkloadType_WORKLOAD_TYPE_DEPLOYMENT, "test") + if err == nil { + t.Skip("Skipping test - running inside Kubernetes cluster") + } + // Expected to fail when not in cluster + if err == nil { + t.Error("Expected error when creating service outside cluster, got nil") + } } // TestWorkloadTypeEnum tests that workload types are correctly defined diff --git a/ui/repository/feature/v1/feature.pb.go b/ui/repository/feature/v1/feature.pb.go index 37b0134..14fa061 100644 --- a/ui/repository/feature/v1/feature.pb.go +++ b/ui/repository/feature/v1/feature.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.33.2 +// protoc-gen-go v1.36.11 +// protoc v3.21.12 // source: feature.proto package featurev1 diff --git a/ui/repository/feature/v1/feature_grpc.pb.go b/ui/repository/feature/v1/feature_grpc.pb.go index 60893f3..dbada6d 100644 --- a/ui/repository/feature/v1/feature_grpc.pb.go +++ b/ui/repository/feature/v1/feature_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v6.33.2 +// - protoc-gen-go-grpc v1.6.0 +// - protoc v3.21.12 // source: feature.proto package featurev1 @@ -125,19 +125,19 @@ type FeatureServer interface { type UnimplementedFeatureServer struct{} func (UnimplementedFeatureServer) GetAll(*emptypb.Empty, grpc.ServerStreamingServer[KeyValue]) error { - return status.Errorf(codes.Unimplemented, "method GetAll not implemented") + return status.Error(codes.Unimplemented, "method GetAll not implemented") } func (UnimplementedFeatureServer) PreSet(context.Context, *KeyValue) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method PreSet not implemented") + return nil, status.Error(codes.Unimplemented, "method PreSet not implemented") } func (UnimplementedFeatureServer) Set(context.Context, *KeyValue) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Set not implemented") + return nil, status.Error(codes.Unimplemented, "method Set not implemented") } func (UnimplementedFeatureServer) Get(context.Context, *Key) (*Value, error) { - return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") + return nil, status.Error(codes.Unimplemented, "method Get not implemented") } func (UnimplementedFeatureServer) Delete(context.Context, *Key) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") + return nil, status.Error(codes.Unimplemented, "method Delete not implemented") } func (UnimplementedFeatureServer) mustEmbedUnimplementedFeatureServer() {} func (UnimplementedFeatureServer) testEmbeddedByValue() {} @@ -150,7 +150,7 @@ type UnsafeFeatureServer interface { } func RegisterFeatureServer(s grpc.ServiceRegistrar, srv FeatureServer) { - // If the following call pancis, it indicates UnimplementedFeatureServer was + // If the following call panics, it indicates UnimplementedFeatureServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. diff --git a/ui/repository/meta/v1/meta.pb.go b/ui/repository/meta/v1/meta.pb.go index 426df7b..31461fa 100644 --- a/ui/repository/meta/v1/meta.pb.go +++ b/ui/repository/meta/v1/meta.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.33.2 +// protoc-gen-go v1.36.11 +// protoc v3.21.12 // source: meta.proto package metav1 diff --git a/ui/repository/meta/v1/meta_grpc.pb.go b/ui/repository/meta/v1/meta_grpc.pb.go index f383b5c..95d0f4b 100644 --- a/ui/repository/meta/v1/meta_grpc.pb.go +++ b/ui/repository/meta/v1/meta_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc v6.33.2 +// - protoc-gen-go-grpc v1.6.0 +// - protoc v3.21.12 // source: meta.proto package metav1 @@ -63,7 +63,7 @@ type MetaServer interface { type UnimplementedMetaServer struct{} func (UnimplementedMetaServer) Meta(context.Context, *MetaRequest) (*MetaResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Meta not implemented") + return nil, status.Error(codes.Unimplemented, "method Meta not implemented") } func (UnimplementedMetaServer) mustEmbedUnimplementedMetaServer() {} func (UnimplementedMetaServer) testEmbeddedByValue() {} @@ -76,7 +76,7 @@ type UnsafeMetaServer interface { } func RegisterMetaServer(s grpc.ServiceRegistrar, srv MetaServer) { - // If the following call pancis, it indicates UnimplementedMetaServer was + // If the following call panics, it indicates UnimplementedMetaServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. diff --git a/ui/repository/workload/v1/workload.pb.go b/ui/repository/workload/v1/workload.pb.go index 2a62d2b..b24e767 100644 --- a/ui/repository/workload/v1/workload.pb.go +++ b/ui/repository/workload/v1/workload.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.32.0 +// protoc-gen-go v1.36.11 // protoc v3.21.12 // source: workload.proto @@ -11,6 +11,7 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -75,22 +76,19 @@ func (WorkloadType) EnumDescriptor() ([]byte, []int) { // RestartRequest contains the workload type and name to restart type RestartRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Type WorkloadType `protobuf:"varint,1,opt,name=type,proto3,enum=workload.v1.WorkloadType" json:"type,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` unknownFields protoimpl.UnknownFields - - Type WorkloadType `protobuf:"varint,1,opt,name=type,proto3,enum=workload.v1.WorkloadType" json:"type,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` + sizeCache protoimpl.SizeCache } func (x *RestartRequest) Reset() { *x = RestartRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_workload_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_workload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RestartRequest) String() string { @@ -101,7 +99,7 @@ func (*RestartRequest) ProtoMessage() {} func (x *RestartRequest) ProtoReflect() protoreflect.Message { mi := &file_workload_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -139,21 +137,18 @@ func (x *RestartRequest) GetNamespace() string { // RestartResponse contains the result of the restart operation type RestartResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` unknownFields protoimpl.UnknownFields - - Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + sizeCache protoimpl.SizeCache } func (x *RestartResponse) Reset() { *x = RestartResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_workload_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_workload_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RestartResponse) String() string { @@ -164,7 +159,7 @@ func (*RestartResponse) ProtoMessage() {} func (x *RestartResponse) ProtoReflect() protoreflect.Message { mi := &file_workload_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -193,72 +188,205 @@ func (x *RestartResponse) GetMessage() string { return "" } -var File_workload_proto protoreflect.FileDescriptor +// ServiceInfo contains information about the configured restart service +type ServiceInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + Type WorkloadType `protobuf:"varint,2,opt,name=type,proto3,enum=workload.v1.WorkloadType" json:"type,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ServiceInfo) Reset() { + *x = ServiceInfo{} + mi := &file_workload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} -var file_workload_proto_rawDesc = []byte{ - 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x22, 0x71, 0x0a, - 0x0e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, - 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x22, 0x45, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2a, 0x87, 0x01, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x57, 0x4f, 0x52, 0x4b, - 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x57, 0x4f, 0x52, 0x4b, 0x4c, - 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, - 0x45, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, - 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x46, 0x55, 0x4c, 0x53, - 0x45, 0x54, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x53, 0x45, 0x54, 0x10, - 0x03, 0x32, 0x58, 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x4c, 0x0a, - 0x0f, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x12, 0x1b, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, - 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4b, 0x5a, 0x49, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x6b, 0x72, 0x69, 0x7a, 0x69, - 0x63, 0x2f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x76, 0x31, 0x3b, 0x77, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +func (x *ServiceInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServiceInfo) ProtoMessage() {} + +func (x *ServiceInfo) ProtoReflect() protoreflect.Message { + mi := &file_workload_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServiceInfo.ProtoReflect.Descriptor instead. +func (*ServiceInfo) Descriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{2} +} + +func (x *ServiceInfo) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *ServiceInfo) GetType() WorkloadType { + if x != nil { + return x.Type + } + return WorkloadType_WORKLOAD_TYPE_UNSPECIFIED +} + +func (x *ServiceInfo) GetName() string { + if x != nil { + return x.Name + } + return "" } +// InfoRequest is an empty request for getting service info +type InfoRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InfoRequest) Reset() { + *x = InfoRequest{} + mi := &file_workload_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InfoRequest) ProtoMessage() {} + +func (x *InfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_workload_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InfoRequest.ProtoReflect.Descriptor instead. +func (*InfoRequest) Descriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{3} +} + +// SimpleRestartRequest is an empty request that uses configured values +type SimpleRestartRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SimpleRestartRequest) Reset() { + *x = SimpleRestartRequest{} + mi := &file_workload_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SimpleRestartRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SimpleRestartRequest) ProtoMessage() {} + +func (x *SimpleRestartRequest) ProtoReflect() protoreflect.Message { + mi := &file_workload_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SimpleRestartRequest.ProtoReflect.Descriptor instead. +func (*SimpleRestartRequest) Descriptor() ([]byte, []int) { + return file_workload_proto_rawDescGZIP(), []int{4} +} + +var File_workload_proto protoreflect.FileDescriptor + +const file_workload_proto_rawDesc = "" + + "\n" + + "\x0eworkload.proto\x12\vworkload.v1\"q\n" + + "\x0eRestartRequest\x12-\n" + + "\x04type\x18\x01 \x01(\x0e2\x19.workload.v1.WorkloadTypeR\x04type\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12\x1c\n" + + "\tnamespace\x18\x03 \x01(\tR\tnamespace\"E\n" + + "\x0fRestartResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess\x12\x18\n" + + "\amessage\x18\x02 \x01(\tR\amessage\"j\n" + + "\vServiceInfo\x12\x18\n" + + "\aenabled\x18\x01 \x01(\bR\aenabled\x12-\n" + + "\x04type\x18\x02 \x01(\x0e2\x19.workload.v1.WorkloadTypeR\x04type\x12\x12\n" + + "\x04name\x18\x03 \x01(\tR\x04name\"\r\n" + + "\vInfoRequest\"\x16\n" + + "\x14SimpleRestartRequest*\x87\x01\n" + + "\fWorkloadType\x12\x1d\n" + + "\x19WORKLOAD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n" + + "\x18WORKLOAD_TYPE_DEPLOYMENT\x10\x01\x12\x1d\n" + + "\x19WORKLOAD_TYPE_STATEFULSET\x10\x02\x12\x1b\n" + + "\x17WORKLOAD_TYPE_DAEMONSET\x10\x032\xe0\x01\n" + + "\bWorkload\x12L\n" + + "\x0fRestartWorkload\x12\x1b.workload.v1.RestartRequest\x1a\x1c.workload.v1.RestartResponse\x12:\n" + + "\x04Info\x12\x18.workload.v1.InfoRequest\x1a\x18.workload.v1.ServiceInfo\x12J\n" + + "\aRestart\x12!.workload.v1.SimpleRestartRequest\x1a\x1c.workload.v1.RestartResponseBKZIgithub.com/dkrizic/feature/service/service/workload/workloadv1;workloadv1b\x06proto3" + var ( file_workload_proto_rawDescOnce sync.Once - file_workload_proto_rawDescData = file_workload_proto_rawDesc + file_workload_proto_rawDescData []byte ) func file_workload_proto_rawDescGZIP() []byte { file_workload_proto_rawDescOnce.Do(func() { - file_workload_proto_rawDescData = protoimpl.X.CompressGZIP(file_workload_proto_rawDescData) + file_workload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_workload_proto_rawDesc), len(file_workload_proto_rawDesc))) }) return file_workload_proto_rawDescData } var file_workload_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_workload_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_workload_proto_goTypes = []interface{}{ - (WorkloadType)(0), // 0: workload.v1.WorkloadType - (*RestartRequest)(nil), // 1: workload.v1.RestartRequest - (*RestartResponse)(nil), // 2: workload.v1.RestartResponse +var file_workload_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_workload_proto_goTypes = []any{ + (WorkloadType)(0), // 0: workload.v1.WorkloadType + (*RestartRequest)(nil), // 1: workload.v1.RestartRequest + (*RestartResponse)(nil), // 2: workload.v1.RestartResponse + (*ServiceInfo)(nil), // 3: workload.v1.ServiceInfo + (*InfoRequest)(nil), // 4: workload.v1.InfoRequest + (*SimpleRestartRequest)(nil), // 5: workload.v1.SimpleRestartRequest } var file_workload_proto_depIdxs = []int32{ 0, // 0: workload.v1.RestartRequest.type:type_name -> workload.v1.WorkloadType - 1, // 1: workload.v1.Workload.RestartWorkload:input_type -> workload.v1.RestartRequest - 2, // 2: workload.v1.Workload.RestartWorkload:output_type -> workload.v1.RestartResponse - 2, // [2:3] is the sub-list for method output_type - 1, // [1:2] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 0, // 1: workload.v1.ServiceInfo.type:type_name -> workload.v1.WorkloadType + 1, // 2: workload.v1.Workload.RestartWorkload:input_type -> workload.v1.RestartRequest + 4, // 3: workload.v1.Workload.Info:input_type -> workload.v1.InfoRequest + 5, // 4: workload.v1.Workload.Restart:input_type -> workload.v1.SimpleRestartRequest + 2, // 5: workload.v1.Workload.RestartWorkload:output_type -> workload.v1.RestartResponse + 3, // 6: workload.v1.Workload.Info:output_type -> workload.v1.ServiceInfo + 2, // 7: workload.v1.Workload.Restart:output_type -> workload.v1.RestartResponse + 5, // [5:8] is the sub-list for method output_type + 2, // [2:5] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_workload_proto_init() } @@ -266,39 +394,13 @@ func file_workload_proto_init() { if File_workload_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_workload_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RestartRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_workload_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RestartResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_workload_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_workload_proto_rawDesc), len(file_workload_proto_rawDesc)), NumEnums: 1, - NumMessages: 2, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, @@ -308,7 +410,6 @@ func file_workload_proto_init() { MessageInfos: file_workload_proto_msgTypes, }.Build() File_workload_proto = out.File - file_workload_proto_rawDesc = nil file_workload_proto_goTypes = nil file_workload_proto_depIdxs = nil } diff --git a/ui/repository/workload/v1/workload_grpc.pb.go b/ui/repository/workload/v1/workload_grpc.pb.go index f7292b0..837da77 100644 --- a/ui/repository/workload/v1/workload_grpc.pb.go +++ b/ui/repository/workload/v1/workload_grpc.pb.go @@ -1,4 +1,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.0 +// - protoc v3.21.12 +// source: workload.proto package workloadv1 @@ -11,13 +15,22 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Workload_RestartWorkload_FullMethodName = "/workload.v1.Workload/RestartWorkload" + Workload_Info_FullMethodName = "/workload.v1.Workload/Info" + Workload_Restart_FullMethodName = "/workload.v1.Workload/Restart" +) // WorkloadClient is the client API for Workload service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type WorkloadClient interface { RestartWorkload(ctx context.Context, in *RestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) + Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*ServiceInfo, error) + Restart(ctx context.Context, in *SimpleRestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) } type workloadClient struct { @@ -29,8 +42,29 @@ func NewWorkloadClient(cc grpc.ClientConnInterface) WorkloadClient { } func (c *workloadClient) RestartWorkload(ctx context.Context, in *RestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RestartResponse) - err := c.cc.Invoke(ctx, "/workload.v1.Workload/RestartWorkload", in, out, opts...) + err := c.cc.Invoke(ctx, Workload_RestartWorkload_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *workloadClient) Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*ServiceInfo, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ServiceInfo) + err := c.cc.Invoke(ctx, Workload_Info_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *workloadClient) Restart(ctx context.Context, in *SimpleRestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RestartResponse) + err := c.cc.Invoke(ctx, Workload_Restart_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -39,20 +73,32 @@ func (c *workloadClient) RestartWorkload(ctx context.Context, in *RestartRequest // WorkloadServer is the server API for Workload service. // All implementations must embed UnimplementedWorkloadServer -// for forward compatibility +// for forward compatibility. type WorkloadServer interface { RestartWorkload(context.Context, *RestartRequest) (*RestartResponse, error) + Info(context.Context, *InfoRequest) (*ServiceInfo, error) + Restart(context.Context, *SimpleRestartRequest) (*RestartResponse, error) mustEmbedUnimplementedWorkloadServer() } -// UnimplementedWorkloadServer must be embedded to have forward compatible implementations. -type UnimplementedWorkloadServer struct { -} +// UnimplementedWorkloadServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedWorkloadServer struct{} func (UnimplementedWorkloadServer) RestartWorkload(context.Context, *RestartRequest) (*RestartResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RestartWorkload not implemented") + return nil, status.Error(codes.Unimplemented, "method RestartWorkload not implemented") +} +func (UnimplementedWorkloadServer) Info(context.Context, *InfoRequest) (*ServiceInfo, error) { + return nil, status.Error(codes.Unimplemented, "method Info not implemented") +} +func (UnimplementedWorkloadServer) Restart(context.Context, *SimpleRestartRequest) (*RestartResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Restart not implemented") } func (UnimplementedWorkloadServer) mustEmbedUnimplementedWorkloadServer() {} +func (UnimplementedWorkloadServer) testEmbeddedByValue() {} // UnsafeWorkloadServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to WorkloadServer will @@ -61,8 +107,15 @@ type UnsafeWorkloadServer interface { mustEmbedUnimplementedWorkloadServer() } -func RegisterWorkloadServer(s *grpc.Server, srv WorkloadServer) { - s.RegisterService(&_Workload_serviceDesc, srv) +func RegisterWorkloadServer(s grpc.ServiceRegistrar, srv WorkloadServer) { + // If the following call panics, it indicates UnimplementedWorkloadServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Workload_ServiceDesc, srv) } func _Workload_RestartWorkload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { @@ -75,7 +128,7 @@ func _Workload_RestartWorkload_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/workload.v1.Workload/RestartWorkload", + FullMethod: Workload_RestartWorkload_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WorkloadServer).RestartWorkload(ctx, req.(*RestartRequest)) @@ -83,7 +136,46 @@ func _Workload_RestartWorkload_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -var _Workload_serviceDesc = grpc.ServiceDesc{ +func _Workload_Info_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkloadServer).Info(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Workload_Info_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkloadServer).Info(ctx, req.(*InfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Workload_Restart_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SimpleRestartRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkloadServer).Restart(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Workload_Restart_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkloadServer).Restart(ctx, req.(*SimpleRestartRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Workload_ServiceDesc is the grpc.ServiceDesc for Workload service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Workload_ServiceDesc = grpc.ServiceDesc{ ServiceName: "workload.v1.Workload", HandlerType: (*WorkloadServer)(nil), Methods: []grpc.MethodDesc{ @@ -91,6 +183,14 @@ var _Workload_serviceDesc = grpc.ServiceDesc{ MethodName: "RestartWorkload", Handler: _Workload_RestartWorkload_Handler, }, + { + MethodName: "Info", + Handler: _Workload_Info_Handler, + }, + { + MethodName: "Restart", + Handler: _Workload_Restart_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "workload.proto", diff --git a/ui/service/handlers.go b/ui/service/handlers.go index 50483ee..0d6183b 100644 --- a/ui/service/handlers.go +++ b/ui/service/handlers.go @@ -30,6 +30,7 @@ func (s *Server) registerHandlers(mux *http.ServeMux) { mux.HandleFunc("POST "+prefix+"/features/create", otelhttp.NewHandler(http.HandlerFunc(s.handleFeatureCreate), "handleFeatureCreate").ServeHTTP) mux.HandleFunc("POST "+prefix+"/features/update", otelhttp.NewHandler(http.HandlerFunc(s.handleFeatureUpdate), "handleFeatureUpdate").ServeHTTP) mux.HandleFunc("POST "+prefix+"/features/delete", otelhttp.NewHandler(http.HandlerFunc(s.handleFeatureDelete), "handleFeatureDelete").ServeHTTP) + mux.HandleFunc("POST "+prefix+"/restart", otelhttp.NewHandler(http.HandlerFunc(s.handleRestart), "handleRestart").ServeHTTP) mux.HandleFunc("GET "+prefix+"/health", s.handleHealth) } @@ -42,10 +43,16 @@ func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) { UIVersion string BackendVersion string Subpath string + RestartEnabled bool + RestartName string + RestartType string }{ UIVersion: s.uiVersion, BackendVersion: s.backendVersion, Subpath: s.subpath, + RestartEnabled: s.restartEnabled, + RestartName: s.restartName, + RestartType: s.restartType, } if err := s.templates.ExecuteTemplate(w, "index.gohtml", data); err != nil { @@ -312,3 +319,42 @@ func (s *Server) handleWorkloadRestart(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(fmt.Sprintf(`
`, resp.Message))) } + +// handleRestart handles the simplified restart request using configured values +func (s *Server) handleRestart(w http.ResponseWriter, r *http.Request) { + ctx, span := otel.Tracer("ui/service").Start(r.Context(), "handleRestart") + defer span.End() + + slog.InfoContext(ctx, "Handling restart request for configured service") + + // Validate that restart is enabled + if !s.restartEnabled { + slog.WarnContext(ctx, "Restart feature is not enabled") + http.Error(w, "Restart feature is not enabled", http.StatusForbidden) + span.SetStatus(codes.Error, "Restart feature is not enabled") + return + } + + // Call the gRPC backend + resp, err := s.workloadClient.Restart(ctx, &workloadv1.SimpleRestartRequest{}) + if err != nil { + slog.ErrorContext(ctx, "Failed to restart service", "error", err) + http.Error(w, fmt.Sprintf("Failed to restart service: %v", err), http.StatusInternalServerError) + span.SetStatus(codes.Error, err.Error()) + return + } + + if !resp.Success { + slog.WarnContext(ctx, "Service restart unsuccessful", "message", resp.Message) + http.Error(w, resp.Message, http.StatusBadRequest) + span.SetStatus(codes.Error, resp.Message) + return + } + + slog.InfoContext(ctx, "Service restarted successfully") + + // Return success message + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(http.StatusOK) + w.Write([]byte(fmt.Sprintf(``, resp.Message))) +} diff --git a/ui/service/service.go b/ui/service/service.go index 1a75d02..081df65 100644 --- a/ui/service/service.go +++ b/ui/service/service.go @@ -28,15 +28,18 @@ import ( // Server holds the HTTP server and gRPC clients. type Server struct { - address string - subpath string - templates *template.Template - featureClient featurev1.FeatureClient - metaClient metav1.MetaClient - workloadClient workloadv1.WorkloadClient - backendVersion string - uiVersion string - httpServer *http.Server + address string + subpath string + templates *template.Template + featureClient featurev1.FeatureClient + metaClient metav1.MetaClient + workloadClient workloadv1.WorkloadClient + backendVersion string + uiVersion string + httpServer *http.Server + restartEnabled bool + restartName string + restartType string } var otelShutdown func(ctx context.Context) error = nil @@ -122,8 +125,9 @@ func Service(ctx context.Context, cmd *cli.Command) error { workloadClient := workloadv1.NewWorkloadClient(conn) // Fetch backend version + const grpcCallTimeout = 5 * time.Second backendVersion := "" - metaCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + metaCtx, cancel := context.WithTimeout(ctx, grpcCallTimeout) defer cancel() metaResp, err := metaClient.Meta(metaCtx, &metav1.MetaRequest{}) if err != nil { @@ -133,6 +137,22 @@ func Service(ctx context.Context, cmd *cli.Command) error { slog.InfoContext(ctx, "Backend version retrieved", "version", backendVersion) } + // Fetch service restart info + restartEnabled := false + restartName := "" + restartType := "" + infoCtx, infoCancel := context.WithTimeout(ctx, grpcCallTimeout) + defer infoCancel() + infoResp, err := workloadClient.Info(infoCtx, &workloadv1.InfoRequest{}) + if err != nil { + slog.WarnContext(ctx, "Failed to fetch service info", "error", err) + } else { + restartEnabled = infoResp.Enabled + restartName = infoResp.Name + restartType = infoResp.Type.String() + slog.InfoContext(ctx, "Service info retrieved", "enabled", restartEnabled, "name", restartName, "type", restartType) + } + // Create server server := &Server{ address: fmt.Sprintf(":%d", port), @@ -143,6 +163,9 @@ func Service(ctx context.Context, cmd *cli.Command) error { workloadClient: workloadClient, backendVersion: backendVersion, uiVersion: meta.Version, + restartEnabled: restartEnabled, + restartName: restartName, + restartType: restartType, } // Setup HTTP routes diff --git a/ui/service/templates/index.gohtml b/ui/service/templates/index.gohtml index 2364c84..6c73d19 100644 --- a/ui/service/templates/index.gohtml +++ b/ui/service/templates/index.gohtml @@ -204,47 +204,27 @@ + {{if .RestartEnabled}}