diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.github/workflows/on-push.yaml b/.github/workflows/on-push.yaml new file mode 100644 index 0000000..966d99e --- /dev/null +++ b/.github/workflows/on-push.yaml @@ -0,0 +1,28 @@ +name: On Push + +on: + push: + branches: + - main + - master + pull_request: + branches: + - main + - master + +jobs: + build-and-push: + permissions: + contents: read + packages: write + uses: builderhub/actions-internal/.github/workflows/build-push-image.yaml@main + with: + build-type: dockerfile + image-name: build-api + dockerfile: Dockerfile + context: . + buildx-args: --secret id=netrc,env=SECRET_SLOT_0 + secrets: + registry-password: ${{ secrets.GITHUB_TOKEN }} + secret-slot-0: | + machine github.com login x-access-token password ${{ secrets.ORG_REPO_READ_ONLY }} diff --git a/.gitignore b/.gitignore index aaadf73..cd23119 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -# If you prefer the allow list template instead of the deny list, see community template: -# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore -# +# Binaries and build output +bin/ + # Binaries for programs and plugins *.exe *.exe~ @@ -24,9 +24,16 @@ profile.cov go.work go.work.sum -# env file +# Env files .env +.env.local +.env.*.local + +# Nix +result +result-* +.direnv # Editor/IDE -# .idea/ -# .vscode/ +.idea/ +.vscode/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e3856b0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# BuilderHub Build API - multi-stage, distroless, multi-arch +# Requires netrc for go mod download (private build-operator): --secret id=netrc,env=SECRET_SLOT_0 +# Build from build-api repo: docker buildx build --secret id=netrc,env=SECRET_SLOT_0 . + +FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder +WORKDIR /workspace + +RUN apk add --no-cache git ca-certificates tzdata + +COPY go.mod go.sum ./ +COPY . . + +# go mod download fetches build-operator from github.com/builderhub/build-operator (netrc for private repo auth) +ENV GOPRIVATE=github.com/builderhub/* +RUN --mount=type=secret,id=netrc,target=/root/.netrc go mod download + +ARG TARGETARCH +RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -ldflags="-w -s" -o build-api ./cmd/server + +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /workspace/build-api . +USER nonroot:nonroot +ENTRYPOINT ["/build-api"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6fc9223 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +GO ?= go +BUF ?= buf +BIN_DIR ?= bin + +.PHONY: generate +generate: + $(BUF) dep update + $(BUF) generate + +.PHONY: build +build: generate + $(GO) build -o $(BIN_DIR)/build-api ./cmd/server + +.PHONY: run +run: build + ./$(BIN_DIR)/build-api --grpc-addr=:9090 --http-addr=:8080 + +.PHONY: migrate-up +migrate-up: + migrate -path migrations -database "$${DATABASE_URL}" up + +.PHONY: migrate-down +migrate-down: + migrate -path migrations -database "$${DATABASE_URL}" down diff --git a/Tiltfile b/Tiltfile new file mode 100644 index 0000000..ab85e80 --- /dev/null +++ b/Tiltfile @@ -0,0 +1,9 @@ +# BuilderHub Build API - local dev +allow_k8s_contexts('kind-kind', 'docker-desktop', 'minikube') + +local_resource( + 'build-api', + cmd='make build', + deps=['cmd/', 'internal/', 'api/', 'go.mod', 'go.sum', 'Makefile'], + serve_cmd='bin/build-api --grpc-addr=:9090 --http-addr=:8080', +) diff --git a/api/gen/buildapi/v1/buildapi.pb.go b/api/gen/buildapi/v1/buildapi.pb.go new file mode 100644 index 0000000..7cf5526 --- /dev/null +++ b/api/gen/buildapi/v1/buildapi.pb.go @@ -0,0 +1,825 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: buildapi/v1/buildapi.proto + +package buildapiv1 + +import ( + _ "google.golang.org/genproto/googleapis/api/annotations" + 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) +) + +type ListBuildersRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListBuildersRequest) Reset() { + *x = ListBuildersRequest{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListBuildersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListBuildersRequest) ProtoMessage() {} + +func (x *ListBuildersRequest) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_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 ListBuildersRequest.ProtoReflect.Descriptor instead. +func (*ListBuildersRequest) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{0} +} + +func (x *ListBuildersRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +type ListBuildersResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Builders []*Builder `protobuf:"bytes,1,rep,name=builders,proto3" json:"builders,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListBuildersResponse) Reset() { + *x = ListBuildersResponse{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListBuildersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListBuildersResponse) ProtoMessage() {} + +func (x *ListBuildersResponse) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_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 ListBuildersResponse.ProtoReflect.Descriptor instead. +func (*ListBuildersResponse) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{1} +} + +func (x *ListBuildersResponse) GetBuilders() []*Builder { + if x != nil { + return x.Builders + } + return nil +} + +type GetBuilderRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetBuilderRequest) Reset() { + *x = GetBuilderRequest{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetBuilderRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBuilderRequest) ProtoMessage() {} + +func (x *GetBuilderRequest) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_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 GetBuilderRequest.ProtoReflect.Descriptor instead. +func (*GetBuilderRequest) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{2} +} + +func (x *GetBuilderRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *GetBuilderRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type GetBuilderResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Builder *Builder `protobuf:"bytes,1,opt,name=builder,proto3" json:"builder,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetBuilderResponse) Reset() { + *x = GetBuilderResponse{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetBuilderResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBuilderResponse) ProtoMessage() {} + +func (x *GetBuilderResponse) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_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 GetBuilderResponse.ProtoReflect.Descriptor instead. +func (*GetBuilderResponse) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{3} +} + +func (x *GetBuilderResponse) GetBuilder() *Builder { + if x != nil { + return x.Builder + } + return nil +} + +type CreateBuilderRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Spec *BuilderSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateBuilderRequest) Reset() { + *x = CreateBuilderRequest{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateBuilderRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateBuilderRequest) ProtoMessage() {} + +func (x *CreateBuilderRequest) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_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 CreateBuilderRequest.ProtoReflect.Descriptor instead. +func (*CreateBuilderRequest) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateBuilderRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *CreateBuilderRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CreateBuilderRequest) GetSpec() *BuilderSpec { + if x != nil { + return x.Spec + } + return nil +} + +type CreateBuilderResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Builder *Builder `protobuf:"bytes,1,opt,name=builder,proto3" json:"builder,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateBuilderResponse) Reset() { + *x = CreateBuilderResponse{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateBuilderResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateBuilderResponse) ProtoMessage() {} + +func (x *CreateBuilderResponse) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_proto_msgTypes[5] + 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 CreateBuilderResponse.ProtoReflect.Descriptor instead. +func (*CreateBuilderResponse) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateBuilderResponse) GetBuilder() *Builder { + if x != nil { + return x.Builder + } + return nil +} + +type WakeBuilderRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WakeBuilderRequest) Reset() { + *x = WakeBuilderRequest{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WakeBuilderRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WakeBuilderRequest) ProtoMessage() {} + +func (x *WakeBuilderRequest) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_proto_msgTypes[6] + 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 WakeBuilderRequest.ProtoReflect.Descriptor instead. +func (*WakeBuilderRequest) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{6} +} + +func (x *WakeBuilderRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *WakeBuilderRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type WakeBuilderResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Builder *Builder `protobuf:"bytes,1,opt,name=builder,proto3" json:"builder,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WakeBuilderResponse) Reset() { + *x = WakeBuilderResponse{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WakeBuilderResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WakeBuilderResponse) ProtoMessage() {} + +func (x *WakeBuilderResponse) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_proto_msgTypes[7] + 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 WakeBuilderResponse.ProtoReflect.Descriptor instead. +func (*WakeBuilderResponse) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{7} +} + +func (x *WakeBuilderResponse) GetBuilder() *Builder { + if x != nil { + return x.Builder + } + return nil +} + +type HealthCheckRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HealthCheckRequest) Reset() { + *x = HealthCheckRequest{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HealthCheckRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HealthCheckRequest) ProtoMessage() {} + +func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_proto_msgTypes[8] + 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 HealthCheckRequest.ProtoReflect.Descriptor instead. +func (*HealthCheckRequest) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{8} +} + +type HealthCheckResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HealthCheckResponse) Reset() { + *x = HealthCheckResponse{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HealthCheckResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HealthCheckResponse) ProtoMessage() {} + +func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_proto_msgTypes[9] + 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 HealthCheckResponse.ProtoReflect.Descriptor instead. +func (*HealthCheckResponse) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{9} +} + +func (x *HealthCheckResponse) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +type Builder struct { + state protoimpl.MessageState `protogen:"open.v1"` + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Spec *BuilderSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec,omitempty"` + Status *BuilderStatus `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Builder) Reset() { + *x = Builder{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Builder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Builder) ProtoMessage() {} + +func (x *Builder) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_proto_msgTypes[10] + 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 Builder.ProtoReflect.Descriptor instead. +func (*Builder) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{10} +} + +func (x *Builder) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *Builder) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Builder) GetSpec() *BuilderSpec { + if x != nil { + return x.Spec + } + return nil +} + +func (x *Builder) GetStatus() *BuilderStatus { + if x != nil { + return x.Status + } + return nil +} + +type BuilderSpec struct { + state protoimpl.MessageState `protogen:"open.v1"` + TemplateRef string `protobuf:"bytes,1,opt,name=template_ref,json=templateRef,proto3" json:"template_ref,omitempty"` + Mode string `protobuf:"bytes,2,opt,name=mode,proto3" json:"mode,omitempty"` // ephemeral, persistent, sleepy + Replicas int32 `protobuf:"varint,3,opt,name=replicas,proto3" json:"replicas,omitempty"` + IdleTimeoutSeconds int32 `protobuf:"varint,4,opt,name=idle_timeout_seconds,json=idleTimeoutSeconds,proto3" json:"idle_timeout_seconds,omitempty"` + Labels map[string]string `protobuf:"bytes,5,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BuilderSpec) Reset() { + *x = BuilderSpec{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BuilderSpec) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BuilderSpec) ProtoMessage() {} + +func (x *BuilderSpec) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_proto_msgTypes[11] + 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 BuilderSpec.ProtoReflect.Descriptor instead. +func (*BuilderSpec) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{11} +} + +func (x *BuilderSpec) GetTemplateRef() string { + if x != nil { + return x.TemplateRef + } + return "" +} + +func (x *BuilderSpec) GetMode() string { + if x != nil { + return x.Mode + } + return "" +} + +func (x *BuilderSpec) GetReplicas() int32 { + if x != nil { + return x.Replicas + } + return 0 +} + +func (x *BuilderSpec) GetIdleTimeoutSeconds() int32 { + if x != nil { + return x.IdleTimeoutSeconds + } + return 0 +} + +func (x *BuilderSpec) GetLabels() map[string]string { + if x != nil { + return x.Labels + } + return nil +} + +type BuilderStatus struct { + state protoimpl.MessageState `protogen:"open.v1"` + Endpoint string `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"` + NodePort int32 `protobuf:"varint,2,opt,name=node_port,json=nodePort,proto3" json:"node_port,omitempty"` + Phase string `protobuf:"bytes,3,opt,name=phase,proto3" json:"phase,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BuilderStatus) Reset() { + *x = BuilderStatus{} + mi := &file_buildapi_v1_buildapi_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BuilderStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BuilderStatus) ProtoMessage() {} + +func (x *BuilderStatus) ProtoReflect() protoreflect.Message { + mi := &file_buildapi_v1_buildapi_proto_msgTypes[12] + 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 BuilderStatus.ProtoReflect.Descriptor instead. +func (*BuilderStatus) Descriptor() ([]byte, []int) { + return file_buildapi_v1_buildapi_proto_rawDescGZIP(), []int{12} +} + +func (x *BuilderStatus) GetEndpoint() string { + if x != nil { + return x.Endpoint + } + return "" +} + +func (x *BuilderStatus) GetNodePort() int32 { + if x != nil { + return x.NodePort + } + return 0 +} + +func (x *BuilderStatus) GetPhase() string { + if x != nil { + return x.Phase + } + return "" +} + +var File_buildapi_v1_buildapi_proto protoreflect.FileDescriptor + +const file_buildapi_v1_buildapi_proto_rawDesc = "" + + "\n" + + "\x1abuildapi/v1/buildapi.proto\x12\vbuildapi.v1\x1a\x1cgoogle/api/annotations.proto\"3\n" + + "\x13ListBuildersRequest\x12\x1c\n" + + "\tnamespace\x18\x01 \x01(\tR\tnamespace\"H\n" + + "\x14ListBuildersResponse\x120\n" + + "\bbuilders\x18\x01 \x03(\v2\x14.buildapi.v1.BuilderR\bbuilders\"E\n" + + "\x11GetBuilderRequest\x12\x1c\n" + + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\"D\n" + + "\x12GetBuilderResponse\x12.\n" + + "\abuilder\x18\x01 \x01(\v2\x14.buildapi.v1.BuilderR\abuilder\"v\n" + + "\x14CreateBuilderRequest\x12\x1c\n" + + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12,\n" + + "\x04spec\x18\x03 \x01(\v2\x18.buildapi.v1.BuilderSpecR\x04spec\"G\n" + + "\x15CreateBuilderResponse\x12.\n" + + "\abuilder\x18\x01 \x01(\v2\x14.buildapi.v1.BuilderR\abuilder\"F\n" + + "\x12WakeBuilderRequest\x12\x1c\n" + + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\"E\n" + + "\x13WakeBuilderResponse\x12.\n" + + "\abuilder\x18\x01 \x01(\v2\x14.buildapi.v1.BuilderR\abuilder\"\x14\n" + + "\x12HealthCheckRequest\"-\n" + + "\x13HealthCheckResponse\x12\x16\n" + + "\x06status\x18\x01 \x01(\tR\x06status\"\x9d\x01\n" + + "\aBuilder\x12\x1c\n" + + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12,\n" + + "\x04spec\x18\x03 \x01(\v2\x18.buildapi.v1.BuilderSpecR\x04spec\x122\n" + + "\x06status\x18\x04 \x01(\v2\x1a.buildapi.v1.BuilderStatusR\x06status\"\x8b\x02\n" + + "\vBuilderSpec\x12!\n" + + "\ftemplate_ref\x18\x01 \x01(\tR\vtemplateRef\x12\x12\n" + + "\x04mode\x18\x02 \x01(\tR\x04mode\x12\x1a\n" + + "\breplicas\x18\x03 \x01(\x05R\breplicas\x120\n" + + "\x14idle_timeout_seconds\x18\x04 \x01(\x05R\x12idleTimeoutSeconds\x12<\n" + + "\x06labels\x18\x05 \x03(\v2$.buildapi.v1.BuilderSpec.LabelsEntryR\x06labels\x1a9\n" + + "\vLabelsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"^\n" + + "\rBuilderStatus\x12\x1a\n" + + "\bendpoint\x18\x01 \x01(\tR\bendpoint\x12\x1b\n" + + "\tnode_port\x18\x02 \x01(\x05R\bnodePort\x12\x14\n" + + "\x05phase\x18\x03 \x01(\tR\x05phase2\x8f\x05\n" + + "\bBuildAPI\x12\x80\x01\n" + + "\fListBuilders\x12 .buildapi.v1.ListBuildersRequest\x1a!.buildapi.v1.ListBuildersResponse\"+\x82\xd3\xe4\x93\x02%\x12#/v1/namespaces/{namespace}/builders\x12\x81\x01\n" + + "\n" + + "GetBuilder\x12\x1e.buildapi.v1.GetBuilderRequest\x1a\x1f.buildapi.v1.GetBuilderResponse\"2\x82\xd3\xe4\x93\x02,\x12*/v1/namespaces/{namespace}/builders/{name}\x12\x86\x01\n" + + "\rCreateBuilder\x12!.buildapi.v1.CreateBuilderRequest\x1a\".buildapi.v1.CreateBuilderResponse\".\x82\xd3\xe4\x93\x02(:\x01*\"#/v1/namespaces/{namespace}/builders\x12\x8c\x01\n" + + "\vWakeBuilder\x12\x1f.buildapi.v1.WakeBuilderRequest\x1a .buildapi.v1.WakeBuilderResponse\":\x82\xd3\xe4\x93\x024:\x01*\"//v1/namespaces/{namespace}/builders/{name}/wake\x12d\n" + + "\vHealthCheck\x12\x1f.buildapi.v1.HealthCheckRequest\x1a .buildapi.v1.HealthCheckResponse\"\x12\x82\xd3\xe4\x93\x02\f\x12\n" + + "/v1/healthB@Z>github.com/builderhub/build-api/api/gen/buildapi/v1;buildapiv1b\x06proto3" + +var ( + file_buildapi_v1_buildapi_proto_rawDescOnce sync.Once + file_buildapi_v1_buildapi_proto_rawDescData []byte +) + +func file_buildapi_v1_buildapi_proto_rawDescGZIP() []byte { + file_buildapi_v1_buildapi_proto_rawDescOnce.Do(func() { + file_buildapi_v1_buildapi_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_buildapi_v1_buildapi_proto_rawDesc), len(file_buildapi_v1_buildapi_proto_rawDesc))) + }) + return file_buildapi_v1_buildapi_proto_rawDescData +} + +var file_buildapi_v1_buildapi_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_buildapi_v1_buildapi_proto_goTypes = []any{ + (*ListBuildersRequest)(nil), // 0: buildapi.v1.ListBuildersRequest + (*ListBuildersResponse)(nil), // 1: buildapi.v1.ListBuildersResponse + (*GetBuilderRequest)(nil), // 2: buildapi.v1.GetBuilderRequest + (*GetBuilderResponse)(nil), // 3: buildapi.v1.GetBuilderResponse + (*CreateBuilderRequest)(nil), // 4: buildapi.v1.CreateBuilderRequest + (*CreateBuilderResponse)(nil), // 5: buildapi.v1.CreateBuilderResponse + (*WakeBuilderRequest)(nil), // 6: buildapi.v1.WakeBuilderRequest + (*WakeBuilderResponse)(nil), // 7: buildapi.v1.WakeBuilderResponse + (*HealthCheckRequest)(nil), // 8: buildapi.v1.HealthCheckRequest + (*HealthCheckResponse)(nil), // 9: buildapi.v1.HealthCheckResponse + (*Builder)(nil), // 10: buildapi.v1.Builder + (*BuilderSpec)(nil), // 11: buildapi.v1.BuilderSpec + (*BuilderStatus)(nil), // 12: buildapi.v1.BuilderStatus + nil, // 13: buildapi.v1.BuilderSpec.LabelsEntry +} +var file_buildapi_v1_buildapi_proto_depIdxs = []int32{ + 10, // 0: buildapi.v1.ListBuildersResponse.builders:type_name -> buildapi.v1.Builder + 10, // 1: buildapi.v1.GetBuilderResponse.builder:type_name -> buildapi.v1.Builder + 11, // 2: buildapi.v1.CreateBuilderRequest.spec:type_name -> buildapi.v1.BuilderSpec + 10, // 3: buildapi.v1.CreateBuilderResponse.builder:type_name -> buildapi.v1.Builder + 10, // 4: buildapi.v1.WakeBuilderResponse.builder:type_name -> buildapi.v1.Builder + 11, // 5: buildapi.v1.Builder.spec:type_name -> buildapi.v1.BuilderSpec + 12, // 6: buildapi.v1.Builder.status:type_name -> buildapi.v1.BuilderStatus + 13, // 7: buildapi.v1.BuilderSpec.labels:type_name -> buildapi.v1.BuilderSpec.LabelsEntry + 0, // 8: buildapi.v1.BuildAPI.ListBuilders:input_type -> buildapi.v1.ListBuildersRequest + 2, // 9: buildapi.v1.BuildAPI.GetBuilder:input_type -> buildapi.v1.GetBuilderRequest + 4, // 10: buildapi.v1.BuildAPI.CreateBuilder:input_type -> buildapi.v1.CreateBuilderRequest + 6, // 11: buildapi.v1.BuildAPI.WakeBuilder:input_type -> buildapi.v1.WakeBuilderRequest + 8, // 12: buildapi.v1.BuildAPI.HealthCheck:input_type -> buildapi.v1.HealthCheckRequest + 1, // 13: buildapi.v1.BuildAPI.ListBuilders:output_type -> buildapi.v1.ListBuildersResponse + 3, // 14: buildapi.v1.BuildAPI.GetBuilder:output_type -> buildapi.v1.GetBuilderResponse + 5, // 15: buildapi.v1.BuildAPI.CreateBuilder:output_type -> buildapi.v1.CreateBuilderResponse + 7, // 16: buildapi.v1.BuildAPI.WakeBuilder:output_type -> buildapi.v1.WakeBuilderResponse + 9, // 17: buildapi.v1.BuildAPI.HealthCheck:output_type -> buildapi.v1.HealthCheckResponse + 13, // [13:18] is the sub-list for method output_type + 8, // [8:13] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name +} + +func init() { file_buildapi_v1_buildapi_proto_init() } +func file_buildapi_v1_buildapi_proto_init() { + if File_buildapi_v1_buildapi_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_buildapi_v1_buildapi_proto_rawDesc), len(file_buildapi_v1_buildapi_proto_rawDesc)), + NumEnums: 0, + NumMessages: 14, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_buildapi_v1_buildapi_proto_goTypes, + DependencyIndexes: file_buildapi_v1_buildapi_proto_depIdxs, + MessageInfos: file_buildapi_v1_buildapi_proto_msgTypes, + }.Build() + File_buildapi_v1_buildapi_proto = out.File + file_buildapi_v1_buildapi_proto_goTypes = nil + file_buildapi_v1_buildapi_proto_depIdxs = nil +} diff --git a/api/gen/buildapi/v1/buildapi.pb.gw.go b/api/gen/buildapi/v1/buildapi.pb.gw.go new file mode 100644 index 0000000..161bfe3 --- /dev/null +++ b/api/gen/buildapi/v1/buildapi.pb.gw.go @@ -0,0 +1,507 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: buildapi/v1/buildapi.proto + +/* +Package buildapiv1 is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package buildapiv1 + +import ( + "context" + "errors" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var ( + _ codes.Code + _ io.Reader + _ status.Status + _ = errors.New + _ = runtime.String + _ = utilities.NewDoubleArray + _ = metadata.Join +) + +func request_BuildAPI_ListBuilders_0(ctx context.Context, marshaler runtime.Marshaler, client BuildAPIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq ListBuildersRequest + metadata runtime.ServerMetadata + err error + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + val, ok := pathParams["namespace"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") + } + protoReq.Namespace, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) + } + msg, err := client.ListBuilders(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_BuildAPI_ListBuilders_0(ctx context.Context, marshaler runtime.Marshaler, server BuildAPIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq ListBuildersRequest + metadata runtime.ServerMetadata + err error + ) + val, ok := pathParams["namespace"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") + } + protoReq.Namespace, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) + } + msg, err := server.ListBuilders(ctx, &protoReq) + return msg, metadata, err +} + +func request_BuildAPI_GetBuilder_0(ctx context.Context, marshaler runtime.Marshaler, client BuildAPIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetBuilderRequest + metadata runtime.ServerMetadata + err error + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + val, ok := pathParams["namespace"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") + } + protoReq.Namespace, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) + } + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := client.GetBuilder(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_BuildAPI_GetBuilder_0(ctx context.Context, marshaler runtime.Marshaler, server BuildAPIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetBuilderRequest + metadata runtime.ServerMetadata + err error + ) + val, ok := pathParams["namespace"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") + } + protoReq.Namespace, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) + } + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := server.GetBuilder(ctx, &protoReq) + return msg, metadata, err +} + +func request_BuildAPI_CreateBuilder_0(ctx context.Context, marshaler runtime.Marshaler, client BuildAPIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq CreateBuilderRequest + metadata runtime.ServerMetadata + err error + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + val, ok := pathParams["namespace"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") + } + protoReq.Namespace, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) + } + msg, err := client.CreateBuilder(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_BuildAPI_CreateBuilder_0(ctx context.Context, marshaler runtime.Marshaler, server BuildAPIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq CreateBuilderRequest + metadata runtime.ServerMetadata + err error + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + val, ok := pathParams["namespace"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") + } + protoReq.Namespace, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) + } + msg, err := server.CreateBuilder(ctx, &protoReq) + return msg, metadata, err +} + +func request_BuildAPI_WakeBuilder_0(ctx context.Context, marshaler runtime.Marshaler, client BuildAPIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq WakeBuilderRequest + metadata runtime.ServerMetadata + err error + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + val, ok := pathParams["namespace"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") + } + protoReq.Namespace, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) + } + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := client.WakeBuilder(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_BuildAPI_WakeBuilder_0(ctx context.Context, marshaler runtime.Marshaler, server BuildAPIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq WakeBuilderRequest + metadata runtime.ServerMetadata + err error + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + val, ok := pathParams["namespace"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") + } + protoReq.Namespace, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) + } + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := server.WakeBuilder(ctx, &protoReq) + return msg, metadata, err +} + +func request_BuildAPI_HealthCheck_0(ctx context.Context, marshaler runtime.Marshaler, client BuildAPIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq HealthCheckRequest + metadata runtime.ServerMetadata + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + msg, err := client.HealthCheck(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_BuildAPI_HealthCheck_0(ctx context.Context, marshaler runtime.Marshaler, server BuildAPIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq HealthCheckRequest + metadata runtime.ServerMetadata + ) + msg, err := server.HealthCheck(ctx, &protoReq) + return msg, metadata, err +} + +// RegisterBuildAPIHandlerServer registers the http handlers for service BuildAPI to "mux". +// UnaryRPC :call BuildAPIServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterBuildAPIHandlerFromEndpoint instead. +// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call. +func RegisterBuildAPIHandlerServer(ctx context.Context, mux *runtime.ServeMux, server BuildAPIServer) error { + mux.Handle(http.MethodGet, pattern_BuildAPI_ListBuilders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/buildapi.v1.BuildAPI/ListBuilders", runtime.WithHTTPPathPattern("/v1/namespaces/{namespace}/builders")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_BuildAPI_ListBuilders_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_BuildAPI_ListBuilders_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_BuildAPI_GetBuilder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/buildapi.v1.BuildAPI/GetBuilder", runtime.WithHTTPPathPattern("/v1/namespaces/{namespace}/builders/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_BuildAPI_GetBuilder_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_BuildAPI_GetBuilder_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPost, pattern_BuildAPI_CreateBuilder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/buildapi.v1.BuildAPI/CreateBuilder", runtime.WithHTTPPathPattern("/v1/namespaces/{namespace}/builders")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_BuildAPI_CreateBuilder_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_BuildAPI_CreateBuilder_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPost, pattern_BuildAPI_WakeBuilder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/buildapi.v1.BuildAPI/WakeBuilder", runtime.WithHTTPPathPattern("/v1/namespaces/{namespace}/builders/{name}/wake")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_BuildAPI_WakeBuilder_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_BuildAPI_WakeBuilder_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_BuildAPI_HealthCheck_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/buildapi.v1.BuildAPI/HealthCheck", runtime.WithHTTPPathPattern("/v1/health")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_BuildAPI_HealthCheck_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_BuildAPI_HealthCheck_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + return nil +} + +// RegisterBuildAPIHandlerFromEndpoint is same as RegisterBuildAPIHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterBuildAPIHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.NewClient(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + return RegisterBuildAPIHandler(ctx, mux, conn) +} + +// RegisterBuildAPIHandler registers the http handlers for service BuildAPI to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterBuildAPIHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterBuildAPIHandlerClient(ctx, mux, NewBuildAPIClient(conn)) +} + +// RegisterBuildAPIHandlerClient registers the http handlers for service BuildAPI +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "BuildAPIClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "BuildAPIClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "BuildAPIClient" to call the correct interceptors. This client ignores the HTTP middlewares. +func RegisterBuildAPIHandlerClient(ctx context.Context, mux *runtime.ServeMux, client BuildAPIClient) error { + mux.Handle(http.MethodGet, pattern_BuildAPI_ListBuilders_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/buildapi.v1.BuildAPI/ListBuilders", runtime.WithHTTPPathPattern("/v1/namespaces/{namespace}/builders")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_BuildAPI_ListBuilders_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_BuildAPI_ListBuilders_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_BuildAPI_GetBuilder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/buildapi.v1.BuildAPI/GetBuilder", runtime.WithHTTPPathPattern("/v1/namespaces/{namespace}/builders/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_BuildAPI_GetBuilder_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_BuildAPI_GetBuilder_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPost, pattern_BuildAPI_CreateBuilder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/buildapi.v1.BuildAPI/CreateBuilder", runtime.WithHTTPPathPattern("/v1/namespaces/{namespace}/builders")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_BuildAPI_CreateBuilder_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_BuildAPI_CreateBuilder_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPost, pattern_BuildAPI_WakeBuilder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/buildapi.v1.BuildAPI/WakeBuilder", runtime.WithHTTPPathPattern("/v1/namespaces/{namespace}/builders/{name}/wake")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_BuildAPI_WakeBuilder_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_BuildAPI_WakeBuilder_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_BuildAPI_HealthCheck_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/buildapi.v1.BuildAPI/HealthCheck", runtime.WithHTTPPathPattern("/v1/health")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_BuildAPI_HealthCheck_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_BuildAPI_HealthCheck_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + return nil +} + +var ( + pattern_BuildAPI_ListBuilders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1", "namespaces", "namespace", "builders"}, "")) + pattern_BuildAPI_GetBuilder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "namespaces", "namespace", "builders", "name"}, "")) + pattern_BuildAPI_CreateBuilder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1", "namespaces", "namespace", "builders"}, "")) + pattern_BuildAPI_WakeBuilder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"v1", "namespaces", "namespace", "builders", "name", "wake"}, "")) + pattern_BuildAPI_HealthCheck_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "health"}, "")) +) + +var ( + forward_BuildAPI_ListBuilders_0 = runtime.ForwardResponseMessage + forward_BuildAPI_GetBuilder_0 = runtime.ForwardResponseMessage + forward_BuildAPI_CreateBuilder_0 = runtime.ForwardResponseMessage + forward_BuildAPI_WakeBuilder_0 = runtime.ForwardResponseMessage + forward_BuildAPI_HealthCheck_0 = runtime.ForwardResponseMessage +) diff --git a/api/gen/buildapi/v1/buildapi.swagger.json b/api/gen/buildapi/v1/buildapi.swagger.json new file mode 100644 index 0000000..6b24277 --- /dev/null +++ b/api/gen/buildapi/v1/buildapi.swagger.json @@ -0,0 +1,339 @@ +{ + "swagger": "2.0", + "info": { + "title": "buildapi/v1/buildapi.proto", + "version": "version not set" + }, + "tags": [ + { + "name": "BuildAPI" + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/v1/health": { + "get": { + "summary": "HealthCheck for liveness/readiness.", + "operationId": "BuildAPI_HealthCheck", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/HealthCheckResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Status" + } + } + }, + "tags": [ + "BuildAPI" + ] + } + }, + "/v1/namespaces/{namespace}/builders": { + "get": { + "summary": "ListBuilders returns all BuildkitBuilder CRs in a namespace.", + "operationId": "BuildAPI_ListBuilders", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/ListBuildersResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Status" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "BuildAPI" + ] + }, + "post": { + "summary": "CreateBuilder creates a new BuildkitBuilder CR.", + "operationId": "BuildAPI_CreateBuilder", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/CreateBuilderResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Status" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/CreateBuilderBody" + } + } + ], + "tags": [ + "BuildAPI" + ] + } + }, + "/v1/namespaces/{namespace}/builders/{name}": { + "get": { + "summary": "GetBuilder returns a single BuildkitBuilder by name.", + "operationId": "BuildAPI_GetBuilder", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/GetBuilderResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Status" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "BuildAPI" + ] + } + }, + "/v1/namespaces/{namespace}/builders/{name}/wake": { + "post": { + "summary": "WakeBuilder patches builder-hub.dev/last-used for sleepy builders (wakes them up).", + "operationId": "BuildAPI_WakeBuilder", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/WakeBuilderResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Status" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/WakeBuilderBody" + } + } + ], + "tags": [ + "BuildAPI" + ] + } + } + }, + "definitions": { + "Any": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "Builder": { + "type": "object", + "properties": { + "namespace": { + "type": "string" + }, + "name": { + "type": "string" + }, + "spec": { + "$ref": "#/definitions/BuilderSpec" + }, + "status": { + "$ref": "#/definitions/BuilderStatus" + } + } + }, + "BuilderSpec": { + "type": "object", + "properties": { + "templateRef": { + "type": "string" + }, + "mode": { + "type": "string", + "title": "ephemeral, persistent, sleepy" + }, + "replicas": { + "type": "integer", + "format": "int32" + }, + "idleTimeoutSeconds": { + "type": "integer", + "format": "int32" + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "BuilderStatus": { + "type": "object", + "properties": { + "endpoint": { + "type": "string" + }, + "nodePort": { + "type": "integer", + "format": "int32" + }, + "phase": { + "type": "string" + } + } + }, + "CreateBuilderBody": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "spec": { + "$ref": "#/definitions/BuilderSpec" + } + } + }, + "CreateBuilderResponse": { + "type": "object", + "properties": { + "builder": { + "$ref": "#/definitions/Builder" + } + } + }, + "GetBuilderResponse": { + "type": "object", + "properties": { + "builder": { + "$ref": "#/definitions/Builder" + } + } + }, + "HealthCheckResponse": { + "type": "object", + "properties": { + "status": { + "type": "string" + } + } + }, + "ListBuildersResponse": { + "type": "object", + "properties": { + "builders": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/Builder" + } + } + } + }, + "Status": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/Any" + } + } + } + }, + "WakeBuilderBody": { + "type": "object" + }, + "WakeBuilderResponse": { + "type": "object", + "properties": { + "builder": { + "$ref": "#/definitions/Builder" + } + } + } + } +} diff --git a/api/gen/buildapi/v1/buildapi_grpc.pb.go b/api/gen/buildapi/v1/buildapi_grpc.pb.go new file mode 100644 index 0000000..2abef14 --- /dev/null +++ b/api/gen/buildapi/v1/buildapi_grpc.pb.go @@ -0,0 +1,287 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc (unknown) +// source: buildapi/v1/buildapi.proto + +package buildapiv1 + +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 ( + BuildAPI_ListBuilders_FullMethodName = "/buildapi.v1.BuildAPI/ListBuilders" + BuildAPI_GetBuilder_FullMethodName = "/buildapi.v1.BuildAPI/GetBuilder" + BuildAPI_CreateBuilder_FullMethodName = "/buildapi.v1.BuildAPI/CreateBuilder" + BuildAPI_WakeBuilder_FullMethodName = "/buildapi.v1.BuildAPI/WakeBuilder" + BuildAPI_HealthCheck_FullMethodName = "/buildapi.v1.BuildAPI/HealthCheck" +) + +// BuildAPIClient is the client API for BuildAPI 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. +// +// BuildAPI service - manages builds and builders. +type BuildAPIClient interface { + // ListBuilders returns all BuildkitBuilder CRs in a namespace. + ListBuilders(ctx context.Context, in *ListBuildersRequest, opts ...grpc.CallOption) (*ListBuildersResponse, error) + // GetBuilder returns a single BuildkitBuilder by name. + GetBuilder(ctx context.Context, in *GetBuilderRequest, opts ...grpc.CallOption) (*GetBuilderResponse, error) + // CreateBuilder creates a new BuildkitBuilder CR. + CreateBuilder(ctx context.Context, in *CreateBuilderRequest, opts ...grpc.CallOption) (*CreateBuilderResponse, error) + // WakeBuilder patches builder-hub.dev/last-used for sleepy builders (wakes them up). + WakeBuilder(ctx context.Context, in *WakeBuilderRequest, opts ...grpc.CallOption) (*WakeBuilderResponse, error) + // HealthCheck for liveness/readiness. + HealthCheck(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) +} + +type buildAPIClient struct { + cc grpc.ClientConnInterface +} + +func NewBuildAPIClient(cc grpc.ClientConnInterface) BuildAPIClient { + return &buildAPIClient{cc} +} + +func (c *buildAPIClient) ListBuilders(ctx context.Context, in *ListBuildersRequest, opts ...grpc.CallOption) (*ListBuildersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListBuildersResponse) + err := c.cc.Invoke(ctx, BuildAPI_ListBuilders_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *buildAPIClient) GetBuilder(ctx context.Context, in *GetBuilderRequest, opts ...grpc.CallOption) (*GetBuilderResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetBuilderResponse) + err := c.cc.Invoke(ctx, BuildAPI_GetBuilder_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *buildAPIClient) CreateBuilder(ctx context.Context, in *CreateBuilderRequest, opts ...grpc.CallOption) (*CreateBuilderResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateBuilderResponse) + err := c.cc.Invoke(ctx, BuildAPI_CreateBuilder_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *buildAPIClient) WakeBuilder(ctx context.Context, in *WakeBuilderRequest, opts ...grpc.CallOption) (*WakeBuilderResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WakeBuilderResponse) + err := c.cc.Invoke(ctx, BuildAPI_WakeBuilder_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *buildAPIClient) HealthCheck(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(HealthCheckResponse) + err := c.cc.Invoke(ctx, BuildAPI_HealthCheck_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// BuildAPIServer is the server API for BuildAPI service. +// All implementations must embed UnimplementedBuildAPIServer +// for forward compatibility. +// +// BuildAPI service - manages builds and builders. +type BuildAPIServer interface { + // ListBuilders returns all BuildkitBuilder CRs in a namespace. + ListBuilders(context.Context, *ListBuildersRequest) (*ListBuildersResponse, error) + // GetBuilder returns a single BuildkitBuilder by name. + GetBuilder(context.Context, *GetBuilderRequest) (*GetBuilderResponse, error) + // CreateBuilder creates a new BuildkitBuilder CR. + CreateBuilder(context.Context, *CreateBuilderRequest) (*CreateBuilderResponse, error) + // WakeBuilder patches builder-hub.dev/last-used for sleepy builders (wakes them up). + WakeBuilder(context.Context, *WakeBuilderRequest) (*WakeBuilderResponse, error) + // HealthCheck for liveness/readiness. + HealthCheck(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) + mustEmbedUnimplementedBuildAPIServer() +} + +// UnimplementedBuildAPIServer 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 UnimplementedBuildAPIServer struct{} + +func (UnimplementedBuildAPIServer) ListBuilders(context.Context, *ListBuildersRequest) (*ListBuildersResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListBuilders not implemented") +} +func (UnimplementedBuildAPIServer) GetBuilder(context.Context, *GetBuilderRequest) (*GetBuilderResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetBuilder not implemented") +} +func (UnimplementedBuildAPIServer) CreateBuilder(context.Context, *CreateBuilderRequest) (*CreateBuilderResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateBuilder not implemented") +} +func (UnimplementedBuildAPIServer) WakeBuilder(context.Context, *WakeBuilderRequest) (*WakeBuilderResponse, error) { + return nil, status.Error(codes.Unimplemented, "method WakeBuilder not implemented") +} +func (UnimplementedBuildAPIServer) HealthCheck(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) { + return nil, status.Error(codes.Unimplemented, "method HealthCheck not implemented") +} +func (UnimplementedBuildAPIServer) mustEmbedUnimplementedBuildAPIServer() {} +func (UnimplementedBuildAPIServer) testEmbeddedByValue() {} + +// UnsafeBuildAPIServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to BuildAPIServer will +// result in compilation errors. +type UnsafeBuildAPIServer interface { + mustEmbedUnimplementedBuildAPIServer() +} + +func RegisterBuildAPIServer(s grpc.ServiceRegistrar, srv BuildAPIServer) { + // If the following call panics, it indicates UnimplementedBuildAPIServer 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(&BuildAPI_ServiceDesc, srv) +} + +func _BuildAPI_ListBuilders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListBuildersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BuildAPIServer).ListBuilders(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: BuildAPI_ListBuilders_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BuildAPIServer).ListBuilders(ctx, req.(*ListBuildersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BuildAPI_GetBuilder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetBuilderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BuildAPIServer).GetBuilder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: BuildAPI_GetBuilder_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BuildAPIServer).GetBuilder(ctx, req.(*GetBuilderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BuildAPI_CreateBuilder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateBuilderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BuildAPIServer).CreateBuilder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: BuildAPI_CreateBuilder_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BuildAPIServer).CreateBuilder(ctx, req.(*CreateBuilderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BuildAPI_WakeBuilder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WakeBuilderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BuildAPIServer).WakeBuilder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: BuildAPI_WakeBuilder_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BuildAPIServer).WakeBuilder(ctx, req.(*WakeBuilderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BuildAPI_HealthCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HealthCheckRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BuildAPIServer).HealthCheck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: BuildAPI_HealthCheck_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BuildAPIServer).HealthCheck(ctx, req.(*HealthCheckRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// BuildAPI_ServiceDesc is the grpc.ServiceDesc for BuildAPI service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var BuildAPI_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "buildapi.v1.BuildAPI", + HandlerType: (*BuildAPIServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListBuilders", + Handler: _BuildAPI_ListBuilders_Handler, + }, + { + MethodName: "GetBuilder", + Handler: _BuildAPI_GetBuilder_Handler, + }, + { + MethodName: "CreateBuilder", + Handler: _BuildAPI_CreateBuilder_Handler, + }, + { + MethodName: "WakeBuilder", + Handler: _BuildAPI_WakeBuilder_Handler, + }, + { + MethodName: "HealthCheck", + Handler: _BuildAPI_HealthCheck_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "buildapi/v1/buildapi.proto", +} diff --git a/api/gen/buildapi/v1/openapi_embed.go b/api/gen/buildapi/v1/openapi_embed.go new file mode 100644 index 0000000..c5c1a59 --- /dev/null +++ b/api/gen/buildapi/v1/openapi_embed.go @@ -0,0 +1,7 @@ +package buildapiv1 + +import _ "embed" + +// OpenAPISpec is the Swagger/OpenAPI 2.0 spec for the BuildAPI (generated by buf). +//go:embed buildapi.swagger.json +var OpenAPISpec []byte diff --git a/api/proto/buildapi/v1/buildapi.proto b/api/proto/buildapi/v1/buildapi.proto new file mode 100644 index 0000000..e05e7f8 --- /dev/null +++ b/api/proto/buildapi/v1/buildapi.proto @@ -0,0 +1,104 @@ +syntax = "proto3"; + +package buildapi.v1; + +import "google/api/annotations.proto"; + +option go_package = "github.com/builderhub/build-api/api/gen/buildapi/v1;buildapiv1"; + +// BuildAPI service - manages builds and builders. +service BuildAPI { + // ListBuilders returns all BuildkitBuilder CRs in a namespace. + rpc ListBuilders(ListBuildersRequest) returns (ListBuildersResponse) { + option (google.api.http) = {get: "/v1/namespaces/{namespace}/builders"}; + } + + // GetBuilder returns a single BuildkitBuilder by name. + rpc GetBuilder(GetBuilderRequest) returns (GetBuilderResponse) { + option (google.api.http) = {get: "/v1/namespaces/{namespace}/builders/{name}"}; + } + + // CreateBuilder creates a new BuildkitBuilder CR. + rpc CreateBuilder(CreateBuilderRequest) returns (CreateBuilderResponse) { + option (google.api.http) = { + post: "/v1/namespaces/{namespace}/builders" + body: "*" + }; + } + + // WakeBuilder patches builder-hub.dev/last-used for sleepy builders (wakes them up). + rpc WakeBuilder(WakeBuilderRequest) returns (WakeBuilderResponse) { + option (google.api.http) = { + post: "/v1/namespaces/{namespace}/builders/{name}/wake" + body: "*" + }; + } + + // HealthCheck for liveness/readiness. + rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse) { + option (google.api.http) = {get: "/v1/health"}; + } +} + +message ListBuildersRequest { + string namespace = 1; +} + +message ListBuildersResponse { + repeated Builder builders = 1; +} + +message GetBuilderRequest { + string namespace = 1; + string name = 2; +} + +message GetBuilderResponse { + Builder builder = 1; +} + +message CreateBuilderRequest { + string namespace = 1; + string name = 2; + BuilderSpec spec = 3; +} + +message CreateBuilderResponse { + Builder builder = 1; +} + +message WakeBuilderRequest { + string namespace = 1; + string name = 2; +} + +message WakeBuilderResponse { + Builder builder = 1; +} + +message HealthCheckRequest {} + +message HealthCheckResponse { + string status = 1; +} + +message Builder { + string namespace = 1; + string name = 2; + BuilderSpec spec = 3; + BuilderStatus status = 4; +} + +message BuilderSpec { + string template_ref = 1; + string mode = 2; // ephemeral, persistent, sleepy + int32 replicas = 3; + int32 idle_timeout_seconds = 4; + map labels = 5; +} + +message BuilderStatus { + string endpoint = 1; + int32 node_port = 2; + string phase = 3; +} diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..363c745 --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,14 @@ +version: v2 +plugins: + - remote: buf.build/protocolbuffers/go + out: api/gen + opt: paths=source_relative + - remote: buf.build/grpc/go + out: api/gen + opt: paths=source_relative + - remote: buf.build/grpc-ecosystem/gateway + out: api/gen + opt: paths=source_relative + - remote: buf.build/grpc-ecosystem/openapiv2 + out: api/gen + opt: openapi_naming_strategy=simple diff --git a/buf.lock b/buf.lock new file mode 100644 index 0000000..5df8acd --- /dev/null +++ b/buf.lock @@ -0,0 +1,6 @@ +# Generated by buf. DO NOT EDIT. +version: v2 +deps: + - name: buf.build/googleapis/googleapis + commit: 004180b77378443887d3b55cabc00384 + digest: b5:e8f475fe3330f31f5fd86ac689093bcd274e19611a09db91f41d637cb9197881ce89882b94d13a58738e53c91c6e4bae7dc1feba85f590164c975a89e25115dc diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..c09e687 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,5 @@ +version: v2 +modules: + - path: api/proto +deps: + - buf.build/googleapis/googleapis diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 0000000..c5878a3 --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1,119 @@ +package main + +import ( + "context" + "flag" + "html/template" + "net" + "net/http" + "os/signal" + "syscall" + + buildapiv1 "github.com/builderhub/build-api/api/gen/buildapi/v1" + "github.com/builderhub/build-api/internal/server" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +const swaggerUIHTML = ` + + + BuildAPI - Swagger UI + + + +
+ + + + +` + +func swaggerUIHandler() http.HandlerFunc { + tmpl := template.Must(template.New("swagger").Parse(swaggerUIHTML)) + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + _ = tmpl.Execute(w, nil) + } +} + +func main() { + grpcAddr := flag.String("grpc-addr", ":9090", "gRPC listen address") + httpAddr := flag.String("http-addr", ":8080", "HTTP health listen address") + kubeconfig := flag.String("kubeconfig-path", "", "Path to kubeconfig (empty for in-cluster or KUBECONFIG env)") + flag.Parse() + + log, err := zap.NewProduction() + if err != nil { + panic(err) + } + defer log.Sync() + sugar := log.Sugar() + + ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer cancel() + + k8sClient, err := server.NewK8sClient(*kubeconfig) + if err != nil { + log.Fatal("failed to create k8s client", zap.Error(err)) + } + + svc := server.NewBuildAPIService(k8sClient, sugar) + srv := grpc.NewServer() + buildapiv1.RegisterBuildAPIServer(srv, svc) + reflection.Register(srv) + + lis, err := net.Listen("tcp", *grpcAddr) + if err != nil { + log.Fatal("failed to listen", zap.String("addr", *grpcAddr), zap.Error(err)) + } + + go func() { + log.Info("gRPC listening", zap.String("addr", *grpcAddr)) + if err := srv.Serve(lis); err != nil && err != grpc.ErrServerStopped { + log.Error("gRPC serve error", zap.Error(err)) + } + }() + + mux := http.NewServeMux() + mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ok")) + }) + mux.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ok")) + }) + + gwMux := runtime.NewServeMux() + if err := buildapiv1.RegisterBuildAPIHandlerServer(context.Background(), gwMux, svc); err != nil { + log.Fatal("failed to register gateway", zap.Error(err)) + } + mux.Handle("/v1/", gwMux) + mux.HandleFunc("/openapi.json", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(buildapiv1.OpenAPISpec) + }) + mux.HandleFunc("/docs", swaggerUIHandler()) + mux.HandleFunc("/docs/", swaggerUIHandler()) + httpSrv := &http.Server{Addr: *httpAddr, Handler: mux} + go func() { + log.Info("HTTP health listening", zap.String("addr", *httpAddr)) + if err := httpSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Error("HTTP serve error", zap.Error(err)) + } + }() + + log.Info("Swagger UI at /docs, OpenAPI spec at /openapi.json") + <-ctx.Done() + log.Info("shutting down...") + srv.GracefulStop() + _ = httpSrv.Shutdown(context.Background()) +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..3c3a5a8 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1771369470, + "narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0182a361324364ae3f436a63005877674cf45efb", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..5e8314c --- /dev/null +++ b/flake.nix @@ -0,0 +1,38 @@ +{ + description = "BuilderHub Build API - gRPC + HTTP API for BuildKit builders"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ] (system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + go + buf + protoc-gen-go + protoc-gen-go-grpc + go-migrate + tilt + kubectl + kind + gnumake + ]; + + shellHook = '' + echo "BuilderHub Build API Dev Environment" + echo "Go: $(go version)" + echo "Buf: $(buf --version 2>/dev/null || true)" + echo "" + echo "Run 'make generate' to generate proto code" + ''; + }; + } + ); +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2385dd8 --- /dev/null +++ b/go.mod @@ -0,0 +1,72 @@ +module github.com/builderhub/build-api + +go 1.25 + +require ( + github.com/builderhub/build-operator v0.0.0-beta.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 + go.uber.org/zap v1.27.0 + google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 + google.golang.org/grpc v1.79.1 + google.golang.org/protobuf v1.36.11 + k8s.io/apimachinery v0.30.1 + k8s.io/client-go v0.30.1 + k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 + sigs.k8s.io/controller-runtime v0.18.3 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.9.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/oauth2 v0.35.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/term v0.39.0 // indirect + golang.org/x/text v0.34.0 // indirect + golang.org/x/time v0.5.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.30.1 // indirect + k8s.io/apiextensions-apiserver v0.30.1 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..523c7a9 --- /dev/null +++ b/go.sum @@ -0,0 +1,216 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/builderhub/build-operator v0.0.0-beta.0 h1:Um+jhBqOMD7BWGutT3ip1WAVkqy17hTnc0wGwQvq+xw= +github.com/builderhub/build-operator v0.0.0-beta.0/go.mod h1:IrKWR+W1A11hfKhma6baEf4CiY615DY4saN0R/lFh+Y= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= +github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= +golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= +golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0= +google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.30.1 h1:kCm/6mADMdbAxmIh0LBjS54nQBE+U4KmbCfIkF5CpJY= +k8s.io/api v0.30.1/go.mod h1:ddbN2C0+0DIiPntan/bye3SW3PdwLa11/0yqwvuRrJM= +k8s.io/apiextensions-apiserver v0.30.1 h1:4fAJZ9985BmpJG6PkoxVRpXv9vmPUOVzl614xarePws= +k8s.io/apiextensions-apiserver v0.30.1/go.mod h1:R4GuSrlhgq43oRY9sF2IToFh7PVlF1JjfWdoG3pixk4= +k8s.io/apimachinery v0.30.1 h1:ZQStsEfo4n65yAdlGTfP/uSHMQSoYzU/oeEbkmF7P2U= +k8s.io/apimachinery v0.30.1/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.1 h1:uC/Ir6A3R46wdkgCV3vbLyNOYyCJ8oZnjtJGKfytl/Q= +k8s.io/client-go v0.30.1/go.mod h1:wrAqLNs2trwiCH/wxxmT/x3hKVH9PuV0GGW0oDoHVqc= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak= +k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.18.3 h1:B5Wmmo8WMWK7izei+2LlXLVDGzMwAHBNLX68lwtlSR4= +sigs.k8s.io/controller-runtime v0.18.3/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/helm/build-api/.helmignore b/helm/build-api/.helmignore new file mode 100644 index 0000000..119c0eb --- /dev/null +++ b/helm/build-api/.helmignore @@ -0,0 +1,19 @@ +# Patterns to ignore when building packages. +.DS_Store +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +*.swp +*.bak +*.tmp +*.orig +*~ +.project +.idea/ +*.tmproj +.vscode/ +README.md diff --git a/helm/build-api/Chart.yaml b/helm/build-api/Chart.yaml new file mode 100644 index 0000000..34ddeb0 --- /dev/null +++ b/helm/build-api/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v2 +name: build-api +description: A Helm chart for BuilderHub Build API (gRPC + REST) +type: application +version: 0.1.0 +appVersion: "0.1.0" +keywords: + - builderhub + - build-api + - buildkit + - grpc +home: https://github.com/builderhub/build-api +sources: + - https://github.com/builderhub/build-api +maintainers: + - name: BuilderHub Team diff --git a/helm/build-api/templates/NOTES.txt b/helm/build-api/templates/NOTES.txt new file mode 100644 index 0000000..1301a69 --- /dev/null +++ b/helm/build-api/templates/NOTES.txt @@ -0,0 +1,28 @@ +1. Get the API URL: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} + Swagger UI: http{{ if .Values.ingress.tls }}s{{ end }}:///docs + OpenAPI: http{{ if .Values.ingress.tls }}s{{ end }}:///openapi.json +{{- else if eq .Values.service.type "LoadBalancer" }} + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "build-api.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo "HTTP: http://$SERVICE_IP:{{ .Values.service.http.port }}" + echo "gRPC: $SERVICE_IP:{{ .Values.service.grpc.port }}" + echo "Docs: http://$SERVICE_IP:{{ .Values.service.http.port }}/docs" +{{- else }} + kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "build-api.fullname" . }} 8080:{{ .Values.service.http.port }} 9090:{{ .Values.service.grpc.port }} + echo "HTTP: http://127.0.0.1:8080" + echo "gRPC: 127.0.0.1:9090" + echo "Docs: http://127.0.0.1:8080/docs" +{{- end }} + +2. View logs: + kubectl logs -f --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "build-api.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" + +3. Check deployment: + kubectl get deployment --namespace {{ .Release.Namespace }} {{ include "build-api.fullname" . }} + +Thank you for installing {{ .Chart.Name }}! diff --git a/helm/build-api/templates/_helpers.tpl b/helm/build-api/templates/_helpers.tpl new file mode 100644 index 0000000..a2263ac --- /dev/null +++ b/helm/build-api/templates/_helpers.tpl @@ -0,0 +1,68 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "build-api.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "build-api.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "build-api.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "build-api.labels" -}} +helm.sh/chart: {{ include "build-api.chart" . }} +{{ include "build-api.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "build-api.selectorLabels" -}} +app.kubernetes.io/name: {{ include "build-api.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "build-api.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "build-api.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Image reference +*/}} +{{- define "build-api.image" -}} +{{- $tag := .Values.image.tag | default .Chart.AppVersion }} +{{- printf "%s:%s" .Values.image.repository $tag }} +{{- end }} diff --git a/helm/build-api/templates/deployment.yaml b/helm/build-api/templates/deployment.yaml new file mode 100644 index 0000000..898e274 --- /dev/null +++ b/helm/build-api/templates/deployment.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "build-api.fullname" . }} + labels: + {{- include "build-api.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "build-api.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "build-api.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "build-api.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: {{ include "build-api.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - --grpc-addr=:{{ .Values.service.grpc.targetPort }} + - --http-addr=:{{ .Values.service.http.targetPort }} + - --kubeconfig-path= + ports: + - name: http + containerPort: {{ .Values.service.http.targetPort }} + protocol: TCP + - name: grpc + containerPort: {{ .Values.service.grpc.targetPort }} + protocol: TCP + {{- with .Values.env }} + env: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.envFrom }} + envFrom: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm/build-api/templates/hpa.yaml b/helm/build-api/templates/hpa.yaml new file mode 100644 index 0000000..3799eab --- /dev/null +++ b/helm/build-api/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "build-api.fullname" . }} + labels: + {{- include "build-api.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "build-api.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm/build-api/templates/ingress.yaml b/helm/build-api/templates/ingress.yaml new file mode 100644 index 0000000..5413ab1 --- /dev/null +++ b/helm/build-api/templates/ingress.yaml @@ -0,0 +1,41 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "build-api.fullname" . }} + labels: + {{- include "build-api.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "build-api.fullname" $ }} + port: + number: {{ $.Values.service.http.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm/build-api/templates/rbac.yaml b/helm/build-api/templates/rbac.yaml new file mode 100644 index 0000000..03bbf20 --- /dev/null +++ b/helm/build-api/templates/rbac.yaml @@ -0,0 +1,43 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "build-api.fullname" . }} + labels: + {{- include "build-api.labels" . | nindent 4 }} +rules: +- apiGroups: + - builder-hub.dev + resources: + - buildkitbuilders + verbs: + - get + - list + - watch + - create + - patch + - update +- apiGroups: + - builder-hub.dev + resources: + - buildkitbuildertemplates + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "build-api.fullname" . }} + labels: + {{- include "build-api.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "build-api.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "build-api.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/helm/build-api/templates/service.yaml b/helm/build-api/templates/service.yaml new file mode 100644 index 0000000..292efe5 --- /dev/null +++ b/helm/build-api/templates/service.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "build-api.fullname" . }} + labels: + {{- include "build-api.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.http.port }} + targetPort: http + protocol: TCP + name: http + - port: {{ .Values.service.grpc.port }} + targetPort: grpc + protocol: TCP + name: grpc + selector: + {{- include "build-api.selectorLabels" . | nindent 4 }} diff --git a/helm/build-api/templates/serviceaccount.yaml b/helm/build-api/templates/serviceaccount.yaml new file mode 100644 index 0000000..dea123d --- /dev/null +++ b/helm/build-api/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "build-api.serviceAccountName" . }} + labels: + {{- include "build-api.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/helm/build-api/values.yaml b/helm/build-api/values.yaml new file mode 100644 index 0000000..b3c9006 --- /dev/null +++ b/helm/build-api/values.yaml @@ -0,0 +1,97 @@ +replicaCount: 1 + +image: + repository: ghcr.io/builderhub/build-api + pullPolicy: IfNotPresent + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + create: true + automount: true + annotations: {} + name: "" + +rbac: + create: true + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + runAsUser: 65532 + seccompProfile: + type: RuntimeDefault + +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + +service: + type: ClusterIP + annotations: {} + http: + port: 8080 + targetPort: 8080 + grpc: + port: 9090 + targetPort: 9090 + +ingress: + enabled: false + className: "" + annotations: {} + hosts: + - host: api.builderhub.example.com + paths: + - path: / + pathType: Prefix + tls: [] + +resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 50m + memory: 128Mi + +livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +readinessProbe: + httpGet: + path: /ready + port: http + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} +tolerations: [] +affinity: {} + +env: [] + +envFrom: [] diff --git a/internal/k8s/client.go b/internal/k8s/client.go new file mode 100644 index 0000000..3f5e2b3 --- /dev/null +++ b/internal/k8s/client.go @@ -0,0 +1,48 @@ +package k8s + +import ( + "path/filepath" + + buildkitv1alpha1 "github.com/builderhub/build-operator/api/v1alpha1" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime/pkg/client" +) + +// Client wraps controller-runtime client for BuildkitBuilder CRs. +type Client struct { + ctrl.Client +} + +// NewClient creates a K8s client that can manage BuildkitBuilder CRs. +func NewClient(kubeconfig string) (*Client, error) { + config, err := restConfig(kubeconfig) + if err != nil { + return nil, err + } + scheme := runtime.NewScheme() + if err := clientgoscheme.AddToScheme(scheme); err != nil { + return nil, err + } + if err := buildkitv1alpha1.AddToScheme(scheme); err != nil { + return nil, err + } + client, err := ctrl.New(config, ctrl.Options{Scheme: scheme}) + if err != nil { + return nil, err + } + return &Client{Client: client}, nil +} + +func restConfig(kubeconfig string) (*rest.Config, error) { + if kubeconfig != "" { + return clientcmd.BuildConfigFromFlags("", kubeconfig) + } + if home := homedir.HomeDir(); home != "" { + kubeconfig = filepath.Join(home, ".kube", "config") + } + return clientcmd.BuildConfigFromFlags("", kubeconfig) +} diff --git a/internal/server/handlers.go b/internal/server/handlers.go new file mode 100644 index 0000000..b631bc0 --- /dev/null +++ b/internal/server/handlers.go @@ -0,0 +1,150 @@ +package server + +import ( + "context" + "fmt" + "time" + + buildapiv1 "github.com/builderhub/build-api/api/gen/buildapi/v1" + buildkitv1alpha1 "github.com/builderhub/build-operator/api/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime/pkg/client" +) + +const lastUsedAnnotation = "builder-hub.dev/last-used" + +// BuildAPIService implements buildapiv1.BuildAPIServer. +// Logger is a minimal interface for structured logging. +type Logger interface { + Infow(msg string, keysAndValues ...interface{}) + Errorw(msg string, keysAndValues ...interface{}) +} + +type BuildAPIService struct { + buildapiv1.UnimplementedBuildAPIServer + k8s *K8sClient + log Logger +} + +// NewBuildAPIService creates a new BuildAPIService. +func NewBuildAPIService(k8s *K8sClient, log Logger) *BuildAPIService { + return &BuildAPIService{k8s: k8s, log: log} +} + +// ListBuilders returns all BuildkitBuilder CRs in a namespace. +func (s *BuildAPIService) ListBuilders(ctx context.Context, req *buildapiv1.ListBuildersRequest) (*buildapiv1.ListBuildersResponse, error) { + var list buildkitv1alpha1.BuildkitBuilderList + opts := []ctrl.ListOption{} + if req.Namespace != "" { + opts = append(opts, ctrl.InNamespace(req.Namespace)) + } + if err := s.k8s.List(ctx, &list, opts...); err != nil { + return nil, fmt.Errorf("list builders: %w", err) + } + builders := make([]*buildapiv1.Builder, 0, len(list.Items)) + for i := range list.Items { + builders = append(builders, crToProto(&list.Items[i])) + } + return &buildapiv1.ListBuildersResponse{Builders: builders}, nil +} + +// GetBuilder returns a single BuildkitBuilder by name. +func (s *BuildAPIService) GetBuilder(ctx context.Context, req *buildapiv1.GetBuilderRequest) (*buildapiv1.GetBuilderResponse, error) { + var b buildkitv1alpha1.BuildkitBuilder + if err := s.k8s.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, &b); err != nil { + return nil, fmt.Errorf("get builder: %w", err) + } + return &buildapiv1.GetBuilderResponse{Builder: crToProto(&b)}, nil +} + +// CreateBuilder creates a new BuildkitBuilder CR. +func (s *BuildAPIService) CreateBuilder(ctx context.Context, req *buildapiv1.CreateBuilderRequest) (*buildapiv1.CreateBuilderResponse, error) { + b := &buildkitv1alpha1.BuildkitBuilder{ + ObjectMeta: metav1.ObjectMeta{Namespace: req.Namespace, Name: req.Name}, + Spec: protoSpecToCR(req.Spec), + } + if err := s.k8s.Create(ctx, b); err != nil { + return nil, fmt.Errorf("create builder: %w", err) + } + s.log.Infow("created builder", "namespace", req.Namespace, "name", req.Name) + return &buildapiv1.CreateBuilderResponse{Builder: crToProto(b)}, nil +} + +// WakeBuilder patches builder-hub.dev/last-used for sleepy builders. +func (s *BuildAPIService) WakeBuilder(ctx context.Context, req *buildapiv1.WakeBuilderRequest) (*buildapiv1.WakeBuilderResponse, error) { + var b buildkitv1alpha1.BuildkitBuilder + if err := s.k8s.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, &b); err != nil { + return nil, fmt.Errorf("get builder: %w", err) + } + original := b.DeepCopy() + modified := b.DeepCopy() + if modified.Annotations == nil { + modified.Annotations = make(map[string]string) + } + modified.Annotations[lastUsedAnnotation] = time.Now().UTC().Format(time.RFC3339) + if err := s.k8s.Patch(ctx, modified, ctrl.MergeFrom(original)); err != nil { + return nil, fmt.Errorf("patch builder: %w", err) + } + s.log.Infow("woke builder", "namespace", req.Namespace, "name", req.Name) + return &buildapiv1.WakeBuilderResponse{Builder: crToProto(modified)}, nil +} + +// HealthCheck returns health status. +func (s *BuildAPIService) HealthCheck(ctx context.Context, _ *buildapiv1.HealthCheckRequest) (*buildapiv1.HealthCheckResponse, error) { + return &buildapiv1.HealthCheckResponse{Status: "ok"}, nil +} + +func crToProto(b *buildkitv1alpha1.BuildkitBuilder) *buildapiv1.Builder { + spec := &buildapiv1.BuilderSpec{ + Mode: string(b.Spec.Mode), + } + if b.Spec.TemplateRef != nil { + spec.TemplateRef = *b.Spec.TemplateRef + } + if b.Spec.Replicas != nil { + spec.Replicas = *b.Spec.Replicas + } else { + spec.Replicas = 1 + } + if b.Spec.IdleTimeoutSeconds != nil { + spec.IdleTimeoutSeconds = *b.Spec.IdleTimeoutSeconds + } else { + spec.IdleTimeoutSeconds = 300 + } + if b.Spec.Labels != nil { + spec.Labels = b.Spec.Labels + } + + status := &buildapiv1.BuilderStatus{ + Endpoint: b.Status.Endpoint, + NodePort: b.Status.NodePort, + Phase: b.Status.Phase, + } + return &buildapiv1.Builder{ + Namespace: b.Namespace, + Name: b.Name, + Spec: spec, + Status: status, + } +} + +func protoSpecToCR(s *buildapiv1.BuilderSpec) buildkitv1alpha1.BuildkitBuilderSpec { + spec := buildkitv1alpha1.BuildkitBuilderSpec{ + Mode: buildkitv1alpha1.BuilderMode(s.Mode), + } + if s.TemplateRef != "" { + spec.TemplateRef = ptr.To(s.TemplateRef) + } + if s.Replicas > 0 { + spec.Replicas = ptr.To(s.Replicas) + } + if s.IdleTimeoutSeconds > 0 { + spec.IdleTimeoutSeconds = ptr.To(s.IdleTimeoutSeconds) + } + if s.Labels != nil { + spec.Labels = s.Labels + } + return spec +} diff --git a/internal/server/k8s.go b/internal/server/k8s.go new file mode 100644 index 0000000..40c839e --- /dev/null +++ b/internal/server/k8s.go @@ -0,0 +1,13 @@ +package server + +import ( + "github.com/builderhub/build-api/internal/k8s" +) + +// K8sClient wraps the k8s client for use by BuildAPIService. +type K8sClient = k8s.Client + +// NewK8sClient creates a K8s client. +func NewK8sClient(kubeconfig string) (*K8sClient, error) { + return k8s.NewClient(kubeconfig) +} diff --git a/migrations/000001_init.down.sql b/migrations/000001_init.down.sql new file mode 100644 index 0000000..8d43df9 --- /dev/null +++ b/migrations/000001_init.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS schema_version; diff --git a/migrations/000001_init.up.sql b/migrations/000001_init.up.sql new file mode 100644 index 0000000..8037555 --- /dev/null +++ b/migrations/000001_init.up.sql @@ -0,0 +1,5 @@ +-- Build API initial schema (placeholder for future build session metadata) +CREATE TABLE IF NOT EXISTS schema_version ( + version INTEGER PRIMARY KEY, + dirty BOOLEAN NOT NULL DEFAULT FALSE +);