From 9dbb502f7fb9cd574ffed6fac8a5604089b01499 Mon Sep 17 00:00:00 2001 From: Matthias Loibl Date: Wed, 5 Oct 2022 23:39:31 +0200 Subject: [PATCH] Create an interactive editor --- .../pyrra.dev_servicelevelobjectives.json | 10 +- .../pyrra.dev_servicelevelobjectives.yaml | 8 +- main.go | 7 + proto/objectives/v1alpha1/objectives.pb.go | 348 ++++++++++---- proto/objectives/v1alpha1/objectives.proto | 11 + .../objectives.connect.go | 22 + ui/package-lock.json | 131 ++++- ui/package.json | 4 +- ui/src/App.tsx | 2 + ui/src/components/tiles/AvailabilityTile.tsx | 95 ++++ ui/src/components/tiles/ErrorBudgetTile.tsx | 63 +++ ui/src/components/tiles/ObjectiveTile.tsx | 54 +++ ui/src/components/tiles/Tiles.scss | 69 +++ ui/src/components/tiles/Tiles.tsx | 9 + ui/src/crd/servicelevelobjectives.ts | 57 +++ ui/src/index.scss | 1 + ui/src/labels.tsx | 8 + ui/src/pages/Create.tsx | 447 ++++++++++++++++++ ui/src/pages/Detail.scss | 69 --- ui/src/pages/Detail.tsx | 216 ++------- .../v1alpha1/objectives_connectweb.d.ts | 11 +- .../v1alpha1/objectives_connectweb.js | 11 +- .../objectives/v1alpha1/objectives_pb.d.ts | 58 +++ .../objectives/v1alpha1/objectives_pb.js | 22 + 24 files changed, 1341 insertions(+), 392 deletions(-) create mode 100644 ui/src/components/tiles/AvailabilityTile.tsx create mode 100644 ui/src/components/tiles/ErrorBudgetTile.tsx create mode 100644 ui/src/components/tiles/ObjectiveTile.tsx create mode 100644 ui/src/components/tiles/Tiles.scss create mode 100644 ui/src/components/tiles/Tiles.tsx create mode 100644 ui/src/crd/servicelevelobjectives.ts create mode 100644 ui/src/pages/Create.tsx diff --git a/config/crd/bases/pyrra.dev_servicelevelobjectives.json b/config/crd/bases/pyrra.dev_servicelevelobjectives.json index 8b87398a7..550c97cb3 100644 --- a/config/crd/bases/pyrra.dev_servicelevelobjectives.json +++ b/config/crd/bases/pyrra.dev_servicelevelobjectives.json @@ -3,7 +3,7 @@ "kind": "CustomResourceDefinition", "metadata": { "annotations": { - "controller-gen.kubebuilder.io/version": "v0.8.0" + "controller-gen.kubebuilder.io/version": "v0.11.1" }, "creationTimestamp": null, "name": "servicelevelobjectives.pyrra.dev" @@ -195,13 +195,5 @@ "storage": true } ] - }, - "status": { - "acceptedNames": { - "kind": "", - "plural": "" - }, - "conditions": [], - "storedVersions": [] } } diff --git a/config/crd/bases/pyrra.dev_servicelevelobjectives.yaml b/config/crd/bases/pyrra.dev_servicelevelobjectives.yaml index 1bb197112..97c41e74d 100644 --- a/config/crd/bases/pyrra.dev_servicelevelobjectives.yaml +++ b/config/crd/bases/pyrra.dev_servicelevelobjectives.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: servicelevelobjectives.pyrra.dev spec: @@ -162,9 +162,3 @@ spec: type: object served: true storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/main.go b/main.go index fed6b2ebe..7d91236ba 100644 --- a/main.go +++ b/main.go @@ -1486,6 +1486,13 @@ func (s *objectiveServer) GraphDuration(ctx context.Context, req *connect.Reques }), nil } +func (s *objectiveServer) GetAvailability(ctx context.Context, req *connect.Request[objectivesv1alpha1.GetAvailabilityRequest]) (*connect.Response[objectivesv1alpha1.GetAvailabilityResponse], error) { + return connect.NewResponse(&objectivesv1alpha1.GetAvailabilityResponse{ + Total: 1000, + Errors: 1, + }), nil +} + const ( hours12 = 12 * time.Hour day = 24 * time.Hour diff --git a/proto/objectives/v1alpha1/objectives.pb.go b/proto/objectives/v1alpha1/objectives.pb.go index ab562b699..62180ced9 100644 --- a/proto/objectives/v1alpha1/objectives.pb.go +++ b/proto/objectives/v1alpha1/objectives.pb.go @@ -1960,6 +1960,116 @@ func (x *GraphDurationResponse) GetTimeseries() []*Timeseries { return nil } +type GetAvailabilityRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Window *durationpb.Duration `protobuf:"bytes,1,opt,name=window,proto3" json:"window,omitempty"` + Indicator *Indicator `protobuf:"bytes,2,opt,name=indicator,proto3" json:"indicator,omitempty"` +} + +func (x *GetAvailabilityRequest) Reset() { + *x = GetAvailabilityRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_objectives_v1alpha1_objectives_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetAvailabilityRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAvailabilityRequest) ProtoMessage() {} + +func (x *GetAvailabilityRequest) ProtoReflect() protoreflect.Message { + mi := &file_objectives_v1alpha1_objectives_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetAvailabilityRequest.ProtoReflect.Descriptor instead. +func (*GetAvailabilityRequest) Descriptor() ([]byte, []int) { + return file_objectives_v1alpha1_objectives_proto_rawDescGZIP(), []int{29} +} + +func (x *GetAvailabilityRequest) GetWindow() *durationpb.Duration { + if x != nil { + return x.Window + } + return nil +} + +func (x *GetAvailabilityRequest) GetIndicator() *Indicator { + if x != nil { + return x.Indicator + } + return nil +} + +type GetAvailabilityResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Total int64 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"` + Errors int64 `protobuf:"varint,2,opt,name=errors,proto3" json:"errors,omitempty"` +} + +func (x *GetAvailabilityResponse) Reset() { + *x = GetAvailabilityResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_objectives_v1alpha1_objectives_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetAvailabilityResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAvailabilityResponse) ProtoMessage() {} + +func (x *GetAvailabilityResponse) ProtoReflect() protoreflect.Message { + mi := &file_objectives_v1alpha1_objectives_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetAvailabilityResponse.ProtoReflect.Descriptor instead. +func (*GetAvailabilityResponse) Descriptor() ([]byte, []int) { + return file_objectives_v1alpha1_objectives_proto_rawDescGZIP(), []int{30} +} + +func (x *GetAvailabilityResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *GetAvailabilityResponse) GetErrors() int64 { + if x != nil { + return x.Errors + } + return 0 +} + var File_objectives_v1alpha1_objectives_proto protoreflect.FileDescriptor var file_objectives_v1alpha1_objectives_proto_rawDesc = []byte{ @@ -2230,63 +2340,83 @@ var file_objectives_v1alpha1_objectives_proto_rawDesc = []byte{ 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, - 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x32, 0xbc, 0x05, 0x0a, 0x10, - 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x4d, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x20, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x5c, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x2e, 0x6f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, - 0x09, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x6f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x26, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x65, 0x72, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x10, 0x47, - 0x72, 0x61, 0x70, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x12, - 0x2c, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, - 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x75, - 0x64, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, - 0x0a, 0x09, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x6f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0b, - 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x6f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x68, 0x0a, 0x0d, 0x47, 0x72, 0x61, 0x70, 0x68, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x29, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, + 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x89, 0x01, 0x0a, 0x16, + 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x06, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x06, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x3c, 0x0a, 0x09, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0x68, 0x0a, 0x17, 0x4f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x53, 0x65, + 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x09, 0x69, 0x6e, + 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x22, 0x47, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, + 0x32, 0xac, 0x06, 0x0a, 0x10, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4d, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x20, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x42, 0x49, 0x5a, 0x47, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x70, 0x79, 0x72, 0x72, 0x61, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x79, 0x72, - 0x72, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, - 0x76, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x6f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x25, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x12, + 0x25, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x71, 0x0a, 0x10, 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x75, + 0x64, 0x67, 0x65, 0x74, 0x12, 0x2c, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x09, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x61, 0x74, 0x65, + 0x12, 0x25, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, + 0x61, 0x70, 0x68, 0x52, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x62, 0x0a, 0x0b, 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, + 0x12, 0x27, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x0d, 0x47, 0x72, 0x61, 0x70, 0x68, 0x44, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, + 0x70, 0x68, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x6e, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x12, 0x2b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, + 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2c, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, + 0x68, 0x0a, 0x17, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x42, 0x61, 0x63, 0x6b, + 0x65, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4d, 0x0a, 0x04, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x20, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x49, 0x5a, 0x47, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x79, 0x72, 0x72, 0x61, 0x2d, 0x64, 0x65, + 0x76, 0x2f, 0x70, 0x79, 0x72, 0x72, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x3b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2302,7 +2432,7 @@ func file_objectives_v1alpha1_objectives_proto_rawDescGZIP() []byte { } var file_objectives_v1alpha1_objectives_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_objectives_v1alpha1_objectives_proto_msgTypes = make([]protoimpl.MessageInfo, 32) +var file_objectives_v1alpha1_objectives_proto_msgTypes = make([]protoimpl.MessageInfo, 34) var file_objectives_v1alpha1_objectives_proto_goTypes = []interface{}{ (LabelMatcher_Type)(0), // 0: objectives.v1alpha1.LabelMatcher.Type (Alert_State)(0), // 1: objectives.v1alpha1.Alert.State @@ -2335,16 +2465,18 @@ var file_objectives_v1alpha1_objectives_proto_goTypes = []interface{}{ (*Series)(nil), // 28: objectives.v1alpha1.Series (*GraphDurationRequest)(nil), // 29: objectives.v1alpha1.GraphDurationRequest (*GraphDurationResponse)(nil), // 30: objectives.v1alpha1.GraphDurationResponse - nil, // 31: objectives.v1alpha1.Objective.LabelsEntry - nil, // 32: objectives.v1alpha1.ObjectiveStatus.LabelsEntry - nil, // 33: objectives.v1alpha1.Alert.LabelsEntry - (*durationpb.Duration)(nil), // 34: google.protobuf.Duration - (*timestamppb.Timestamp)(nil), // 35: google.protobuf.Timestamp + (*GetAvailabilityRequest)(nil), // 31: objectives.v1alpha1.GetAvailabilityRequest + (*GetAvailabilityResponse)(nil), // 32: objectives.v1alpha1.GetAvailabilityResponse + nil, // 33: objectives.v1alpha1.Objective.LabelsEntry + nil, // 34: objectives.v1alpha1.ObjectiveStatus.LabelsEntry + nil, // 35: objectives.v1alpha1.Alert.LabelsEntry + (*durationpb.Duration)(nil), // 36: google.protobuf.Duration + (*timestamppb.Timestamp)(nil), // 37: google.protobuf.Timestamp } var file_objectives_v1alpha1_objectives_proto_depIdxs = []int32{ 4, // 0: objectives.v1alpha1.ListResponse.objectives:type_name -> objectives.v1alpha1.Objective - 31, // 1: objectives.v1alpha1.Objective.labels:type_name -> objectives.v1alpha1.Objective.LabelsEntry - 34, // 2: objectives.v1alpha1.Objective.window:type_name -> google.protobuf.Duration + 33, // 1: objectives.v1alpha1.Objective.labels:type_name -> objectives.v1alpha1.Objective.LabelsEntry + 36, // 2: objectives.v1alpha1.Objective.window:type_name -> google.protobuf.Duration 5, // 3: objectives.v1alpha1.Objective.indicator:type_name -> objectives.v1alpha1.Indicator 10, // 4: objectives.v1alpha1.Objective.queries:type_name -> objectives.v1alpha1.Queries 6, // 5: objectives.v1alpha1.Indicator.ratio:type_name -> objectives.v1alpha1.Ratio @@ -2357,52 +2489,56 @@ var file_objectives_v1alpha1_objectives_proto_depIdxs = []int32{ 9, // 12: objectives.v1alpha1.BoolGauge.boolGauge:type_name -> objectives.v1alpha1.Query 11, // 13: objectives.v1alpha1.Query.matchers:type_name -> objectives.v1alpha1.LabelMatcher 0, // 14: objectives.v1alpha1.LabelMatcher.type:type_name -> objectives.v1alpha1.LabelMatcher.Type - 35, // 15: objectives.v1alpha1.GetStatusRequest.time:type_name -> google.protobuf.Timestamp + 37, // 15: objectives.v1alpha1.GetStatusRequest.time:type_name -> google.protobuf.Timestamp 14, // 16: objectives.v1alpha1.GetStatusResponse.status:type_name -> objectives.v1alpha1.ObjectiveStatus - 32, // 17: objectives.v1alpha1.ObjectiveStatus.labels:type_name -> objectives.v1alpha1.ObjectiveStatus.LabelsEntry + 34, // 17: objectives.v1alpha1.ObjectiveStatus.labels:type_name -> objectives.v1alpha1.ObjectiveStatus.LabelsEntry 15, // 18: objectives.v1alpha1.ObjectiveStatus.availability:type_name -> objectives.v1alpha1.Availability 16, // 19: objectives.v1alpha1.ObjectiveStatus.budget:type_name -> objectives.v1alpha1.Budget 19, // 20: objectives.v1alpha1.GetAlertsResponse.alerts:type_name -> objectives.v1alpha1.Alert - 33, // 21: objectives.v1alpha1.Alert.labels:type_name -> objectives.v1alpha1.Alert.LabelsEntry - 34, // 22: objectives.v1alpha1.Alert.for:type_name -> google.protobuf.Duration + 35, // 21: objectives.v1alpha1.Alert.labels:type_name -> objectives.v1alpha1.Alert.LabelsEntry + 36, // 22: objectives.v1alpha1.Alert.for:type_name -> google.protobuf.Duration 1, // 23: objectives.v1alpha1.Alert.state:type_name -> objectives.v1alpha1.Alert.State 20, // 24: objectives.v1alpha1.Alert.short:type_name -> objectives.v1alpha1.Burnrate 20, // 25: objectives.v1alpha1.Alert.long:type_name -> objectives.v1alpha1.Burnrate - 34, // 26: objectives.v1alpha1.Burnrate.window:type_name -> google.protobuf.Duration - 35, // 27: objectives.v1alpha1.GraphErrorBudgetRequest.start:type_name -> google.protobuf.Timestamp - 35, // 28: objectives.v1alpha1.GraphErrorBudgetRequest.end:type_name -> google.protobuf.Timestamp + 36, // 26: objectives.v1alpha1.Burnrate.window:type_name -> google.protobuf.Duration + 37, // 27: objectives.v1alpha1.GraphErrorBudgetRequest.start:type_name -> google.protobuf.Timestamp + 37, // 28: objectives.v1alpha1.GraphErrorBudgetRequest.end:type_name -> google.protobuf.Timestamp 27, // 29: objectives.v1alpha1.GraphErrorBudgetResponse.timeseries:type_name -> objectives.v1alpha1.Timeseries - 35, // 30: objectives.v1alpha1.GraphRateRequest.start:type_name -> google.protobuf.Timestamp - 35, // 31: objectives.v1alpha1.GraphRateRequest.end:type_name -> google.protobuf.Timestamp + 37, // 30: objectives.v1alpha1.GraphRateRequest.start:type_name -> google.protobuf.Timestamp + 37, // 31: objectives.v1alpha1.GraphRateRequest.end:type_name -> google.protobuf.Timestamp 27, // 32: objectives.v1alpha1.GraphRateResponse.timeseries:type_name -> objectives.v1alpha1.Timeseries - 35, // 33: objectives.v1alpha1.GraphErrorsRequest.start:type_name -> google.protobuf.Timestamp - 35, // 34: objectives.v1alpha1.GraphErrorsRequest.end:type_name -> google.protobuf.Timestamp + 37, // 33: objectives.v1alpha1.GraphErrorsRequest.start:type_name -> google.protobuf.Timestamp + 37, // 34: objectives.v1alpha1.GraphErrorsRequest.end:type_name -> google.protobuf.Timestamp 27, // 35: objectives.v1alpha1.GraphErrorsResponse.timeseries:type_name -> objectives.v1alpha1.Timeseries 28, // 36: objectives.v1alpha1.Timeseries.series:type_name -> objectives.v1alpha1.Series - 35, // 37: objectives.v1alpha1.GraphDurationRequest.start:type_name -> google.protobuf.Timestamp - 35, // 38: objectives.v1alpha1.GraphDurationRequest.end:type_name -> google.protobuf.Timestamp + 37, // 37: objectives.v1alpha1.GraphDurationRequest.start:type_name -> google.protobuf.Timestamp + 37, // 38: objectives.v1alpha1.GraphDurationRequest.end:type_name -> google.protobuf.Timestamp 27, // 39: objectives.v1alpha1.GraphDurationResponse.timeseries:type_name -> objectives.v1alpha1.Timeseries - 2, // 40: objectives.v1alpha1.ObjectiveService.List:input_type -> objectives.v1alpha1.ListRequest - 12, // 41: objectives.v1alpha1.ObjectiveService.GetStatus:input_type -> objectives.v1alpha1.GetStatusRequest - 17, // 42: objectives.v1alpha1.ObjectiveService.GetAlerts:input_type -> objectives.v1alpha1.GetAlertsRequest - 21, // 43: objectives.v1alpha1.ObjectiveService.GraphErrorBudget:input_type -> objectives.v1alpha1.GraphErrorBudgetRequest - 23, // 44: objectives.v1alpha1.ObjectiveService.GraphRate:input_type -> objectives.v1alpha1.GraphRateRequest - 25, // 45: objectives.v1alpha1.ObjectiveService.GraphErrors:input_type -> objectives.v1alpha1.GraphErrorsRequest - 29, // 46: objectives.v1alpha1.ObjectiveService.GraphDuration:input_type -> objectives.v1alpha1.GraphDurationRequest - 2, // 47: objectives.v1alpha1.ObjectiveBackendService.List:input_type -> objectives.v1alpha1.ListRequest - 3, // 48: objectives.v1alpha1.ObjectiveService.List:output_type -> objectives.v1alpha1.ListResponse - 13, // 49: objectives.v1alpha1.ObjectiveService.GetStatus:output_type -> objectives.v1alpha1.GetStatusResponse - 18, // 50: objectives.v1alpha1.ObjectiveService.GetAlerts:output_type -> objectives.v1alpha1.GetAlertsResponse - 22, // 51: objectives.v1alpha1.ObjectiveService.GraphErrorBudget:output_type -> objectives.v1alpha1.GraphErrorBudgetResponse - 24, // 52: objectives.v1alpha1.ObjectiveService.GraphRate:output_type -> objectives.v1alpha1.GraphRateResponse - 26, // 53: objectives.v1alpha1.ObjectiveService.GraphErrors:output_type -> objectives.v1alpha1.GraphErrorsResponse - 30, // 54: objectives.v1alpha1.ObjectiveService.GraphDuration:output_type -> objectives.v1alpha1.GraphDurationResponse - 3, // 55: objectives.v1alpha1.ObjectiveBackendService.List:output_type -> objectives.v1alpha1.ListResponse - 48, // [48:56] is the sub-list for method output_type - 40, // [40:48] is the sub-list for method input_type - 40, // [40:40] is the sub-list for extension type_name - 40, // [40:40] is the sub-list for extension extendee - 0, // [0:40] is the sub-list for field type_name + 36, // 40: objectives.v1alpha1.GetAvailabilityRequest.window:type_name -> google.protobuf.Duration + 5, // 41: objectives.v1alpha1.GetAvailabilityRequest.indicator:type_name -> objectives.v1alpha1.Indicator + 2, // 42: objectives.v1alpha1.ObjectiveService.List:input_type -> objectives.v1alpha1.ListRequest + 12, // 43: objectives.v1alpha1.ObjectiveService.GetStatus:input_type -> objectives.v1alpha1.GetStatusRequest + 17, // 44: objectives.v1alpha1.ObjectiveService.GetAlerts:input_type -> objectives.v1alpha1.GetAlertsRequest + 21, // 45: objectives.v1alpha1.ObjectiveService.GraphErrorBudget:input_type -> objectives.v1alpha1.GraphErrorBudgetRequest + 23, // 46: objectives.v1alpha1.ObjectiveService.GraphRate:input_type -> objectives.v1alpha1.GraphRateRequest + 25, // 47: objectives.v1alpha1.ObjectiveService.GraphErrors:input_type -> objectives.v1alpha1.GraphErrorsRequest + 29, // 48: objectives.v1alpha1.ObjectiveService.GraphDuration:input_type -> objectives.v1alpha1.GraphDurationRequest + 31, // 49: objectives.v1alpha1.ObjectiveService.GetAvailability:input_type -> objectives.v1alpha1.GetAvailabilityRequest + 2, // 50: objectives.v1alpha1.ObjectiveBackendService.List:input_type -> objectives.v1alpha1.ListRequest + 3, // 51: objectives.v1alpha1.ObjectiveService.List:output_type -> objectives.v1alpha1.ListResponse + 13, // 52: objectives.v1alpha1.ObjectiveService.GetStatus:output_type -> objectives.v1alpha1.GetStatusResponse + 18, // 53: objectives.v1alpha1.ObjectiveService.GetAlerts:output_type -> objectives.v1alpha1.GetAlertsResponse + 22, // 54: objectives.v1alpha1.ObjectiveService.GraphErrorBudget:output_type -> objectives.v1alpha1.GraphErrorBudgetResponse + 24, // 55: objectives.v1alpha1.ObjectiveService.GraphRate:output_type -> objectives.v1alpha1.GraphRateResponse + 26, // 56: objectives.v1alpha1.ObjectiveService.GraphErrors:output_type -> objectives.v1alpha1.GraphErrorsResponse + 30, // 57: objectives.v1alpha1.ObjectiveService.GraphDuration:output_type -> objectives.v1alpha1.GraphDurationResponse + 32, // 58: objectives.v1alpha1.ObjectiveService.GetAvailability:output_type -> objectives.v1alpha1.GetAvailabilityResponse + 3, // 59: objectives.v1alpha1.ObjectiveBackendService.List:output_type -> objectives.v1alpha1.ListResponse + 51, // [51:60] is the sub-list for method output_type + 42, // [42:51] is the sub-list for method input_type + 42, // [42:42] is the sub-list for extension type_name + 42, // [42:42] is the sub-list for extension extendee + 0, // [0:42] is the sub-list for field type_name } func init() { file_objectives_v1alpha1_objectives_proto_init() } @@ -2759,6 +2895,30 @@ func file_objectives_v1alpha1_objectives_proto_init() { return nil } } + file_objectives_v1alpha1_objectives_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAvailabilityRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_objectives_v1alpha1_objectives_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAvailabilityResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_objectives_v1alpha1_objectives_proto_msgTypes[3].OneofWrappers = []interface{}{ (*Indicator_Ratio)(nil), @@ -2771,7 +2931,7 @@ func file_objectives_v1alpha1_objectives_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_objectives_v1alpha1_objectives_proto_rawDesc, NumEnums: 2, - NumMessages: 32, + NumMessages: 34, NumExtensions: 0, NumServices: 2, }, diff --git a/proto/objectives/v1alpha1/objectives.proto b/proto/objectives/v1alpha1/objectives.proto index eb1b5933b..3eecfaae6 100644 --- a/proto/objectives/v1alpha1/objectives.proto +++ b/proto/objectives/v1alpha1/objectives.proto @@ -15,6 +15,7 @@ service ObjectiveService { rpc GraphRate(GraphRateRequest) returns (GraphRateResponse) {} rpc GraphErrors(GraphErrorsRequest) returns (GraphErrorsResponse) {} rpc GraphDuration(GraphDurationRequest) returns (GraphDurationResponse) {} + rpc GetAvailability(GetAvailabilityRequest) returns (GetAvailabilityResponse) {} } service ObjectiveBackendService { @@ -208,3 +209,13 @@ message GraphDurationRequest { message GraphDurationResponse { repeated Timeseries timeseries = 1; } + +message GetAvailabilityRequest { + google.protobuf.Duration window = 1; + Indicator indicator = 2; +} + +message GetAvailabilityResponse { + int64 total = 1; + int64 errors = 2; +} diff --git a/proto/objectives/v1alpha1/objectivesv1alpha1connect/objectives.connect.go b/proto/objectives/v1alpha1/objectivesv1alpha1connect/objectives.connect.go index dcda41285..40b1e76a6 100644 --- a/proto/objectives/v1alpha1/objectivesv1alpha1connect/objectives.connect.go +++ b/proto/objectives/v1alpha1/objectivesv1alpha1connect/objectives.connect.go @@ -36,6 +36,7 @@ type ObjectiveServiceClient interface { GraphRate(context.Context, *connect_go.Request[v1alpha1.GraphRateRequest]) (*connect_go.Response[v1alpha1.GraphRateResponse], error) GraphErrors(context.Context, *connect_go.Request[v1alpha1.GraphErrorsRequest]) (*connect_go.Response[v1alpha1.GraphErrorsResponse], error) GraphDuration(context.Context, *connect_go.Request[v1alpha1.GraphDurationRequest]) (*connect_go.Response[v1alpha1.GraphDurationResponse], error) + GetAvailability(context.Context, *connect_go.Request[v1alpha1.GetAvailabilityRequest]) (*connect_go.Response[v1alpha1.GetAvailabilityResponse], error) } // NewObjectiveServiceClient constructs a client for the objectives.v1alpha1.ObjectiveService @@ -83,6 +84,11 @@ func NewObjectiveServiceClient(httpClient connect_go.HTTPClient, baseURL string, baseURL+"/objectives.v1alpha1.ObjectiveService/GraphDuration", opts..., ), + getAvailability: connect_go.NewClient[v1alpha1.GetAvailabilityRequest, v1alpha1.GetAvailabilityResponse]( + httpClient, + baseURL+"/objectives.v1alpha1.ObjectiveService/GetAvailability", + opts..., + ), } } @@ -95,6 +101,7 @@ type objectiveServiceClient struct { graphRate *connect_go.Client[v1alpha1.GraphRateRequest, v1alpha1.GraphRateResponse] graphErrors *connect_go.Client[v1alpha1.GraphErrorsRequest, v1alpha1.GraphErrorsResponse] graphDuration *connect_go.Client[v1alpha1.GraphDurationRequest, v1alpha1.GraphDurationResponse] + getAvailability *connect_go.Client[v1alpha1.GetAvailabilityRequest, v1alpha1.GetAvailabilityResponse] } // List calls objectives.v1alpha1.ObjectiveService.List. @@ -132,6 +139,11 @@ func (c *objectiveServiceClient) GraphDuration(ctx context.Context, req *connect return c.graphDuration.CallUnary(ctx, req) } +// GetAvailability calls objectives.v1alpha1.ObjectiveService.GetAvailability. +func (c *objectiveServiceClient) GetAvailability(ctx context.Context, req *connect_go.Request[v1alpha1.GetAvailabilityRequest]) (*connect_go.Response[v1alpha1.GetAvailabilityResponse], error) { + return c.getAvailability.CallUnary(ctx, req) +} + // ObjectiveServiceHandler is an implementation of the objectives.v1alpha1.ObjectiveService service. type ObjectiveServiceHandler interface { List(context.Context, *connect_go.Request[v1alpha1.ListRequest]) (*connect_go.Response[v1alpha1.ListResponse], error) @@ -141,6 +153,7 @@ type ObjectiveServiceHandler interface { GraphRate(context.Context, *connect_go.Request[v1alpha1.GraphRateRequest]) (*connect_go.Response[v1alpha1.GraphRateResponse], error) GraphErrors(context.Context, *connect_go.Request[v1alpha1.GraphErrorsRequest]) (*connect_go.Response[v1alpha1.GraphErrorsResponse], error) GraphDuration(context.Context, *connect_go.Request[v1alpha1.GraphDurationRequest]) (*connect_go.Response[v1alpha1.GraphDurationResponse], error) + GetAvailability(context.Context, *connect_go.Request[v1alpha1.GetAvailabilityRequest]) (*connect_go.Response[v1alpha1.GetAvailabilityResponse], error) } // NewObjectiveServiceHandler builds an HTTP handler from the service implementation. It returns the @@ -185,6 +198,11 @@ func NewObjectiveServiceHandler(svc ObjectiveServiceHandler, opts ...connect_go. svc.GraphDuration, opts..., )) + mux.Handle("/objectives.v1alpha1.ObjectiveService/GetAvailability", connect_go.NewUnaryHandler( + "/objectives.v1alpha1.ObjectiveService/GetAvailability", + svc.GetAvailability, + opts..., + )) return "/objectives.v1alpha1.ObjectiveService/", mux } @@ -219,6 +237,10 @@ func (UnimplementedObjectiveServiceHandler) GraphDuration(context.Context, *conn return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("objectives.v1alpha1.ObjectiveService.GraphDuration is not implemented")) } +func (UnimplementedObjectiveServiceHandler) GetAvailability(context.Context, *connect_go.Request[v1alpha1.GetAvailabilityRequest]) (*connect_go.Response[v1alpha1.GetAvailabilityResponse], error) { + return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("objectives.v1alpha1.ObjectiveService.GetAvailability is not implemented")) +} + // ObjectiveBackendServiceClient is a client for the objectives.v1alpha1.ObjectiveBackendService // service. type ObjectiveBackendServiceClient interface { diff --git a/ui/package-lock.json b/ui/package-lock.json index 654bc9f64..c4d407341 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -15,10 +15,12 @@ "react": "^18.1.0", "react-bootstrap": "^2.7.2", "react-dom": "^18.2.0", + "react-hook-form": "^7.36.1", "react-query": "^3.39.2", "react-router-dom": "^6.8.1", "uplot": "1.6.24", - "uplot-react": "^1.1.4" + "uplot-react": "^1.1.4", + "yaml": "^2.2.1" }, "devDependencies": { "@bufbuild/connect-web": "^0.8.1", @@ -4682,9 +4684,9 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -6046,6 +6048,15 @@ "node": ">=10" } }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/cra-template-typescript": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/cra-template-typescript/-/cra-template-typescript-1.2.0.tgz", @@ -6422,6 +6433,15 @@ "postcss": "^8.2.15" } }, + "node_modules/cssnano/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/csso": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", @@ -8166,9 +8186,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -8527,6 +8547,15 @@ "node": ">=6" } }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", @@ -12389,6 +12418,15 @@ } } }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/postcss-loader": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", @@ -13525,6 +13563,21 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", "dev": true }, + "node_modules/react-hook-form": { + "version": "7.36.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.36.1.tgz", + "integrity": "sha512-EbYYkCG2p8ywe7ikOH2l02lAFMrrrslZi1I8fqd8ifDGNAkhomHZQzQsP6ksvzrWBKntRe8b5L5L7Zsd+Gm02Q==", + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -17015,12 +17068,11 @@ } }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz", + "integrity": "sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==", "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/yargs": { @@ -20435,9 +20487,9 @@ "requires": {} }, "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-escapes": { @@ -21466,6 +21518,14 @@ "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" + }, + "dependencies": { + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + } } }, "cra-template-typescript": { @@ -21669,6 +21729,14 @@ "cssnano-preset-default": "^5.2.7", "lilconfig": "^2.0.3", "yaml": "^1.10.2" + }, + "dependencies": { + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + } } }, "cssnano-preset-default": { @@ -23031,9 +23099,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -23300,6 +23368,12 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true } } }, @@ -26095,6 +26169,14 @@ "requires": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" + }, + "dependencies": { + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + } } }, "postcss-loader": { @@ -26871,6 +26953,12 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", "dev": true }, + "react-hook-form": { + "version": "7.36.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.36.1.tgz", + "integrity": "sha512-EbYYkCG2p8ywe7ikOH2l02lAFMrrrslZi1I8fqd8ifDGNAkhomHZQzQsP6ksvzrWBKntRe8b5L5L7Zsd+Gm02Q==", + "requires": {} + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -29540,10 +29628,9 @@ "dev": true }, "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz", + "integrity": "sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==" }, "yargs": { "version": "16.2.0", diff --git a/ui/package.json b/ui/package.json index 866d712f4..93703e16b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -11,10 +11,12 @@ "react": "^18.1.0", "react-bootstrap": "^2.7.2", "react-dom": "^18.2.0", + "react-hook-form": "^7.36.1", "react-query": "^3.39.2", "react-router-dom": "^6.8.1", "uplot": "1.6.24", - "uplot-react": "^1.1.4" + "uplot-react": "^1.1.4", + "yaml": "^2.2.1" }, "devDependencies": { "@bufbuild/connect-web": "^0.8.1", diff --git a/ui/src/App.tsx b/ui/src/App.tsx index e4cbd1b1c..c2ac2f54f 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -2,6 +2,7 @@ import React from 'react' import {BrowserRouter, Route, Routes} from 'react-router-dom' import List from './pages/List' import Detail from './pages/Detail' +import Create from './pages/Create' import {LabelMatcher, Latency, Objective} from './proto/objectives/v1alpha1/objectives_pb' import {QueryClient, QueryClientProvider} from 'react-query' import {formatDuration} from './duration' @@ -31,6 +32,7 @@ const App = () => { } /> } /> + } /> diff --git a/ui/src/components/tiles/AvailabilityTile.tsx b/ui/src/components/tiles/AvailabilityTile.tsx new file mode 100644 index 000000000..2169080fd --- /dev/null +++ b/ui/src/components/tiles/AvailabilityTile.tsx @@ -0,0 +1,95 @@ +import React from 'react' +import {Spinner} from 'react-bootstrap' +import {QueryStatus} from 'react-query/types/core/types' +import {ObjectiveType} from '../../App' +import {QueryResponse} from '../../proto/prometheus/v1/prometheus_pb' + +interface AvailabilityProps { + target: number + objectiveType: ObjectiveType + total: CounterProps + errors: CounterProps +} + +interface CounterProps { + count: number + status: QueryStatus +} + +const AvailabilityTile = ({ + target, + objectiveType, + total, + errors, +}: AvailabilityProps): JSX.Element => { + const headline =
Availability
+ + if (total.status === 'idle' || errors.status === 'idle') { + return
{headline}
+ } + + if (total.status === 'loading' || errors.status === 'loading') { + return ( +
+ {headline} + +
+ ) + } + + if (total.status === 'error' || errors.status === 'error') { + return ( +
+ {headline} +

Error

+
+ ) + } + + const percentage = 1 - errors.count / total.count + + return ( +
target ? 'good' : 'bad'}> + {headline} +

{(100 * percentage).toFixed(3)}%

+ + + + + + + + + + + +
{objectiveType === ObjectiveType.Latency ? 'Slow:' : 'Errors:'}{Math.floor(errors.count).toLocaleString()}
Total:{Math.floor(total.count).toLocaleString()}
+
+ ) +} + +export default AvailabilityTile + +export const responseToCounterProps = ( + response: QueryResponse | null, + status: QueryStatus, +): CounterProps => { + if (status === 'success' && response?.options.case === 'vector') { + return { + count: response.options.value.samples[0].value, + status, + } + } + + return {count: 0, status} +} diff --git a/ui/src/components/tiles/ErrorBudgetTile.tsx b/ui/src/components/tiles/ErrorBudgetTile.tsx new file mode 100644 index 000000000..83b0b35b3 --- /dev/null +++ b/ui/src/components/tiles/ErrorBudgetTile.tsx @@ -0,0 +1,63 @@ +import React from 'react' +import {Spinner} from 'react-bootstrap' +import {QueryStatus} from 'react-query/types/core/types' + +interface ErrorBudgetTileProps { + target: number + total: CounterProps + errors: CounterProps +} + +interface CounterProps { + count: number + status: QueryStatus +} + +const ErrorBudgetTile = ({target, total, errors}: ErrorBudgetTileProps) => { + const headline =
Error Budget
+ + if (total.status === 'idle' || errors.status === 'idle') { + return
{headline}
+ } + + if (total.status === 'loading' || errors.status === 'loading') { + return ( +
+ {headline} + +
+ ) + } + + if (total.status === 'error' || errors.status === 'error') { + return ( +
+ {headline} +

Error

+
+ ) + } + + const budget = 1 - target + const unavailability = errors.count / total.count + const remainingBudget = (budget - unavailability) / budget + + return ( +
0 ? 'good' : 'bad'}> + {headline} +

{(100 * remainingBudget).toFixed(3)}%

+
+ ) +} + +export default ErrorBudgetTile diff --git a/ui/src/components/tiles/ObjectiveTile.tsx b/ui/src/components/tiles/ObjectiveTile.tsx new file mode 100644 index 000000000..f50b7692b --- /dev/null +++ b/ui/src/components/tiles/ObjectiveTile.tsx @@ -0,0 +1,54 @@ +import React from 'react' +import {ObjectiveType} from '../../App' +import {formatDuration} from '../../duration' + +interface ObjectiveTileProps { + window: number + target: number + objectiveType: ObjectiveType + latency?: number +} + +const ObjectiveTile = ({target, latency, window, objectiveType}: ObjectiveTileProps) => { + if (window === 0 || target === 0) { + return ( +
+
Objective
+

+
+ ) + } + + switch (objectiveType) { + case ObjectiveType.Ratio: + return ( +
+
Objective
+

{(100 * target).toFixed(3)}%

+ <>in {formatDuration(window * 1000)} +
+ ) + case ObjectiveType.Latency: + return ( +
+
Objective
+

{(100 * target).toFixed(3)}%

+ <>in {formatDuration(window * 1000)} +
+

faster than {formatDuration(latency ?? 0)}

+
+ ) + case ObjectiveType.BoolGauge: + return ( +
+
Objective
+

{(100 * target).toFixed(3)}%

+ <>in {formatDuration(window * 1000)} +
+ ) + default: + return
+ } +} + +export default ObjectiveTile diff --git a/ui/src/components/tiles/Tiles.scss b/ui/src/components/tiles/Tiles.scss new file mode 100644 index 000000000..40fca7464 --- /dev/null +++ b/ui/src/components/tiles/Tiles.scss @@ -0,0 +1,69 @@ +.tiles { + width: 100%; + display: grid; + padding: 0 15px; + grid-template-columns: repeat(1, 1fr); + column-gap: 25px; + row-gap: 25px; + justify-items: stretch; + + @media (min-width: 576px) { + grid-template-columns: repeat(2, 1fr); + column-gap: 50px; + row-gap: 50px; + } + @media (min-width: 992px) { + grid-template-columns: repeat(3, 1fr); + column-gap: 75px; + row-gap: 75px; + } + + div { + padding: 35px; + background-color: $gray-300; + color: $gray-900; + border-radius: 8px; + + h2, h6 { + font-family: $font-family-sans-serif; + } + + h2 { + font-weight: 400; + font-size: 40px; + margin-bottom: 0; + } + + h6 { + font-weight: 600; + font-size: 20px; + } + + .headline, .details { + opacity: 0.5; + } + + .metric { + display: inline-block; + margin-right: 0.5rem; + } + + .details { + font-weight: 500; + } + + &.good { + background-color: $green; + color: $green-text; + } + + &.bad { + background-color: $red; + color: $red-text; + } + + h2.error { + color: $red; + } + } +} diff --git a/ui/src/components/tiles/Tiles.tsx b/ui/src/components/tiles/Tiles.tsx new file mode 100644 index 000000000..1b40a0fff --- /dev/null +++ b/ui/src/components/tiles/Tiles.tsx @@ -0,0 +1,9 @@ +interface TilesProps { + children: React.ReactNode +} + +const Tiles = (props: TilesProps) => { + return
{props.children}
+} + +export default Tiles diff --git a/ui/src/crd/servicelevelobjectives.ts b/ui/src/crd/servicelevelobjectives.ts new file mode 100644 index 000000000..8b5b44913 --- /dev/null +++ b/ui/src/crd/servicelevelobjectives.ts @@ -0,0 +1,57 @@ +// apiVersion: pyrra.dev/v1alpha1 +// kind: ServiceLevelObjective +// metadata: +// creationTimestamp: null +// labels: +// prometheus: k8s +// pyrra.dev/team: parca +// role: alert-rules +// name: parca-grpc-query-errors +// namespace: parca +// spec: +// alerting: {} +// description: "" +// indicator: +// ratio: +// errors: +// metric: grpc_server_handled_total{grpc_service="parca.query.v1alpha1.QueryService",grpc_method="Query",grpc_code=~"Aborted|Unavailable|Internal|Unknown|Unimplemented|DataLoss"} +// grouping: null +// total: +// metric: grpc_server_handled_total{grpc_service="parca.query.v1alpha1.QueryService",grpc_method="Query"} +// target: "99" +// window: 2w +// status: {} + +export interface Objective { + apiVersion: 'pyrra.dev/v1alpha1' + kind: 'ServiceLevelObjective' + metadata: Metadata + spec: ObjectiveSpec +} + +export interface Metadata { + name: string + namespace: string + labels?: {[key: string]: string} +} + +export interface ObjectiveSpec { + description: string + target: string + window: string + indicator: Indicator +} + +export interface Indicator { + ratio?: IndicatorRatio +} + +export interface IndicatorRatio { + errors: Metric + total: Metric + grouping?: string[] +} + +export interface Metric { + metric: string +} diff --git a/ui/src/index.scss b/ui/src/index.scss index b387a4a26..59df5091a 100644 --- a/ui/src/index.scss +++ b/ui/src/index.scss @@ -156,3 +156,4 @@ a.external-prometheus { @import 'components/Navbar'; @import 'components/AlertsTable'; @import 'components/graphs/BurnrateGraph'; +@import 'components/tiles/Tiles'; diff --git a/ui/src/labels.tsx b/ui/src/labels.tsx index 0c682e092..9b0500dcb 100644 --- a/ui/src/labels.tsx +++ b/ui/src/labels.tsx @@ -21,6 +21,14 @@ export const labelsString = (lset: Labels | undefined): string => { export const labelValues = (lset: Labels): string[] => Object.values(lset) +export const removeMetricName = (lset: Labels | undefined): Labels => { + if (lset === undefined) { + return {} + } + const {__name__: _, ...rest} = lset + return {...rest} +} + export const parseLabels = (expr: string | null): Labels => { if (expr == null) { return {} diff --git a/ui/src/pages/Create.tsx b/ui/src/pages/Create.tsx new file mode 100644 index 000000000..d4131a7fd --- /dev/null +++ b/ui/src/pages/Create.tsx @@ -0,0 +1,447 @@ +import Navbar from '../components/Navbar' +import React, {useMemo, useState} from 'react' +import {Button, Col, Container, Form, InputGroup, Row, Table} from 'react-bootstrap' +import {useForm} from 'react-hook-form' + +import AvailabilityTile from '../components/tiles/AvailabilityTile' +import ObjectiveTile from '../components/tiles/ObjectiveTile' +import Tiles from '../components/tiles/Tiles' +import ErrorBudgetTile from '../components/tiles/ErrorBudgetTile' +import {createConnectTransport, createPromiseClient} from '@bufbuild/connect-web' +import {API_BASEPATH, ObjectiveType} from '../App' +import {PrometheusService} from '../proto/prometheus/v1/prometheus_connectweb' +import {QueryResponse, Sample} from '../proto/prometheus/v1/prometheus_pb' +import {labelsString, MetricName, removeMetricName} from '../labels' +import ErrorBudgetGraph from '../components/graphs/ErrorBudgetGraph' +import RequestsGraph from '../components/graphs/RequestsGraph' +import ErrorsGraph from '../components/graphs/ErrorsGraph' +import uPlot from 'uplot' +import {parseDuration} from '../duration' +import {Objective} from '../crd/servicelevelobjectives' +import {stringify} from 'yaml' + +const formFieldName = 'name' +const formFieldSLITotal = 'sli-total' +const formFieldSLIErrors = 'sli-error' +const formFieldObjective = 'objective' +const formFieldWindow = 'window' + +const Create = () => { + const baseUrl = API_BASEPATH === undefined ? 'http://localhost:9099' : API_BASEPATH + + const promClient = useMemo(() => { + return createPromiseClient(PrometheusService, createConnectTransport({baseUrl})) + }, [baseUrl]) + + const [objectiveTotal, setObjectiveTotal] = useState() + const [objectiveErrors, setObjectiveErrors] = useState() + const [objectiveTotalSamples, setObjectiveTotalSamples] = useState([]) + const [objectiveErrorsSamples, setObjectiveErrorsSamples] = useState([]) + const [enableGraphs, setEnableGraphs] = useState(false) + + const { + register, + handleSubmit, + getValues, + watch, + formState: {errors, touchedFields, isValid}, + } = useForm({ + mode: 'onTouched', + reValidateMode: 'onChange', + shouldFocusError: true, + }) + + const now = Date.now() + const from = now - 24 * 60 * 60 * 1000 + + const queryTotal = () => { + promClient + .query({ + query: increaseQuery(getValues(formFieldSLITotal), getValues(formFieldWindow)), + time: BigInt(Math.floor(now / 1000)), + }) + .then((resp: QueryResponse) => { + if (resp.options.case === 'vector') { + setObjectiveTotalSamples(resp.options.value.samples) + setObjectiveTotal( + // sum up all values for the total number + resp.options.value.samples + .map((s: Sample): number => s.value) + .reduce( + (previousValue: number, currentValue: number): number => + previousValue + currentValue, + ), + ) + } + }) + .catch((err) => console.warn(err)) + } + + const queryErrors = () => { + promClient + .query({ + query: increaseQuery(getValues(formFieldSLIErrors), getValues(formFieldWindow)), + time: BigInt(Math.floor(now / 1000)), + }) + .then((resp: QueryResponse) => { + if (resp.options.case === 'vector') { + if (resp.options.value.samples.length === 0) { + setObjectiveErrorsSamples([]) + setObjectiveErrors(0) + return + } + + setObjectiveErrorsSamples(resp.options.value.samples) + setObjectiveErrors( + // sum up all values for the total error number + resp.options.value.samples + .map((s: Sample): number => s.value) + .reduce( + (previousValue: number, currentValue: number): number => + previousValue + currentValue, + ), + ) + } + }) + .catch((err) => console.log(err)) + } + + const uPlotCursor: uPlot.Cursor = { + y: false, + lock: true, + sync: { + key: 'detail', + }, + } + + // TODO: Validate against generated types + const objective: Objective = { + apiVersion: 'pyrra.dev/v1alpha1', + kind: 'ServiceLevelObjective', + metadata: { + name: getValues(formFieldName), + namespace: 'default', + }, + spec: { + description: getValues('description'), + target: getValues(formFieldObjective), + window: getValues(formFieldWindow), + indicator: { + ratio: { + errors: { + metric: getValues(formFieldSLIErrors), + }, + total: { + metric: getValues(formFieldSLITotal), + }, + }, + }, + }, + } + + return ( + <> + + + + +

Create a SLO

+ +
+ + +
{ + console.log('submit data', data) + })} + validated={isValid}> + + Name + + + + Please choose a name for your SLO. + + + + Give this SLO a name that has meaning for others on your team. + + + + + Description + + + + + + Objective + + + + + + Window + + + + + + +
+ + +

Indicator

+ +
+ + + +
Total
+
+ + console.log('query the shit')} + /> + + + + Summing up the values of all selected series results in the total number of + requests in 4w. + + + + {objectiveTotalSamples + .sort((a: Sample, b: Sample) => b.value - a.value) + .map((s: Sample) => ( + + + + + ))} + {objectiveTotalSamples.length > 0 ? ( + + + + + ) : ( + <> + )} + +
+ + {s.metric.__name__} + {labelsString(removeMetricName(s.metric))} + + {Math.floor(s.value)}
Total + {Math.floor(objectiveTotal ?? 0)} +
+
+
+ + + Errors + + + + + + Summing up the values of all selected series results in the total number of + errors in 4w. + + + + {objectiveErrorsSamples + .sort((a: Sample, b: Sample) => b.value - a.value) + .map((s: Sample) => ( + + + + + ))} + {objectiveErrorsSamples.length > 0 ? ( + + + + + ) : ( + <> + )} + +
+ + {s.metric[MetricName]} + {labelsString(removeMetricName(s.metric))} + + {Math.floor(s.value)}
Total + {Math.floor(objectiveErrors ?? 0)} +
+
+
+
+
+ + + +
+ + + + {isValid && ( + + )} + {objectiveErrors !== undefined && objectiveTotal !== undefined ? ( + <> + + + + ) : ( + <> + )} + + + + + + +
+ + + + + + + {enableGraphs ? ( + <> + + {}} + /> + + + + {}} + /> + + + {}} + /> + + + + ) : ( + <> + )} +
+ +
+ + +

Config

+
+              {stringify(objective, null, 2)}
+            
+ +
+
+ + ) +} + +export default Create + +const increaseQuery = (matchers: string, window: string): string => + `increase(${matchers}[${window}]) > 0` + +const errorsRangeQuery = (errors: string, total: string): string => + `sum(rate(${errors}[5m])) / sum(rate(${total}[5m]))` diff --git a/ui/src/pages/Detail.scss b/ui/src/pages/Detail.scss index e86f21386..bb87b7adb 100644 --- a/ui/src/pages/Detail.scss +++ b/ui/src/pages/Detail.scss @@ -9,75 +9,6 @@ } } - .metrics { - width: 100%; - display: grid; - grid-template-columns: repeat(1, 1fr); - column-gap: 25px; - row-gap: 25px; - justify-items: stretch; - - @media (min-width: 576px) { - grid-template-columns: repeat(2, 1fr); - column-gap: 50px; - row-gap: 50px; - } - @media (min-width: 992px) { - grid-template-columns: repeat(3, 1fr); - column-gap: 75px; - row-gap: 75px; - } - - div { - padding: 35px; - background-color: $gray-300; - color: $gray-900; - border-radius: 8px; - - h2, h6 { - font-family: $font-family-sans-serif; - } - - h2 { - font-weight: 400; - font-size: 40px; - margin-bottom: 0; - } - - h6 { - font-weight: 600; - font-size: 20px; - } - - .headline, .details { - opacity: 0.5; - } - - .metric { - display: inline-block; - margin-right: 0.5rem; - } - - .details { - font-weight: 500; - } - - &.good { - background-color: $green; - color: $green-text; - } - - &.bad { - background-color: $red; - color: $red-text; - } - - h2.error { - color: $red; - } - } - } - .timerange { background: linear-gradient(0deg, rgba(0, 0, 0, 0) 45%, $gray-300 50%, rgba(0, 0, 0, 0) 55%); diff --git a/ui/src/pages/Detail.tsx b/ui/src/pages/Detail.tsx index cc30ef221..91e6d621c 100644 --- a/ui/src/pages/Detail.tsx +++ b/ui/src/pages/Detail.tsx @@ -11,13 +11,7 @@ import { Spinner, Tooltip as OverlayTooltip, } from 'react-bootstrap' -import { - API_BASEPATH, - hasObjectiveType, - latencyTarget, - ObjectiveType, - renderLatencyTarget, -} from '../App' +import {API_BASEPATH, hasObjectiveType, latencyTarget, ObjectiveType} from '../App' import Navbar from '../components/Navbar' import {MetricName, parseLabels} from '../labels' import ErrorBudgetGraph from '../components/graphs/ErrorBudgetGraph' @@ -34,6 +28,10 @@ import {replaceInterval, usePrometheusQuery} from '../prometheus' import {useObjectivesList} from '../objectives' import {Objective} from '../proto/objectives/v1alpha1/objectives_pb' import {formatDuration, parseDuration} from '../duration' +import AvailabilityTile, {responseToCounterProps} from '../components/tiles/AvailabilityTile' +import ObjectiveTile from '../components/tiles/ObjectiveTile' +import ErrorBudgetTile from '../components/tiles/ErrorBudgetTile' +import Tiles from '../components/tiles/Tiles' const Detail = () => { const baseUrl = API_BASEPATH === undefined ? 'http://localhost:9099' : API_BASEPATH @@ -105,6 +103,8 @@ const Detail = () => { {enabled: objectiveStatus === 'success' && objective?.queries?.countTotal !== undefined}, ) + const totalCounterProps = responseToCounterProps(totalResponse, totalStatus) + const {response: errorResponse, status: errorStatus} = usePrometheusQuery( promClient, objective?.queries?.countErrors ?? '', @@ -112,6 +112,8 @@ const Detail = () => { {enabled: objectiveStatus === 'success' && objective?.queries?.countTotal !== undefined}, ) + const errorCounterProps = responseToCounterProps(errorResponse, errorStatus) + const updateTimeRange = useCallback( (from: number, to: number, absolute: boolean) => { let fromStr = from.toString() @@ -197,182 +199,6 @@ const Detail = () => { const objectiveType = hasObjectiveType(objective) - const renderObjective = () => { - switch (objectiveType) { - case ObjectiveType.Ratio: - return ( -
-
Objective
-

{(100 * objective.target).toFixed(3)}%

- <>in {formatDuration(Number(objective.window?.seconds) * 1000)} -
- ) - case ObjectiveType.BoolGauge: - return ( -
-
Objective
-

{(100 * objective.target).toFixed(3)}%

- <>in {formatDuration(Number(objective.window?.seconds) * 1000)} -
- ) - case ObjectiveType.Latency: - return ( -
-
Objective
-

{(100 * objective.target).toFixed(3)}%

- <>in {formatDuration(Number(objective.window?.seconds) * 1000)} -
-

faster than {renderLatencyTarget(objective)}

-
- ) - default: - return
- } - } - - const renderAvailability = () => { - const headline =
Availability
- if ( - totalStatus === 'loading' || - totalStatus === 'idle' || - errorStatus === 'loading' || - errorStatus === 'idle' - ) { - return ( -
- {headline} - -
- ) - } - - if (totalStatus === 'success' && errorStatus === 'success') { - if (totalResponse?.options.case === 'vector' && errorResponse?.options.case === 'vector') { - let errors = 0 - if (errorResponse.options.value.samples.length > 0) { - errors = errorResponse.options.value.samples[0].value - } - - let total = 1 - if (totalResponse.options.value.samples.length > 0) { - total = totalResponse.options.value.samples[0].value - } - - const percentage = 1 - errors / total - - return ( -
objective.target ? 'good' : 'bad'}> - {headline} -

{(100 * percentage).toFixed(3)}%

- - - - - - - - - - - -
{objectiveType === ObjectiveType.Latency ? 'Slow:' : 'Errors:'}{Math.floor(errors).toLocaleString()}
Total:{Math.floor(total).toLocaleString()}
-
- ) - } else { - return ( -
- {headline} -

No data

-
- ) - } - } - - return ( -
- <> - {headline} -

Error

- -
- ) - } - - const renderErrorBudget = () => { - const headline =
Error Budget
- - if ( - totalStatus === 'loading' || - totalStatus === 'idle' || - errorStatus === 'loading' || - errorStatus === 'idle' - ) { - return ( -
- {headline} - -
- ) - } - if (totalStatus === 'success' && errorStatus === 'success') { - if (totalResponse?.options.case === 'vector' && errorResponse?.options.case === 'vector') { - let errors = 0 - if (errorResponse.options.value.samples.length > 0) { - errors = errorResponse.options.value.samples[0].value - } - - let total = 1 - if (totalResponse.options.value.samples.length > 0) { - total = totalResponse.options.value.samples[0].value - } - - const budget = 1 - objective.target - const unavailability = errors / total - const availableBudget = (budget - unavailability) / budget - - return ( -
0 ? 'good' : 'bad'}> - {headline} -

{(100 * availableBudget).toFixed(3)}%

-
- ) - } else { - return ( -
- {headline} -

No data

-
- ) - } - } - return ( -
- {headline} -

Error

-
- ) - } - const labelBadges = Object.entries({...objective.labels, ...groupingLabels}) .filter((l: [string, string]) => l[0] !== MetricName) .map((l: [string, string]) => ( @@ -418,11 +244,25 @@ const Detail = () => { -
- {renderObjective()} - {renderAvailability()} - {renderErrorBudget()} -
+ + + + +
diff --git a/ui/src/proto/objectives/v1alpha1/objectives_connectweb.d.ts b/ui/src/proto/objectives/v1alpha1/objectives_connectweb.d.ts index 47a0fb92c..b78391918 100644 --- a/ui/src/proto/objectives/v1alpha1/objectives_connectweb.d.ts +++ b/ui/src/proto/objectives/v1alpha1/objectives_connectweb.d.ts @@ -3,7 +3,7 @@ /* eslint-disable */ // @ts-nocheck -import { GetAlertsRequest, GetAlertsResponse, GetStatusRequest, GetStatusResponse, GraphDurationRequest, GraphDurationResponse, GraphErrorBudgetRequest, GraphErrorBudgetResponse, GraphErrorsRequest, GraphErrorsResponse, GraphRateRequest, GraphRateResponse, ListRequest, ListResponse } from "./objectives_pb.js"; +import { GetAlertsRequest, GetAlertsResponse, GetAvailabilityRequest, GetAvailabilityResponse, GetStatusRequest, GetStatusResponse, GraphDurationRequest, GraphDurationResponse, GraphErrorBudgetRequest, GraphErrorBudgetResponse, GraphErrorsRequest, GraphErrorsResponse, GraphRateRequest, GraphRateResponse, ListRequest, ListResponse } from "./objectives_pb.js"; import { MethodKind } from "@bufbuild/protobuf"; /** @@ -75,6 +75,15 @@ export declare const ObjectiveService: { readonly O: typeof GraphDurationResponse, readonly kind: MethodKind.Unary, }, + /** + * @generated from rpc objectives.v1alpha1.ObjectiveService.GetAvailability + */ + readonly getAvailability: { + readonly name: "GetAvailability", + readonly I: typeof GetAvailabilityRequest, + readonly O: typeof GetAvailabilityResponse, + readonly kind: MethodKind.Unary, + }, } }; diff --git a/ui/src/proto/objectives/v1alpha1/objectives_connectweb.js b/ui/src/proto/objectives/v1alpha1/objectives_connectweb.js index 3013a7a71..420f7dae4 100644 --- a/ui/src/proto/objectives/v1alpha1/objectives_connectweb.js +++ b/ui/src/proto/objectives/v1alpha1/objectives_connectweb.js @@ -3,7 +3,7 @@ /* eslint-disable */ // @ts-nocheck -import { GetAlertsRequest, GetAlertsResponse, GetStatusRequest, GetStatusResponse, GraphDurationRequest, GraphDurationResponse, GraphErrorBudgetRequest, GraphErrorBudgetResponse, GraphErrorsRequest, GraphErrorsResponse, GraphRateRequest, GraphRateResponse, ListRequest, ListResponse } from "./objectives_pb.js"; +import { GetAlertsRequest, GetAlertsResponse, GetAvailabilityRequest, GetAvailabilityResponse, GetStatusRequest, GetStatusResponse, GraphDurationRequest, GraphDurationResponse, GraphErrorBudgetRequest, GraphErrorBudgetResponse, GraphErrorsRequest, GraphErrorsResponse, GraphRateRequest, GraphRateResponse, ListRequest, ListResponse } from "./objectives_pb.js"; import { MethodKind } from "@bufbuild/protobuf"; /** @@ -75,6 +75,15 @@ export const ObjectiveService = { O: GraphDurationResponse, kind: MethodKind.Unary, }, + /** + * @generated from rpc objectives.v1alpha1.ObjectiveService.GetAvailability + */ + getAvailability: { + name: "GetAvailability", + I: GetAvailabilityRequest, + O: GetAvailabilityResponse, + kind: MethodKind.Unary, + }, } }; diff --git a/ui/src/proto/objectives/v1alpha1/objectives_pb.d.ts b/ui/src/proto/objectives/v1alpha1/objectives_pb.d.ts index 120020257..a0065059c 100644 --- a/ui/src/proto/objectives/v1alpha1/objectives_pb.d.ts +++ b/ui/src/proto/objectives/v1alpha1/objectives_pb.d.ts @@ -1033,3 +1033,61 @@ export declare class GraphDurationResponse extends Message | undefined, b: GraphDurationResponse | PlainMessage | undefined): boolean; } +/** + * @generated from message objectives.v1alpha1.GetAvailabilityRequest + */ +export declare class GetAvailabilityRequest extends Message { + /** + * @generated from field: google.protobuf.Duration window = 1; + */ + window?: Duration; + + /** + * @generated from field: objectives.v1alpha1.Indicator indicator = 2; + */ + indicator?: Indicator; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "objectives.v1alpha1.GetAvailabilityRequest"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): GetAvailabilityRequest; + + static fromJson(jsonValue: JsonValue, options?: Partial): GetAvailabilityRequest; + + static fromJsonString(jsonString: string, options?: Partial): GetAvailabilityRequest; + + static equals(a: GetAvailabilityRequest | PlainMessage | undefined, b: GetAvailabilityRequest | PlainMessage | undefined): boolean; +} + +/** + * @generated from message objectives.v1alpha1.GetAvailabilityResponse + */ +export declare class GetAvailabilityResponse extends Message { + /** + * @generated from field: int64 total = 1; + */ + total: bigint; + + /** + * @generated from field: int64 errors = 2; + */ + errors: bigint; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "objectives.v1alpha1.GetAvailabilityResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): GetAvailabilityResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): GetAvailabilityResponse; + + static fromJsonString(jsonString: string, options?: Partial): GetAvailabilityResponse; + + static equals(a: GetAvailabilityResponse | PlainMessage | undefined, b: GetAvailabilityResponse | PlainMessage | undefined): boolean; +} + diff --git a/ui/src/proto/objectives/v1alpha1/objectives_pb.js b/ui/src/proto/objectives/v1alpha1/objectives_pb.js index 9d1e92ba2..65f8d196b 100644 --- a/ui/src/proto/objectives/v1alpha1/objectives_pb.js +++ b/ui/src/proto/objectives/v1alpha1/objectives_pb.js @@ -378,3 +378,25 @@ export const GraphDurationResponse = proto3.makeMessageType( ], ); +/** + * @generated from message objectives.v1alpha1.GetAvailabilityRequest + */ +export const GetAvailabilityRequest = proto3.makeMessageType( + "objectives.v1alpha1.GetAvailabilityRequest", + () => [ + { no: 1, name: "window", kind: "message", T: Duration }, + { no: 2, name: "indicator", kind: "message", T: Indicator }, + ], +); + +/** + * @generated from message objectives.v1alpha1.GetAvailabilityResponse + */ +export const GetAvailabilityResponse = proto3.makeMessageType( + "objectives.v1alpha1.GetAvailabilityResponse", + () => [ + { no: 1, name: "total", kind: "scalar", T: 3 /* ScalarType.INT64 */ }, + { no: 2, name: "errors", kind: "scalar", T: 3 /* ScalarType.INT64 */ }, + ], +); +