diff --git a/generated/go/codefly/base/v0/agent.pb.go b/generated/go/codefly/base/v0/agent.pb.go index 018619e5..bbf5b16e 100644 --- a/generated/go/codefly/base/v0/agent.pb.go +++ b/generated/go/codefly/base/v0/agent.pb.go @@ -47,6 +47,11 @@ const ( // PROVIDER agents reconcile environment-scoped external provider bindings. // They are typed leaf plugins and never own a service workload lifecycle. Agent_PROVIDER Agent_Kind = 6 + // SOLUTION agents scaffold a solution, package it as an OCI artifact, and + // render its manifests into a gitops repository. Like providers they are + // dynamically installable and stay out of the application graph; the + // solution spec they operate on stays codefly-agnostic. + Agent_SOLUTION Agent_Kind = 7 ) // Enum value maps for Agent_Kind. @@ -59,6 +64,7 @@ var ( 4: "MODULE", 5: "TOOLBOX", 6: "PROVIDER", + 7: "SOLUTION", } Agent_Kind_value = map[string]int32{ "UNKNOWN": 0, @@ -68,6 +74,7 @@ var ( "MODULE": 4, "TOOLBOX": 5, "PROVIDER": 6, + "SOLUTION": 7, } ) @@ -98,7 +105,7 @@ func (Agent_Kind) EnumDescriptor() ([]byte, []int) { return file_codefly_base_v0_agent_proto_rawDescGZIP(), []int{0, 0} } -// Agent identifies the plugin binary that owns a service, job, application, module, or toolbox lifecycle. +// Agent identifies the plugin binary that owns a service, job, application, module, toolbox, provider, or solution lifecycle. type Agent struct { state protoimpl.MessageState `protogen:"open.v1"` // kind identifies which resource family this agent can manage. @@ -175,12 +182,12 @@ var File_codefly_base_v0_agent_proto protoreflect.FileDescriptor const file_codefly_base_v0_agent_proto_rawDesc = "" + "\n" + - "\x1bcodefly/base/v0/agent.proto\x12\x0fcodefly.base.v0\x1a\x1bbuf/validate/validate.proto\"\xfd\x01\n" + + "\x1bcodefly/base/v0/agent.proto\x12\x0fcodefly.base.v0\x1a\x1bbuf/validate/validate.proto\"\x8b\x02\n" + "\x05Agent\x12/\n" + "\x04kind\x18\x01 \x01(\x0e2\x1b.codefly.base.v0.Agent.KindR\x04kind\x12\x1d\n" + "\x04name\x18\x02 \x01(\tB\t\xbaH\x06r\x04\x10\x02\x182R\x04name\x12'\n" + "\tpublisher\x18\x03 \x01(\tB\t\xbaH\x06r\x04\x10\x03\x182R\tpublisher\x12\x18\n" + - "\aversion\x18\x04 \x01(\tR\aversion\"a\n" + + "\aversion\x18\x04 \x01(\tR\aversion\"o\n" + "\x04Kind\x12\v\n" + "\aUNKNOWN\x10\x00\x12\v\n" + "\aSERVICE\x10\x01\x12\a\n" + @@ -189,7 +196,8 @@ const file_codefly_base_v0_agent_proto_rawDesc = "" + "\n" + "\x06MODULE\x10\x04\x12\v\n" + "\aTOOLBOX\x10\x05\x12\f\n" + - "\bPROVIDER\x10\x06B\xb9\x01\n" + + "\bPROVIDER\x10\x06\x12\f\n" + + "\bSOLUTION\x10\aB\xb9\x01\n" + "\x13com.codefly.base.v0B\n" + "AgentProtoP\x01Z8github.com/codefly-dev/core/generated/go/codefly/base/v0\xa2\x02\x03CBV\xaa\x02\x0fCodefly.Base.V0\xca\x02\x0fCodefly\\Base\\V0\xe2\x02\x1bCodefly\\Base\\V0\\GPBMetadata\xea\x02\x11Codefly::Base::V0b\x06proto3" diff --git a/generated/go/codefly/services/solution/v0/solution.pb.go b/generated/go/codefly/services/solution/v0/solution.pb.go new file mode 100644 index 00000000..1818a242 --- /dev/null +++ b/generated/go/codefly/services/solution/v0/solution.pb.go @@ -0,0 +1,1015 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: codefly/services/solution/v0/solution.proto + +package v0 + +import ( + reflect "reflect" + sync "sync" + unsafe "unsafe" + + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + v0 "github.com/codefly-dev/core/generated/go/codefly/base/v0" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +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) +) + +// SolutionArtifact binds one concrete solution executor binary to its package. +type SolutionArtifact struct { + state protoimpl.MessageState `protogen:"open.v1"` + // publisher is the agent publisher namespace. + Publisher string `protobuf:"bytes,1,opt,name=publisher,proto3" json:"publisher,omitempty"` + // name is the solution agent name. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // version is the concrete agent version. + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + // artifact_digest is the verified package digest. + ArtifactDigest string `protobuf:"bytes,4,opt,name=artifact_digest,json=artifactDigest,proto3" json:"artifact_digest,omitempty"` + // manifest_digest is the packaged solution manifest digest. + ManifestDigest string `protobuf:"bytes,5,opt,name=manifest_digest,json=manifestDigest,proto3" json:"manifest_digest,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SolutionArtifact) Reset() { + *x = SolutionArtifact{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SolutionArtifact) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SolutionArtifact) ProtoMessage() {} + +func (x *SolutionArtifact) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 SolutionArtifact.ProtoReflect.Descriptor instead. +func (*SolutionArtifact) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{0} +} + +func (x *SolutionArtifact) GetPublisher() string { + if x != nil { + return x.Publisher + } + return "" +} + +func (x *SolutionArtifact) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *SolutionArtifact) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *SolutionArtifact) GetArtifactDigest() string { + if x != nil { + return x.ArtifactDigest + } + return "" +} + +func (x *SolutionArtifact) GetManifestDigest() string { + if x != nil { + return x.ManifestDigest + } + return "" +} + +// SolutionContext carries the environment-scoped identity for one invocation. +type SolutionContext struct { + state protoimpl.MessageState `protogen:"open.v1"` + // workspace is the stable workspace identity. + Workspace string `protobuf:"bytes,1,opt,name=workspace,proto3" json:"workspace,omitempty"` + // environment is the stable environment identity. + Environment string `protobuf:"bytes,2,opt,name=environment,proto3" json:"environment,omitempty"` + // artifact is the concrete verified executor package. + Artifact *SolutionArtifact `protobuf:"bytes,3,opt,name=artifact,proto3" json:"artifact,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SolutionContext) Reset() { + *x = SolutionContext{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SolutionContext) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SolutionContext) ProtoMessage() {} + +func (x *SolutionContext) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 SolutionContext.ProtoReflect.Descriptor instead. +func (*SolutionContext) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{1} +} + +func (x *SolutionContext) GetWorkspace() string { + if x != nil { + return x.Workspace + } + return "" +} + +func (x *SolutionContext) GetEnvironment() string { + if x != nil { + return x.Environment + } + return "" +} + +func (x *SolutionContext) GetArtifact() *SolutionArtifact { + if x != nil { + return x.Artifact + } + return nil +} + +// SolutionCapabilities advertises which lifecycle operations are implemented. +type SolutionCapabilities struct { + state protoimpl.MessageState `protogen:"open.v1"` + // supports_create declares Create support. + SupportsCreate bool `protobuf:"varint,1,opt,name=supports_create,json=supportsCreate,proto3" json:"supports_create,omitempty"` + // supports_update declares Update support. + SupportsUpdate bool `protobuf:"varint,2,opt,name=supports_update,json=supportsUpdate,proto3" json:"supports_update,omitempty"` + // supports_package declares Package support. + SupportsPackage bool `protobuf:"varint,3,opt,name=supports_package,json=supportsPackage,proto3" json:"supports_package,omitempty"` + // supports_render declares Render support. + SupportsRender bool `protobuf:"varint,4,opt,name=supports_render,json=supportsRender,proto3" json:"supports_render,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SolutionCapabilities) Reset() { + *x = SolutionCapabilities{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SolutionCapabilities) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SolutionCapabilities) ProtoMessage() {} + +func (x *SolutionCapabilities) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 SolutionCapabilities.ProtoReflect.Descriptor instead. +func (*SolutionCapabilities) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{2} +} + +func (x *SolutionCapabilities) GetSupportsCreate() bool { + if x != nil { + return x.SupportsCreate + } + return false +} + +func (x *SolutionCapabilities) GetSupportsUpdate() bool { + if x != nil { + return x.SupportsUpdate + } + return false +} + +func (x *SolutionCapabilities) GetSupportsPackage() bool { + if x != nil { + return x.SupportsPackage + } + return false +} + +func (x *SolutionCapabilities) GetSupportsRender() bool { + if x != nil { + return x.SupportsRender + } + return false +} + +// GetSolutionInformationRequest identifies the binary asking to advertise. +type GetSolutionInformationRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // artifact is the verified executor package identity. + Artifact *SolutionArtifact `protobuf:"bytes,1,opt,name=artifact,proto3" json:"artifact,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetSolutionInformationRequest) Reset() { + *x = GetSolutionInformationRequest{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSolutionInformationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSolutionInformationRequest) ProtoMessage() {} + +func (x *GetSolutionInformationRequest) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 GetSolutionInformationRequest.ProtoReflect.Descriptor instead. +func (*GetSolutionInformationRequest) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{3} +} + +func (x *GetSolutionInformationRequest) GetArtifact() *SolutionArtifact { + if x != nil { + return x.Artifact + } + return nil +} + +// GetSolutionInformationResponse returns immutable executor advertisement. +type GetSolutionInformationResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // artifact must exactly match the request identity. + Artifact *SolutionArtifact `protobuf:"bytes,1,opt,name=artifact,proto3" json:"artifact,omitempty"` + // capabilities are the implemented lifecycle operations. + Capabilities *SolutionCapabilities `protobuf:"bytes,2,opt,name=capabilities,proto3" json:"capabilities,omitempty"` + // diagnostics are bounded neutral diagnostics. + Diagnostics []*v0.FailureDiagnostic `protobuf:"bytes,3,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetSolutionInformationResponse) Reset() { + *x = GetSolutionInformationResponse{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSolutionInformationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSolutionInformationResponse) ProtoMessage() {} + +func (x *GetSolutionInformationResponse) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 GetSolutionInformationResponse.ProtoReflect.Descriptor instead. +func (*GetSolutionInformationResponse) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{4} +} + +func (x *GetSolutionInformationResponse) GetArtifact() *SolutionArtifact { + if x != nil { + return x.Artifact + } + return nil +} + +func (x *GetSolutionInformationResponse) GetCapabilities() *SolutionCapabilities { + if x != nil { + return x.Capabilities + } + return nil +} + +func (x *GetSolutionInformationResponse) GetDiagnostics() []*v0.FailureDiagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +// CreateRequest asks the executor to scaffold a new solution. +type CreateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // context carries the environment-scoped invocation identity. + Context *SolutionContext `protobuf:"bytes,1,opt,name=context,proto3" json:"context,omitempty"` + // destination is the directory the solution is scaffolded into. + Destination string `protobuf:"bytes,2,opt,name=destination,proto3" json:"destination,omitempty"` + // parameters are executor-defined scaffold inputs. + Parameters map[string]string `protobuf:"bytes,3,rep,name=parameters,proto3" json:"parameters,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateRequest) Reset() { + *x = CreateRequest{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateRequest) ProtoMessage() {} + +func (x *CreateRequest) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 CreateRequest.ProtoReflect.Descriptor instead. +func (*CreateRequest) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateRequest) GetContext() *SolutionContext { + if x != nil { + return x.Context + } + return nil +} + +func (x *CreateRequest) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +func (x *CreateRequest) GetParameters() map[string]string { + if x != nil { + return x.Parameters + } + return nil +} + +// CreateResponse reports the scaffolded solution. +type CreateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // created_paths are the paths written relative to the destination. + CreatedPaths []string `protobuf:"bytes,1,rep,name=created_paths,json=createdPaths,proto3" json:"created_paths,omitempty"` + // diagnostics are bounded neutral diagnostics. + Diagnostics []*v0.FailureDiagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateResponse) Reset() { + *x = CreateResponse{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateResponse) ProtoMessage() {} + +func (x *CreateResponse) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 CreateResponse.ProtoReflect.Descriptor instead. +func (*CreateResponse) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{6} +} + +func (x *CreateResponse) GetCreatedPaths() []string { + if x != nil { + return x.CreatedPaths + } + return nil +} + +func (x *CreateResponse) GetDiagnostics() []*v0.FailureDiagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +// UpdateRequest asks the executor to reconcile an existing solution source. +type UpdateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // context carries the environment-scoped invocation identity. + Context *SolutionContext `protobuf:"bytes,1,opt,name=context,proto3" json:"context,omitempty"` + // source is the existing solution directory to update. + Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` + // parameters are executor-defined update inputs. + Parameters map[string]string `protobuf:"bytes,3,rep,name=parameters,proto3" json:"parameters,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateRequest) Reset() { + *x = UpdateRequest{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateRequest) ProtoMessage() {} + +func (x *UpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 UpdateRequest.ProtoReflect.Descriptor instead. +func (*UpdateRequest) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{7} +} + +func (x *UpdateRequest) GetContext() *SolutionContext { + if x != nil { + return x.Context + } + return nil +} + +func (x *UpdateRequest) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *UpdateRequest) GetParameters() map[string]string { + if x != nil { + return x.Parameters + } + return nil +} + +// UpdateResponse reports the updated solution. +type UpdateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // updated_paths are the paths changed relative to the source. + UpdatedPaths []string `protobuf:"bytes,1,rep,name=updated_paths,json=updatedPaths,proto3" json:"updated_paths,omitempty"` + // diagnostics are bounded neutral diagnostics. + Diagnostics []*v0.FailureDiagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateResponse) Reset() { + *x = UpdateResponse{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateResponse) ProtoMessage() {} + +func (x *UpdateResponse) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 UpdateResponse.ProtoReflect.Descriptor instead. +func (*UpdateResponse) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{8} +} + +func (x *UpdateResponse) GetUpdatedPaths() []string { + if x != nil { + return x.UpdatedPaths + } + return nil +} + +func (x *UpdateResponse) GetDiagnostics() []*v0.FailureDiagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +// PackageRequest asks the executor to package a solution as an OCI artifact. +type PackageRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // context carries the environment-scoped invocation identity. + Context *SolutionContext `protobuf:"bytes,1,opt,name=context,proto3" json:"context,omitempty"` + // source is the solution directory to package. + Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` + // reference is the target OCI reference. + Reference string `protobuf:"bytes,3,opt,name=reference,proto3" json:"reference,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PackageRequest) Reset() { + *x = PackageRequest{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PackageRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PackageRequest) ProtoMessage() {} + +func (x *PackageRequest) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 PackageRequest.ProtoReflect.Descriptor instead. +func (*PackageRequest) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{9} +} + +func (x *PackageRequest) GetContext() *SolutionContext { + if x != nil { + return x.Context + } + return nil +} + +func (x *PackageRequest) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *PackageRequest) GetReference() string { + if x != nil { + return x.Reference + } + return "" +} + +// PackageResponse reports the packaged OCI artifact. +type PackageResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // reference is the pushed OCI reference. + Reference string `protobuf:"bytes,1,opt,name=reference,proto3" json:"reference,omitempty"` + // artifact_digest is the digest of the packaged artifact. + ArtifactDigest string `protobuf:"bytes,2,opt,name=artifact_digest,json=artifactDigest,proto3" json:"artifact_digest,omitempty"` + // diagnostics are bounded neutral diagnostics. + Diagnostics []*v0.FailureDiagnostic `protobuf:"bytes,3,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PackageResponse) Reset() { + *x = PackageResponse{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PackageResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PackageResponse) ProtoMessage() {} + +func (x *PackageResponse) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 PackageResponse.ProtoReflect.Descriptor instead. +func (*PackageResponse) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{10} +} + +func (x *PackageResponse) GetReference() string { + if x != nil { + return x.Reference + } + return "" +} + +func (x *PackageResponse) GetArtifactDigest() string { + if x != nil { + return x.ArtifactDigest + } + return "" +} + +func (x *PackageResponse) GetDiagnostics() []*v0.FailureDiagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +// RenderRequest asks the executor to render manifests into a gitops destination. +type RenderRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // context carries the environment-scoped invocation identity. + Context *SolutionContext `protobuf:"bytes,1,opt,name=context,proto3" json:"context,omitempty"` + // artifact_reference is the packaged OCI reference to render from. + ArtifactReference string `protobuf:"bytes,2,opt,name=artifact_reference,json=artifactReference,proto3" json:"artifact_reference,omitempty"` + // destination is the gitops repository path manifests are rendered into. + Destination string `protobuf:"bytes,3,opt,name=destination,proto3" json:"destination,omitempty"` + // values are executor-defined render inputs. + Values map[string]string `protobuf:"bytes,4,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RenderRequest) Reset() { + *x = RenderRequest{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RenderRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RenderRequest) ProtoMessage() {} + +func (x *RenderRequest) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 RenderRequest.ProtoReflect.Descriptor instead. +func (*RenderRequest) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{11} +} + +func (x *RenderRequest) GetContext() *SolutionContext { + if x != nil { + return x.Context + } + return nil +} + +func (x *RenderRequest) GetArtifactReference() string { + if x != nil { + return x.ArtifactReference + } + return "" +} + +func (x *RenderRequest) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +func (x *RenderRequest) GetValues() map[string]string { + if x != nil { + return x.Values + } + return nil +} + +// RenderResponse reports the rendered manifests. +type RenderResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // rendered_paths are the manifest paths written relative to the destination. + RenderedPaths []string `protobuf:"bytes,1,rep,name=rendered_paths,json=renderedPaths,proto3" json:"rendered_paths,omitempty"` + // diagnostics are bounded neutral diagnostics. + Diagnostics []*v0.FailureDiagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RenderResponse) Reset() { + *x = RenderResponse{} + mi := &file_codefly_services_solution_v0_solution_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RenderResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RenderResponse) ProtoMessage() {} + +func (x *RenderResponse) ProtoReflect() protoreflect.Message { + mi := &file_codefly_services_solution_v0_solution_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 RenderResponse.ProtoReflect.Descriptor instead. +func (*RenderResponse) Descriptor() ([]byte, []int) { + return file_codefly_services_solution_v0_solution_proto_rawDescGZIP(), []int{12} +} + +func (x *RenderResponse) GetRenderedPaths() []string { + if x != nil { + return x.RenderedPaths + } + return nil +} + +func (x *RenderResponse) GetDiagnostics() []*v0.FailureDiagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +var File_codefly_services_solution_v0_solution_proto protoreflect.FileDescriptor + +const file_codefly_services_solution_v0_solution_proto_rawDesc = "" + + "\n" + + "+codefly/services/solution/v0/solution.proto\x12\x1ccodefly.services.solution.v0\x1a\x1bbuf/validate/validate.proto\x1a\x1dcodefly/base/v0/failure.proto\"\xb0\x01\n" + + "\x10SolutionArtifact\x12\x1c\n" + + "\tpublisher\x18\x01 \x01(\tR\tpublisher\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12\x18\n" + + "\aversion\x18\x03 \x01(\tR\aversion\x12'\n" + + "\x0fartifact_digest\x18\x04 \x01(\tR\x0eartifactDigest\x12'\n" + + "\x0fmanifest_digest\x18\x05 \x01(\tR\x0emanifestDigest\"\x9d\x01\n" + + "\x0fSolutionContext\x12\x1c\n" + + "\tworkspace\x18\x01 \x01(\tR\tworkspace\x12 \n" + + "\venvironment\x18\x02 \x01(\tR\venvironment\x12J\n" + + "\bartifact\x18\x03 \x01(\v2..codefly.services.solution.v0.SolutionArtifactR\bartifact\"\xbc\x01\n" + + "\x14SolutionCapabilities\x12'\n" + + "\x0fsupports_create\x18\x01 \x01(\bR\x0esupportsCreate\x12'\n" + + "\x0fsupports_update\x18\x02 \x01(\bR\x0esupportsUpdate\x12)\n" + + "\x10supports_package\x18\x03 \x01(\bR\x0fsupportsPackage\x12'\n" + + "\x0fsupports_render\x18\x04 \x01(\bR\x0esupportsRender\"k\n" + + "\x1dGetSolutionInformationRequest\x12J\n" + + "\bartifact\x18\x01 \x01(\v2..codefly.services.solution.v0.SolutionArtifactR\bartifact\"\x94\x02\n" + + "\x1eGetSolutionInformationResponse\x12J\n" + + "\bartifact\x18\x01 \x01(\v2..codefly.services.solution.v0.SolutionArtifactR\bartifact\x12V\n" + + "\fcapabilities\x18\x02 \x01(\v22.codefly.services.solution.v0.SolutionCapabilitiesR\fcapabilities\x12N\n" + + "\vdiagnostics\x18\x03 \x03(\v2\".codefly.base.v0.FailureDiagnosticB\b\xbaH\x05\x92\x01\x02\x10dR\vdiagnostics\"\x96\x02\n" + + "\rCreateRequest\x12G\n" + + "\acontext\x18\x01 \x01(\v2-.codefly.services.solution.v0.SolutionContextR\acontext\x12 \n" + + "\vdestination\x18\x02 \x01(\tR\vdestination\x12[\n" + + "\n" + + "parameters\x18\x03 \x03(\v2;.codefly.services.solution.v0.CreateRequest.ParametersEntryR\n" + + "parameters\x1a=\n" + + "\x0fParametersEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x90\x01\n" + + "\x0eCreateResponse\x12.\n" + + "\rcreated_paths\x18\x01 \x03(\tB\t\xbaH\x06\x92\x01\x03\x10\x90NR\fcreatedPaths\x12N\n" + + "\vdiagnostics\x18\x02 \x03(\v2\".codefly.base.v0.FailureDiagnosticB\b\xbaH\x05\x92\x01\x02\x10dR\vdiagnostics\"\x8c\x02\n" + + "\rUpdateRequest\x12G\n" + + "\acontext\x18\x01 \x01(\v2-.codefly.services.solution.v0.SolutionContextR\acontext\x12\x16\n" + + "\x06source\x18\x02 \x01(\tR\x06source\x12[\n" + + "\n" + + "parameters\x18\x03 \x03(\v2;.codefly.services.solution.v0.UpdateRequest.ParametersEntryR\n" + + "parameters\x1a=\n" + + "\x0fParametersEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x90\x01\n" + + "\x0eUpdateResponse\x12.\n" + + "\rupdated_paths\x18\x01 \x03(\tB\t\xbaH\x06\x92\x01\x03\x10\x90NR\fupdatedPaths\x12N\n" + + "\vdiagnostics\x18\x02 \x03(\v2\".codefly.base.v0.FailureDiagnosticB\b\xbaH\x05\x92\x01\x02\x10dR\vdiagnostics\"\x8f\x01\n" + + "\x0ePackageRequest\x12G\n" + + "\acontext\x18\x01 \x01(\v2-.codefly.services.solution.v0.SolutionContextR\acontext\x12\x16\n" + + "\x06source\x18\x02 \x01(\tR\x06source\x12\x1c\n" + + "\treference\x18\x03 \x01(\tR\treference\"\xa8\x01\n" + + "\x0fPackageResponse\x12\x1c\n" + + "\treference\x18\x01 \x01(\tR\treference\x12'\n" + + "\x0fartifact_digest\x18\x02 \x01(\tR\x0eartifactDigest\x12N\n" + + "\vdiagnostics\x18\x03 \x03(\v2\".codefly.base.v0.FailureDiagnosticB\b\xbaH\x05\x92\x01\x02\x10dR\vdiagnostics\"\xb5\x02\n" + + "\rRenderRequest\x12G\n" + + "\acontext\x18\x01 \x01(\v2-.codefly.services.solution.v0.SolutionContextR\acontext\x12-\n" + + "\x12artifact_reference\x18\x02 \x01(\tR\x11artifactReference\x12 \n" + + "\vdestination\x18\x03 \x01(\tR\vdestination\x12O\n" + + "\x06values\x18\x04 \x03(\v27.codefly.services.solution.v0.RenderRequest.ValuesEntryR\x06values\x1a9\n" + + "\vValuesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x92\x01\n" + + "\x0eRenderResponse\x120\n" + + "\x0erendered_paths\x18\x01 \x03(\tB\t\xbaH\x06\x92\x01\x03\x10\x90NR\rrenderedPaths\x12N\n" + + "\vdiagnostics\x18\x02 \x03(\v2\".codefly.base.v0.FailureDiagnosticB\b\xbaH\x05\x92\x01\x02\x10dR\vdiagnostics2\xb7\x04\n" + + "\bSolution\x12\x93\x01\n" + + "\x16GetSolutionInformation\x12;.codefly.services.solution.v0.GetSolutionInformationRequest\x1a<.codefly.services.solution.v0.GetSolutionInformationResponse\x12c\n" + + "\x06Create\x12+.codefly.services.solution.v0.CreateRequest\x1a,.codefly.services.solution.v0.CreateResponse\x12c\n" + + "\x06Update\x12+.codefly.services.solution.v0.UpdateRequest\x1a,.codefly.services.solution.v0.UpdateResponse\x12f\n" + + "\aPackage\x12,.codefly.services.solution.v0.PackageRequest\x1a-.codefly.services.solution.v0.PackageResponse\x12c\n" + + "\x06Render\x12+.codefly.services.solution.v0.RenderRequest\x1a,.codefly.services.solution.v0.RenderResponseB\x8c\x02\n" + + " com.codefly.services.solution.v0B\rSolutionProtoP\x01ZEgithub.com/codefly-dev/core/generated/go/codefly/services/solution/v0\xa2\x02\x04CSSV\xaa\x02\x1cCodefly.Services.Solution.V0\xca\x02\x1cCodefly\\Services\\Solution\\V0\xe2\x02(Codefly\\Services\\Solution\\V0\\GPBMetadata\xea\x02\x1fCodefly::Services::Solution::V0b\x06proto3" + +var ( + file_codefly_services_solution_v0_solution_proto_rawDescOnce sync.Once + file_codefly_services_solution_v0_solution_proto_rawDescData []byte +) + +func file_codefly_services_solution_v0_solution_proto_rawDescGZIP() []byte { + file_codefly_services_solution_v0_solution_proto_rawDescOnce.Do(func() { + file_codefly_services_solution_v0_solution_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_codefly_services_solution_v0_solution_proto_rawDesc), len(file_codefly_services_solution_v0_solution_proto_rawDesc))) + }) + return file_codefly_services_solution_v0_solution_proto_rawDescData +} + +var file_codefly_services_solution_v0_solution_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_codefly_services_solution_v0_solution_proto_goTypes = []any{ + (*SolutionArtifact)(nil), // 0: codefly.services.solution.v0.SolutionArtifact + (*SolutionContext)(nil), // 1: codefly.services.solution.v0.SolutionContext + (*SolutionCapabilities)(nil), // 2: codefly.services.solution.v0.SolutionCapabilities + (*GetSolutionInformationRequest)(nil), // 3: codefly.services.solution.v0.GetSolutionInformationRequest + (*GetSolutionInformationResponse)(nil), // 4: codefly.services.solution.v0.GetSolutionInformationResponse + (*CreateRequest)(nil), // 5: codefly.services.solution.v0.CreateRequest + (*CreateResponse)(nil), // 6: codefly.services.solution.v0.CreateResponse + (*UpdateRequest)(nil), // 7: codefly.services.solution.v0.UpdateRequest + (*UpdateResponse)(nil), // 8: codefly.services.solution.v0.UpdateResponse + (*PackageRequest)(nil), // 9: codefly.services.solution.v0.PackageRequest + (*PackageResponse)(nil), // 10: codefly.services.solution.v0.PackageResponse + (*RenderRequest)(nil), // 11: codefly.services.solution.v0.RenderRequest + (*RenderResponse)(nil), // 12: codefly.services.solution.v0.RenderResponse + nil, // 13: codefly.services.solution.v0.CreateRequest.ParametersEntry + nil, // 14: codefly.services.solution.v0.UpdateRequest.ParametersEntry + nil, // 15: codefly.services.solution.v0.RenderRequest.ValuesEntry + (*v0.FailureDiagnostic)(nil), // 16: codefly.base.v0.FailureDiagnostic +} +var file_codefly_services_solution_v0_solution_proto_depIdxs = []int32{ + 0, // 0: codefly.services.solution.v0.SolutionContext.artifact:type_name -> codefly.services.solution.v0.SolutionArtifact + 0, // 1: codefly.services.solution.v0.GetSolutionInformationRequest.artifact:type_name -> codefly.services.solution.v0.SolutionArtifact + 0, // 2: codefly.services.solution.v0.GetSolutionInformationResponse.artifact:type_name -> codefly.services.solution.v0.SolutionArtifact + 2, // 3: codefly.services.solution.v0.GetSolutionInformationResponse.capabilities:type_name -> codefly.services.solution.v0.SolutionCapabilities + 16, // 4: codefly.services.solution.v0.GetSolutionInformationResponse.diagnostics:type_name -> codefly.base.v0.FailureDiagnostic + 1, // 5: codefly.services.solution.v0.CreateRequest.context:type_name -> codefly.services.solution.v0.SolutionContext + 13, // 6: codefly.services.solution.v0.CreateRequest.parameters:type_name -> codefly.services.solution.v0.CreateRequest.ParametersEntry + 16, // 7: codefly.services.solution.v0.CreateResponse.diagnostics:type_name -> codefly.base.v0.FailureDiagnostic + 1, // 8: codefly.services.solution.v0.UpdateRequest.context:type_name -> codefly.services.solution.v0.SolutionContext + 14, // 9: codefly.services.solution.v0.UpdateRequest.parameters:type_name -> codefly.services.solution.v0.UpdateRequest.ParametersEntry + 16, // 10: codefly.services.solution.v0.UpdateResponse.diagnostics:type_name -> codefly.base.v0.FailureDiagnostic + 1, // 11: codefly.services.solution.v0.PackageRequest.context:type_name -> codefly.services.solution.v0.SolutionContext + 16, // 12: codefly.services.solution.v0.PackageResponse.diagnostics:type_name -> codefly.base.v0.FailureDiagnostic + 1, // 13: codefly.services.solution.v0.RenderRequest.context:type_name -> codefly.services.solution.v0.SolutionContext + 15, // 14: codefly.services.solution.v0.RenderRequest.values:type_name -> codefly.services.solution.v0.RenderRequest.ValuesEntry + 16, // 15: codefly.services.solution.v0.RenderResponse.diagnostics:type_name -> codefly.base.v0.FailureDiagnostic + 3, // 16: codefly.services.solution.v0.Solution.GetSolutionInformation:input_type -> codefly.services.solution.v0.GetSolutionInformationRequest + 5, // 17: codefly.services.solution.v0.Solution.Create:input_type -> codefly.services.solution.v0.CreateRequest + 7, // 18: codefly.services.solution.v0.Solution.Update:input_type -> codefly.services.solution.v0.UpdateRequest + 9, // 19: codefly.services.solution.v0.Solution.Package:input_type -> codefly.services.solution.v0.PackageRequest + 11, // 20: codefly.services.solution.v0.Solution.Render:input_type -> codefly.services.solution.v0.RenderRequest + 4, // 21: codefly.services.solution.v0.Solution.GetSolutionInformation:output_type -> codefly.services.solution.v0.GetSolutionInformationResponse + 6, // 22: codefly.services.solution.v0.Solution.Create:output_type -> codefly.services.solution.v0.CreateResponse + 8, // 23: codefly.services.solution.v0.Solution.Update:output_type -> codefly.services.solution.v0.UpdateResponse + 10, // 24: codefly.services.solution.v0.Solution.Package:output_type -> codefly.services.solution.v0.PackageResponse + 12, // 25: codefly.services.solution.v0.Solution.Render:output_type -> codefly.services.solution.v0.RenderResponse + 21, // [21:26] is the sub-list for method output_type + 16, // [16:21] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name +} + +func init() { file_codefly_services_solution_v0_solution_proto_init() } +func file_codefly_services_solution_v0_solution_proto_init() { + if File_codefly_services_solution_v0_solution_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_codefly_services_solution_v0_solution_proto_rawDesc), len(file_codefly_services_solution_v0_solution_proto_rawDesc)), + NumEnums: 0, + NumMessages: 16, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_codefly_services_solution_v0_solution_proto_goTypes, + DependencyIndexes: file_codefly_services_solution_v0_solution_proto_depIdxs, + MessageInfos: file_codefly_services_solution_v0_solution_proto_msgTypes, + }.Build() + File_codefly_services_solution_v0_solution_proto = out.File + file_codefly_services_solution_v0_solution_proto_goTypes = nil + file_codefly_services_solution_v0_solution_proto_depIdxs = nil +} diff --git a/generated/go/codefly/services/solution/v0/solution_grpc.pb.go b/generated/go/codefly/services/solution/v0/solution_grpc.pb.go new file mode 100644 index 00000000..6fe79396 --- /dev/null +++ b/generated/go/codefly/services/solution/v0/solution_grpc.pb.go @@ -0,0 +1,318 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc (unknown) +// source: codefly/services/solution/v0/solution.proto + +package v0 + +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 ( + Solution_GetSolutionInformation_FullMethodName = "/codefly.services.solution.v0.Solution/GetSolutionInformation" + Solution_Create_FullMethodName = "/codefly.services.solution.v0.Solution/Create" + Solution_Update_FullMethodName = "/codefly.services.solution.v0.Solution/Update" + Solution_Package_FullMethodName = "/codefly.services.solution.v0.Solution/Package" + Solution_Render_FullMethodName = "/codefly.services.solution.v0.Solution/Render" +) + +// SolutionClient is the client API for Solution 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. +// +// Solution is the host-to-solution executor protocol. A solution agent +// scaffolds a solution, packages it as an OCI artifact, and renders its +// manifests into a gitops repository. The solution spec it operates on stays +// codefly-agnostic; only the executor is a codefly plugin. +// +// The per-RPC effect notes below describe each method's maximum side effect. +// They are documentation only in this version: unlike provider.proto's +// host-enforced provider_method_policy, nothing yet enforces these ceilings. +// A host-enforceable method policy is tracked in codefly-dev/core#289, and the +// agents.Serve() wiring that lets a plugin expose this contract is tracked in +// codefly-dev/core#290. +type SolutionClient interface { + // GetSolutionInformation returns the concrete artifact identity and the + // lifecycle operations this executor implements. Effect: read-only, no writes. + GetSolutionInformation(ctx context.Context, in *GetSolutionInformationRequest, opts ...grpc.CallOption) (*GetSolutionInformationResponse, error) + // Create scaffolds a new solution into a destination directory. + // Effect: writes files under the destination directory. + Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) + // Update reconciles an existing solution source with the executor's template. + // Effect: mutates files under the source directory. + Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*UpdateResponse, error) + // Package builds an OCI artifact from a solution source directory. + // Effect: reads the source directory and pushes an OCI artifact (network write). + Package(ctx context.Context, in *PackageRequest, opts ...grpc.CallOption) (*PackageResponse, error) + // Render renders a packaged solution's manifests into a gitops destination. + // Effect: writes manifests into the gitops destination (filesystem/git mutation). + Render(ctx context.Context, in *RenderRequest, opts ...grpc.CallOption) (*RenderResponse, error) +} + +type solutionClient struct { + cc grpc.ClientConnInterface +} + +func NewSolutionClient(cc grpc.ClientConnInterface) SolutionClient { + return &solutionClient{cc} +} + +func (c *solutionClient) GetSolutionInformation(ctx context.Context, in *GetSolutionInformationRequest, opts ...grpc.CallOption) (*GetSolutionInformationResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetSolutionInformationResponse) + err := c.cc.Invoke(ctx, Solution_GetSolutionInformation_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *solutionClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateResponse) + err := c.cc.Invoke(ctx, Solution_Create_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *solutionClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*UpdateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateResponse) + err := c.cc.Invoke(ctx, Solution_Update_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *solutionClient) Package(ctx context.Context, in *PackageRequest, opts ...grpc.CallOption) (*PackageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(PackageResponse) + err := c.cc.Invoke(ctx, Solution_Package_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *solutionClient) Render(ctx context.Context, in *RenderRequest, opts ...grpc.CallOption) (*RenderResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RenderResponse) + err := c.cc.Invoke(ctx, Solution_Render_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SolutionServer is the server API for Solution service. +// All implementations must embed UnimplementedSolutionServer +// for forward compatibility. +// +// Solution is the host-to-solution executor protocol. A solution agent +// scaffolds a solution, packages it as an OCI artifact, and renders its +// manifests into a gitops repository. The solution spec it operates on stays +// codefly-agnostic; only the executor is a codefly plugin. +// +// The per-RPC effect notes below describe each method's maximum side effect. +// They are documentation only in this version: unlike provider.proto's +// host-enforced provider_method_policy, nothing yet enforces these ceilings. +// A host-enforceable method policy is tracked in codefly-dev/core#289, and the +// agents.Serve() wiring that lets a plugin expose this contract is tracked in +// codefly-dev/core#290. +type SolutionServer interface { + // GetSolutionInformation returns the concrete artifact identity and the + // lifecycle operations this executor implements. Effect: read-only, no writes. + GetSolutionInformation(context.Context, *GetSolutionInformationRequest) (*GetSolutionInformationResponse, error) + // Create scaffolds a new solution into a destination directory. + // Effect: writes files under the destination directory. + Create(context.Context, *CreateRequest) (*CreateResponse, error) + // Update reconciles an existing solution source with the executor's template. + // Effect: mutates files under the source directory. + Update(context.Context, *UpdateRequest) (*UpdateResponse, error) + // Package builds an OCI artifact from a solution source directory. + // Effect: reads the source directory and pushes an OCI artifact (network write). + Package(context.Context, *PackageRequest) (*PackageResponse, error) + // Render renders a packaged solution's manifests into a gitops destination. + // Effect: writes manifests into the gitops destination (filesystem/git mutation). + Render(context.Context, *RenderRequest) (*RenderResponse, error) + mustEmbedUnimplementedSolutionServer() +} + +// UnimplementedSolutionServer 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 UnimplementedSolutionServer struct{} + +func (UnimplementedSolutionServer) GetSolutionInformation(context.Context, *GetSolutionInformationRequest) (*GetSolutionInformationResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetSolutionInformation not implemented") +} +func (UnimplementedSolutionServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Create not implemented") +} +func (UnimplementedSolutionServer) Update(context.Context, *UpdateRequest) (*UpdateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Update not implemented") +} +func (UnimplementedSolutionServer) Package(context.Context, *PackageRequest) (*PackageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Package not implemented") +} +func (UnimplementedSolutionServer) Render(context.Context, *RenderRequest) (*RenderResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Render not implemented") +} +func (UnimplementedSolutionServer) mustEmbedUnimplementedSolutionServer() {} +func (UnimplementedSolutionServer) testEmbeddedByValue() {} + +// UnsafeSolutionServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to SolutionServer will +// result in compilation errors. +type UnsafeSolutionServer interface { + mustEmbedUnimplementedSolutionServer() +} + +func RegisterSolutionServer(s grpc.ServiceRegistrar, srv SolutionServer) { + // If the following call panics, it indicates UnimplementedSolutionServer 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(&Solution_ServiceDesc, srv) +} + +func _Solution_GetSolutionInformation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSolutionInformationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SolutionServer).GetSolutionInformation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Solution_GetSolutionInformation_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SolutionServer).GetSolutionInformation(ctx, req.(*GetSolutionInformationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Solution_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SolutionServer).Create(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Solution_Create_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SolutionServer).Create(ctx, req.(*CreateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Solution_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SolutionServer).Update(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Solution_Update_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SolutionServer).Update(ctx, req.(*UpdateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Solution_Package_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PackageRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SolutionServer).Package(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Solution_Package_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SolutionServer).Package(ctx, req.(*PackageRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Solution_Render_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RenderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SolutionServer).Render(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Solution_Render_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SolutionServer).Render(ctx, req.(*RenderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Solution_ServiceDesc is the grpc.ServiceDesc for Solution service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Solution_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "codefly.services.solution.v0.Solution", + HandlerType: (*SolutionServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetSolutionInformation", + Handler: _Solution_GetSolutionInformation_Handler, + }, + { + MethodName: "Create", + Handler: _Solution_Create_Handler, + }, + { + MethodName: "Update", + Handler: _Solution_Update_Handler, + }, + { + MethodName: "Package", + Handler: _Solution_Package_Handler, + }, + { + MethodName: "Render", + Handler: _Solution_Render_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "codefly/services/solution/v0/solution.proto", +} diff --git a/generated/go/codefly/services/solution/v0/v0connect/solution.connect.go b/generated/go/codefly/services/solution/v0/v0connect/solution.connect.go new file mode 100644 index 00000000..4413155b --- /dev/null +++ b/generated/go/codefly/services/solution/v0/v0connect/solution.connect.go @@ -0,0 +1,242 @@ +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: codefly/services/solution/v0/solution.proto + +package v0connect + +import ( + context "context" + errors "errors" + http "net/http" + strings "strings" + + connect "connectrpc.com/connect" + v0 "github.com/codefly-dev/core/generated/go/codefly/services/solution/v0" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion1_13_0 + +const ( + // SolutionName is the fully-qualified name of the Solution service. + SolutionName = "codefly.services.solution.v0.Solution" +) + +// These constants are the fully-qualified names of the RPCs defined in this package. They're +// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. +// +// Note that these are different from the fully-qualified method names used by +// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to +// reflection-formatted method names, remove the leading slash and convert the remaining slash to a +// period. +const ( + // SolutionGetSolutionInformationProcedure is the fully-qualified name of the Solution's + // GetSolutionInformation RPC. + SolutionGetSolutionInformationProcedure = "/codefly.services.solution.v0.Solution/GetSolutionInformation" + // SolutionCreateProcedure is the fully-qualified name of the Solution's Create RPC. + SolutionCreateProcedure = "/codefly.services.solution.v0.Solution/Create" + // SolutionUpdateProcedure is the fully-qualified name of the Solution's Update RPC. + SolutionUpdateProcedure = "/codefly.services.solution.v0.Solution/Update" + // SolutionPackageProcedure is the fully-qualified name of the Solution's Package RPC. + SolutionPackageProcedure = "/codefly.services.solution.v0.Solution/Package" + // SolutionRenderProcedure is the fully-qualified name of the Solution's Render RPC. + SolutionRenderProcedure = "/codefly.services.solution.v0.Solution/Render" +) + +// SolutionClient is a client for the codefly.services.solution.v0.Solution service. +type SolutionClient interface { + // GetSolutionInformation returns the concrete artifact identity and the + // lifecycle operations this executor implements. Effect: read-only, no writes. + GetSolutionInformation(context.Context, *connect.Request[v0.GetSolutionInformationRequest]) (*connect.Response[v0.GetSolutionInformationResponse], error) + // Create scaffolds a new solution into a destination directory. + // Effect: writes files under the destination directory. + Create(context.Context, *connect.Request[v0.CreateRequest]) (*connect.Response[v0.CreateResponse], error) + // Update reconciles an existing solution source with the executor's template. + // Effect: mutates files under the source directory. + Update(context.Context, *connect.Request[v0.UpdateRequest]) (*connect.Response[v0.UpdateResponse], error) + // Package builds an OCI artifact from a solution source directory. + // Effect: reads the source directory and pushes an OCI artifact (network write). + Package(context.Context, *connect.Request[v0.PackageRequest]) (*connect.Response[v0.PackageResponse], error) + // Render renders a packaged solution's manifests into a gitops destination. + // Effect: writes manifests into the gitops destination (filesystem/git mutation). + Render(context.Context, *connect.Request[v0.RenderRequest]) (*connect.Response[v0.RenderResponse], error) +} + +// NewSolutionClient constructs a client for the codefly.services.solution.v0.Solution service. By +// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, +// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the +// connect.WithGRPC() or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewSolutionClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) SolutionClient { + baseURL = strings.TrimRight(baseURL, "/") + solutionMethods := v0.File_codefly_services_solution_v0_solution_proto.Services().ByName("Solution").Methods() + return &solutionClient{ + getSolutionInformation: connect.NewClient[v0.GetSolutionInformationRequest, v0.GetSolutionInformationResponse]( + httpClient, + baseURL+SolutionGetSolutionInformationProcedure, + connect.WithSchema(solutionMethods.ByName("GetSolutionInformation")), + connect.WithClientOptions(opts...), + ), + create: connect.NewClient[v0.CreateRequest, v0.CreateResponse]( + httpClient, + baseURL+SolutionCreateProcedure, + connect.WithSchema(solutionMethods.ByName("Create")), + connect.WithClientOptions(opts...), + ), + update: connect.NewClient[v0.UpdateRequest, v0.UpdateResponse]( + httpClient, + baseURL+SolutionUpdateProcedure, + connect.WithSchema(solutionMethods.ByName("Update")), + connect.WithClientOptions(opts...), + ), + _package: connect.NewClient[v0.PackageRequest, v0.PackageResponse]( + httpClient, + baseURL+SolutionPackageProcedure, + connect.WithSchema(solutionMethods.ByName("Package")), + connect.WithClientOptions(opts...), + ), + render: connect.NewClient[v0.RenderRequest, v0.RenderResponse]( + httpClient, + baseURL+SolutionRenderProcedure, + connect.WithSchema(solutionMethods.ByName("Render")), + connect.WithClientOptions(opts...), + ), + } +} + +// solutionClient implements SolutionClient. +type solutionClient struct { + getSolutionInformation *connect.Client[v0.GetSolutionInformationRequest, v0.GetSolutionInformationResponse] + create *connect.Client[v0.CreateRequest, v0.CreateResponse] + update *connect.Client[v0.UpdateRequest, v0.UpdateResponse] + _package *connect.Client[v0.PackageRequest, v0.PackageResponse] + render *connect.Client[v0.RenderRequest, v0.RenderResponse] +} + +// GetSolutionInformation calls codefly.services.solution.v0.Solution.GetSolutionInformation. +func (c *solutionClient) GetSolutionInformation(ctx context.Context, req *connect.Request[v0.GetSolutionInformationRequest]) (*connect.Response[v0.GetSolutionInformationResponse], error) { + return c.getSolutionInformation.CallUnary(ctx, req) +} + +// Create calls codefly.services.solution.v0.Solution.Create. +func (c *solutionClient) Create(ctx context.Context, req *connect.Request[v0.CreateRequest]) (*connect.Response[v0.CreateResponse], error) { + return c.create.CallUnary(ctx, req) +} + +// Update calls codefly.services.solution.v0.Solution.Update. +func (c *solutionClient) Update(ctx context.Context, req *connect.Request[v0.UpdateRequest]) (*connect.Response[v0.UpdateResponse], error) { + return c.update.CallUnary(ctx, req) +} + +// Package calls codefly.services.solution.v0.Solution.Package. +func (c *solutionClient) Package(ctx context.Context, req *connect.Request[v0.PackageRequest]) (*connect.Response[v0.PackageResponse], error) { + return c._package.CallUnary(ctx, req) +} + +// Render calls codefly.services.solution.v0.Solution.Render. +func (c *solutionClient) Render(ctx context.Context, req *connect.Request[v0.RenderRequest]) (*connect.Response[v0.RenderResponse], error) { + return c.render.CallUnary(ctx, req) +} + +// SolutionHandler is an implementation of the codefly.services.solution.v0.Solution service. +type SolutionHandler interface { + // GetSolutionInformation returns the concrete artifact identity and the + // lifecycle operations this executor implements. Effect: read-only, no writes. + GetSolutionInformation(context.Context, *connect.Request[v0.GetSolutionInformationRequest]) (*connect.Response[v0.GetSolutionInformationResponse], error) + // Create scaffolds a new solution into a destination directory. + // Effect: writes files under the destination directory. + Create(context.Context, *connect.Request[v0.CreateRequest]) (*connect.Response[v0.CreateResponse], error) + // Update reconciles an existing solution source with the executor's template. + // Effect: mutates files under the source directory. + Update(context.Context, *connect.Request[v0.UpdateRequest]) (*connect.Response[v0.UpdateResponse], error) + // Package builds an OCI artifact from a solution source directory. + // Effect: reads the source directory and pushes an OCI artifact (network write). + Package(context.Context, *connect.Request[v0.PackageRequest]) (*connect.Response[v0.PackageResponse], error) + // Render renders a packaged solution's manifests into a gitops destination. + // Effect: writes manifests into the gitops destination (filesystem/git mutation). + Render(context.Context, *connect.Request[v0.RenderRequest]) (*connect.Response[v0.RenderResponse], error) +} + +// NewSolutionHandler builds an HTTP handler from the service implementation. It returns the path on +// which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewSolutionHandler(svc SolutionHandler, opts ...connect.HandlerOption) (string, http.Handler) { + solutionMethods := v0.File_codefly_services_solution_v0_solution_proto.Services().ByName("Solution").Methods() + solutionGetSolutionInformationHandler := connect.NewUnaryHandler( + SolutionGetSolutionInformationProcedure, + svc.GetSolutionInformation, + connect.WithSchema(solutionMethods.ByName("GetSolutionInformation")), + connect.WithHandlerOptions(opts...), + ) + solutionCreateHandler := connect.NewUnaryHandler( + SolutionCreateProcedure, + svc.Create, + connect.WithSchema(solutionMethods.ByName("Create")), + connect.WithHandlerOptions(opts...), + ) + solutionUpdateHandler := connect.NewUnaryHandler( + SolutionUpdateProcedure, + svc.Update, + connect.WithSchema(solutionMethods.ByName("Update")), + connect.WithHandlerOptions(opts...), + ) + solutionPackageHandler := connect.NewUnaryHandler( + SolutionPackageProcedure, + svc.Package, + connect.WithSchema(solutionMethods.ByName("Package")), + connect.WithHandlerOptions(opts...), + ) + solutionRenderHandler := connect.NewUnaryHandler( + SolutionRenderProcedure, + svc.Render, + connect.WithSchema(solutionMethods.ByName("Render")), + connect.WithHandlerOptions(opts...), + ) + return "/codefly.services.solution.v0.Solution/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case SolutionGetSolutionInformationProcedure: + solutionGetSolutionInformationHandler.ServeHTTP(w, r) + case SolutionCreateProcedure: + solutionCreateHandler.ServeHTTP(w, r) + case SolutionUpdateProcedure: + solutionUpdateHandler.ServeHTTP(w, r) + case SolutionPackageProcedure: + solutionPackageHandler.ServeHTTP(w, r) + case SolutionRenderProcedure: + solutionRenderHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedSolutionHandler returns CodeUnimplemented from all methods. +type UnimplementedSolutionHandler struct{} + +func (UnimplementedSolutionHandler) GetSolutionInformation(context.Context, *connect.Request[v0.GetSolutionInformationRequest]) (*connect.Response[v0.GetSolutionInformationResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("codefly.services.solution.v0.Solution.GetSolutionInformation is not implemented")) +} + +func (UnimplementedSolutionHandler) Create(context.Context, *connect.Request[v0.CreateRequest]) (*connect.Response[v0.CreateResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("codefly.services.solution.v0.Solution.Create is not implemented")) +} + +func (UnimplementedSolutionHandler) Update(context.Context, *connect.Request[v0.UpdateRequest]) (*connect.Response[v0.UpdateResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("codefly.services.solution.v0.Solution.Update is not implemented")) +} + +func (UnimplementedSolutionHandler) Package(context.Context, *connect.Request[v0.PackageRequest]) (*connect.Response[v0.PackageResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("codefly.services.solution.v0.Solution.Package is not implemented")) +} + +func (UnimplementedSolutionHandler) Render(context.Context, *connect.Request[v0.RenderRequest]) (*connect.Response[v0.RenderResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("codefly.services.solution.v0.Solution.Render is not implemented")) +} diff --git a/proto/codefly/base/v0/agent.proto b/proto/codefly/base/v0/agent.proto index 06318647..5601f579 100644 --- a/proto/codefly/base/v0/agent.proto +++ b/proto/codefly/base/v0/agent.proto @@ -3,7 +3,7 @@ package codefly.base.v0; import "buf/validate/validate.proto"; -// Agent identifies the plugin binary that owns a service, job, application, module, or toolbox lifecycle. +// Agent identifies the plugin binary that owns a service, job, application, module, toolbox, provider, or solution lifecycle. message Agent { // Kind enumerates supported kinds. enum Kind { @@ -27,6 +27,11 @@ message Agent { // PROVIDER agents reconcile environment-scoped external provider bindings. // They are typed leaf plugins and never own a service workload lifecycle. PROVIDER = 6; + // SOLUTION agents scaffold a solution, package it as an OCI artifact, and + // render its manifests into a gitops repository. Like providers they are + // dynamically installable and stay out of the application graph; the + // solution spec they operate on stays codefly-agnostic. + SOLUTION = 7; } // kind identifies which resource family this agent can manage. Kind kind = 1; diff --git a/proto/codefly/services/solution/v0/solution.proto b/proto/codefly/services/solution/v0/solution.proto new file mode 100644 index 00000000..5aaa1726 --- /dev/null +++ b/proto/codefly/services/solution/v0/solution.proto @@ -0,0 +1,163 @@ +syntax = "proto3"; + +package codefly.services.solution.v0; + +import "buf/validate/validate.proto"; +import "codefly/base/v0/failure.proto"; + +// Solution is the host-to-solution executor protocol. A solution agent +// scaffolds a solution, packages it as an OCI artifact, and renders its +// manifests into a gitops repository. The solution spec it operates on stays +// codefly-agnostic; only the executor is a codefly plugin. +// +// The per-RPC effect notes below describe each method's maximum side effect. +// They are documentation only in this version: unlike provider.proto's +// host-enforced provider_method_policy, nothing yet enforces these ceilings. +// A host-enforceable method policy is tracked in codefly-dev/core#289, and the +// agents.Serve() wiring that lets a plugin expose this contract is tracked in +// codefly-dev/core#290. +service Solution { + // GetSolutionInformation returns the concrete artifact identity and the + // lifecycle operations this executor implements. Effect: read-only, no writes. + rpc GetSolutionInformation(GetSolutionInformationRequest) returns (GetSolutionInformationResponse); + // Create scaffolds a new solution into a destination directory. + // Effect: writes files under the destination directory. + rpc Create(CreateRequest) returns (CreateResponse); + // Update reconciles an existing solution source with the executor's template. + // Effect: mutates files under the source directory. + rpc Update(UpdateRequest) returns (UpdateResponse); + // Package builds an OCI artifact from a solution source directory. + // Effect: reads the source directory and pushes an OCI artifact (network write). + rpc Package(PackageRequest) returns (PackageResponse); + // Render renders a packaged solution's manifests into a gitops destination. + // Effect: writes manifests into the gitops destination (filesystem/git mutation). + rpc Render(RenderRequest) returns (RenderResponse); +} + +// SolutionArtifact binds one concrete solution executor binary to its package. +message SolutionArtifact { + // publisher is the agent publisher namespace. + string publisher = 1; + // name is the solution agent name. + string name = 2; + // version is the concrete agent version. + string version = 3; + // artifact_digest is the verified package digest. + string artifact_digest = 4; + // manifest_digest is the packaged solution manifest digest. + string manifest_digest = 5; +} + +// SolutionContext carries the environment-scoped identity for one invocation. +message SolutionContext { + // workspace is the stable workspace identity. + string workspace = 1; + // environment is the stable environment identity. + string environment = 2; + // artifact is the concrete verified executor package. + SolutionArtifact artifact = 3; +} + +// SolutionCapabilities advertises which lifecycle operations are implemented. +message SolutionCapabilities { + // supports_create declares Create support. + bool supports_create = 1; + // supports_update declares Update support. + bool supports_update = 2; + // supports_package declares Package support. + bool supports_package = 3; + // supports_render declares Render support. + bool supports_render = 4; +} + +// GetSolutionInformationRequest identifies the binary asking to advertise. +message GetSolutionInformationRequest { + // artifact is the verified executor package identity. + SolutionArtifact artifact = 1; +} + +// GetSolutionInformationResponse returns immutable executor advertisement. +message GetSolutionInformationResponse { + // artifact must exactly match the request identity. + SolutionArtifact artifact = 1; + // capabilities are the implemented lifecycle operations. + SolutionCapabilities capabilities = 2; + // diagnostics are bounded neutral diagnostics. + repeated codefly.base.v0.FailureDiagnostic diagnostics = 3 [(buf.validate.field).repeated.max_items = 100]; +} + +// CreateRequest asks the executor to scaffold a new solution. +message CreateRequest { + // context carries the environment-scoped invocation identity. + SolutionContext context = 1; + // destination is the directory the solution is scaffolded into. + string destination = 2; + // parameters are executor-defined scaffold inputs. + map parameters = 3; +} + +// CreateResponse reports the scaffolded solution. +message CreateResponse { + // created_paths are the paths written relative to the destination. + repeated string created_paths = 1 [(buf.validate.field).repeated.max_items = 10000]; + // diagnostics are bounded neutral diagnostics. + repeated codefly.base.v0.FailureDiagnostic diagnostics = 2 [(buf.validate.field).repeated.max_items = 100]; +} + +// UpdateRequest asks the executor to reconcile an existing solution source. +message UpdateRequest { + // context carries the environment-scoped invocation identity. + SolutionContext context = 1; + // source is the existing solution directory to update. + string source = 2; + // parameters are executor-defined update inputs. + map parameters = 3; +} + +// UpdateResponse reports the updated solution. +message UpdateResponse { + // updated_paths are the paths changed relative to the source. + repeated string updated_paths = 1 [(buf.validate.field).repeated.max_items = 10000]; + // diagnostics are bounded neutral diagnostics. + repeated codefly.base.v0.FailureDiagnostic diagnostics = 2 [(buf.validate.field).repeated.max_items = 100]; +} + +// PackageRequest asks the executor to package a solution as an OCI artifact. +message PackageRequest { + // context carries the environment-scoped invocation identity. + SolutionContext context = 1; + // source is the solution directory to package. + string source = 2; + // reference is the target OCI reference. + string reference = 3; +} + +// PackageResponse reports the packaged OCI artifact. +message PackageResponse { + // reference is the pushed OCI reference. + string reference = 1; + // artifact_digest is the digest of the packaged artifact. + string artifact_digest = 2; + // diagnostics are bounded neutral diagnostics. + repeated codefly.base.v0.FailureDiagnostic diagnostics = 3 [(buf.validate.field).repeated.max_items = 100]; +} + +// RenderRequest asks the executor to render manifests into a gitops destination. +message RenderRequest { + // context carries the environment-scoped invocation identity. + SolutionContext context = 1; + // artifact_reference is the packaged OCI reference to render from. + string artifact_reference = 2; + // destination is the gitops repository path manifests are rendered into. + string destination = 3; + // values are executor-defined render inputs. + map values = 4; +} + +// RenderResponse reports the rendered manifests. +message RenderResponse { + // rendered_paths are the manifest paths written relative to the destination. + repeated string rendered_paths = 1 [(buf.validate.field).repeated.max_items = 10000]; + // diagnostics are bounded neutral diagnostics. + repeated codefly.base.v0.FailureDiagnostic diagnostics = 2 [(buf.validate.field).repeated.max_items = 100]; +} diff --git a/resources/agent.go b/resources/agent.go index 3b3ea887..52c55d40 100644 --- a/resources/agent.go +++ b/resources/agent.go @@ -26,6 +26,7 @@ const ( ModuleAgent AgentKind = "codefly:module" ToolboxAgent AgentKind = "codefly:toolbox" ProviderAgent AgentKind = "codefly:provider" + SolutionAgent AgentKind = "codefly:solution" ) type AgentStore string @@ -191,6 +192,19 @@ var agentKindRegistry = []AgentKindRegistration{ }, Operations: AgentOperationSupport{Build: true, List: true, Install: true, Version: true, Publish: true, CI: true, Load: true}, }, + { + ProtoKind: basev0.Agent_SOLUTION, + Resource: SolutionAgent, + InstallSubdirectory: "solutions", + ExecutablePrefix: "solution", + GitHubRepositoryPrefix: "solution", + GitHubAssetPrefix: "solution", + Resolution: AgentResolutionSupport{ + Local: AgentResolutionVerifiedArtifact, + Nix: AgentResolutionVerifiedArtifact, + }, + Operations: AgentOperationSupport{Build: true, List: true, Install: true, Version: true, Publish: true, CI: true, Load: true}, + }, } var ( @@ -417,6 +431,11 @@ func (p *Agent) IsProvider() bool { return err == nil && registration.Resource == ProviderAgent } +func (p *Agent) IsSolution() bool { + registration, err := AgentKindRegistrationFor(p.Kind) + return err == nil && registration.Resource == SolutionAgent +} + func isRunningInDocker() bool { if _, err := os.Stat("/.dockerenv"); err == nil { return true diff --git a/resources/agent_registry_test.go b/resources/agent_registry_test.go index ebd03714..6491947d 100644 --- a/resources/agent_registry_test.go +++ b/resources/agent_registry_test.go @@ -18,6 +18,7 @@ func TestAgentKindRegistryIsExhaustiveAndFailClosed(t *testing.T) { require.Equal(t, int32(4), int32(basev0.Agent_MODULE)) require.Equal(t, int32(5), int32(basev0.Agent_TOOLBOX)) require.Equal(t, int32(6), int32(basev0.Agent_PROVIDER)) + require.Equal(t, int32(7), int32(basev0.Agent_SOLUTION)) expected := map[basev0.Agent_Kind]struct { resource string subdir string @@ -65,6 +66,13 @@ func TestAgentKindRegistryIsExhaustiveAndFailClosed(t *testing.T) { }, resources.AgentOperationSupport{Build: true, List: true, Install: true, Version: true, Publish: true, CI: true, Load: true}, }, + basev0.Agent_SOLUTION: { + "codefly:solution", "solutions", "solution", + resources.AgentResolutionSupport{ + Local: resources.AgentResolutionVerifiedArtifact, Nix: resources.AgentResolutionVerifiedArtifact, + }, + resources.AgentOperationSupport{Build: true, List: true, Install: true, Version: true, Publish: true, CI: true, Load: true}, + }, } registry := resources.AgentKindRegistry() require.Len(t, registry, len(expected)) @@ -131,6 +139,7 @@ func TestAgentPathsUseRegisteredKindSubdirectories(t *testing.T) { {resources.ModuleAgent, "modules"}, {resources.ToolboxAgent, "toolboxes"}, {resources.ProviderAgent, "providers"}, + {resources.SolutionAgent, "solutions"}, } { agent := &resources.Agent{Kind: tc.kind, Publisher: "codefly.dev", Name: "same", Version: "1.0.0"} got, err := agent.Path(ctx) diff --git a/solution/manifest/manifest.go b/solution/manifest/manifest.go new file mode 100644 index 00000000..8222bfb9 --- /dev/null +++ b/solution/manifest/manifest.go @@ -0,0 +1,327 @@ +// Package manifest defines the solution executor manifest: the schema that +// describes a codefly:solution plugin's identity, the services it scaffolds, +// the APIs and events it exposes and consumes, its UI extensions, its needs, +// its permissions, and the lifecycle operations it implements. The solution +// spec the executor operates on stays codefly-agnostic and is not modeled here. +package manifest + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "regexp" + "sort" + + "github.com/Masterminds/semver/v3" + solutionv0 "github.com/codefly-dev/core/generated/go/codefly/services/solution/v0" + "github.com/codefly-dev/core/policy" + "github.com/codefly-dev/core/resources" + "github.com/codefly-dev/core/standards" + "gopkg.in/yaml.v3" +) + +const ( + FileName = "solution.codefly.yaml" + SchemaVersionV0 = "codefly.solution-manifest/v0" + ProtocolVersionV0 = "codefly.solution/v0" +) + +var idPattern = regexp.MustCompile(`^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$`) + +// Manifest describes a codefly:solution executor. +type Manifest struct { + SchemaVersion string `yaml:"schema_version" json:"schema_version"` + ProtocolVersion string `yaml:"protocol_version" json:"protocol_version"` + Agent resources.Agent `yaml:"agent" json:"agent"` + Services []Service `yaml:"services,omitempty" json:"services,omitempty"` + API API `yaml:"api" json:"api"` + Events Events `yaml:"events" json:"events"` + UI []UIExtension `yaml:"ui,omitempty" json:"ui,omitempty"` + Needs []Need `yaml:"needs,omitempty" json:"needs,omitempty"` + Permissions []Permission `yaml:"permissions,omitempty" json:"permissions,omitempty"` + Lifecycle Lifecycle `yaml:"lifecycle" json:"lifecycle"` +} + +// Service is one service the solution scaffolds. Descriptor-only solutions +// declare none. +type Service struct { + ID string `yaml:"id" json:"id"` + Name string `yaml:"name" json:"name"` +} + +// API declares the APIs a solution exposes and consumes. +type API struct { + Exposes []APIDeclaration `yaml:"exposes,omitempty" json:"exposes,omitempty"` + Consumes []APIDeclaration `yaml:"consumes,omitempty" json:"consumes,omitempty"` +} + +// APIDeclaration is one exposed or consumed API endpoint. +type APIDeclaration struct { + ID string `yaml:"id" json:"id"` + Protocol string `yaml:"protocol" json:"protocol"` +} + +// Events declares the events a solution emits and consumes. +type Events struct { + Emits []EventDeclaration `yaml:"emits,omitempty" json:"emits,omitempty"` + Consumes []EventDeclaration `yaml:"consumes,omitempty" json:"consumes,omitempty"` +} + +// EventDeclaration is one emitted or consumed event. +type EventDeclaration struct { + ID string `yaml:"id" json:"id"` +} + +// UIExtension is one UI extension point contributed by the solution. +type UIExtension struct { + ID string `yaml:"id" json:"id"` + Slot string `yaml:"slot" json:"slot"` +} + +// Need is one requirement the solution declares. +type Need struct { + ID string `yaml:"id" json:"id"` + Kind string `yaml:"kind" json:"kind"` +} + +// Permission is one permission the solution requires. +type Permission struct { + ID string `yaml:"id" json:"id"` + Action string `yaml:"action" json:"action"` + Resource string `yaml:"resource" json:"resource"` + Reason string `yaml:"reason" json:"reason"` + Risk string `yaml:"risk" json:"risk"` +} + +// Lifecycle declares which executor operations the solution implements. +type Lifecycle struct { + Create bool `yaml:"create" json:"create"` + Update bool `yaml:"update" json:"update"` + Package bool `yaml:"package" json:"package"` + Render bool `yaml:"render" json:"render"` +} + +func Load(data []byte) (*Manifest, error) { + decoder := yaml.NewDecoder(bytes.NewReader(data)) + decoder.KnownFields(true) + var manifest Manifest + if err := decoder.Decode(&manifest); err != nil { + return nil, fmt.Errorf("decode solution manifest: %w", err) + } + var extra any + if err := decoder.Decode(&extra); !errors.Is(err, io.EOF) { + if err == nil { + return nil, fmt.Errorf("decode solution manifest: multiple YAML documents are not allowed") + } + return nil, fmt.Errorf("decode solution manifest: %w", err) + } + if err := manifest.Validate(); err != nil { + return nil, err + } + return &manifest, nil +} + +func (m *Manifest) Validate() error { + if m == nil { + return fmt.Errorf("solution manifest is required") + } + if m.SchemaVersion != SchemaVersionV0 { + return fmt.Errorf("unsupported solution manifest schema version %q", m.SchemaVersion) + } + if m.ProtocolVersion != ProtocolVersionV0 { + return fmt.Errorf("unsupported solution protocol version %q", m.ProtocolVersion) + } + if m.Agent.Kind != resources.SolutionAgent { + return fmt.Errorf("agent.kind must be %q", resources.SolutionAgent) + } + if m.Agent.Publisher == "" || m.Agent.Name == "" || m.Agent.Version == "" { + return fmt.Errorf("agent publisher, name, and concrete version are required") + } + if m.Agent.Version == "latest" { + return fmt.Errorf("agent.version must be concrete") + } + if _, err := semver.StrictNewVersion(m.Agent.Version); err != nil { + return fmt.Errorf("agent.version must be semantic: %w", err) + } + + if err := validateIDs("services", len(m.Services), func(i int) string { return m.Services[i].ID }); err != nil { + return err + } + for i := range m.Services { + if m.Services[i].Name == "" { + return fmt.Errorf("services[%d].name is required", i) + } + } + if err := validateAPIDeclarations("api.exposes", m.API.Exposes); err != nil { + return err + } + if err := validateAPIDeclarations("api.consumes", m.API.Consumes); err != nil { + return err + } + if err := validateIDs("events.emits", len(m.Events.Emits), func(i int) string { return m.Events.Emits[i].ID }); err != nil { + return err + } + if err := validateIDs("events.consumes", len(m.Events.Consumes), func(i int) string { return m.Events.Consumes[i].ID }); err != nil { + return err + } + if err := validateIDs("ui", len(m.UI), func(i int) string { return m.UI[i].ID }); err != nil { + return err + } + for i := range m.UI { + if m.UI[i].Slot == "" { + return fmt.Errorf("ui[%d].slot is required", i) + } + } + if err := validateIDs("needs", len(m.Needs), func(i int) string { return m.Needs[i].ID }); err != nil { + return err + } + for i := range m.Needs { + if m.Needs[i].Kind == "" { + return fmt.Errorf("needs[%d].kind is required", i) + } + } + if err := validatePermissions(m.Permissions); err != nil { + return err + } + if !m.Lifecycle.Create && !m.Lifecycle.Update && !m.Lifecycle.Package && !m.Lifecycle.Render { + return fmt.Errorf("lifecycle must declare at least one operation") + } + return nil +} + +func validateAPIDeclarations(name string, declarations []APIDeclaration) error { + seen := make(map[string]struct{}, len(declarations)) + for i, declaration := range declarations { + if !idPattern.MatchString(declaration.ID) { + return fmt.Errorf("%s[%d].id is invalid", name, i) + } + if _, duplicate := seen[declaration.ID]; duplicate { + return fmt.Errorf("%s[%d].id %q is duplicated", name, i, declaration.ID) + } + seen[declaration.ID] = struct{}{} + switch declaration.Protocol { + case standards.GRPC, standards.REST, standards.HTTP, standards.TCP, standards.CONNECT: + default: + return fmt.Errorf("%s[%d].protocol %q is invalid", name, i, declaration.Protocol) + } + } + return nil +} + +func validatePermissions(permissions []Permission) error { + seen := make(map[string]struct{}, len(permissions)) + for i, permission := range permissions { + if !idPattern.MatchString(permission.ID) || !idPattern.MatchString(permission.Action) || permission.Reason == "" { + return fmt.Errorf("permissions[%d] requires a valid id, action, and reason", i) + } + if permission.Resource == "" || permission.Resource == "*" { + return fmt.Errorf("permissions[%d].resource must be a bounded resource identifier", i) + } + if _, duplicate := seen[permission.ID]; duplicate { + return fmt.Errorf("permissions[%d].id %q is duplicated", i, permission.ID) + } + seen[permission.ID] = struct{}{} + switch permission.Risk { + case policy.RiskLevelLow, policy.RiskLevelMedium, policy.RiskLevelHigh, policy.RiskLevelCritical: + default: + return fmt.Errorf("permissions[%d].risk is invalid", i) + } + } + return nil +} + +func validateIDs(name string, count int, id func(int) string) error { + seen := make(map[string]struct{}, count) + for i := 0; i < count; i++ { + value := id(i) + if !idPattern.MatchString(value) { + return fmt.Errorf("%s[%d].id is invalid", name, i) + } + if _, duplicate := seen[value]; duplicate { + return fmt.Errorf("%s[%d].id %q is duplicated", name, i, value) + } + seen[value] = struct{}{} + } + return nil +} + +// CanonicalBytes returns a deterministic JSON encoding of the manifest, +// independent of declaration order, suitable for OCI packaging digests. +func (m *Manifest) CanonicalBytes() ([]byte, error) { + if err := m.Validate(); err != nil { + return nil, err + } + normalized := *m + normalized.Services = append([]Service(nil), m.Services...) + sort.Slice(normalized.Services, func(i, j int) bool { return normalized.Services[i].ID < normalized.Services[j].ID }) + normalized.API.Exposes = sortedAPIDeclarations(m.API.Exposes) + normalized.API.Consumes = sortedAPIDeclarations(m.API.Consumes) + normalized.Events.Emits = sortedEventDeclarations(m.Events.Emits) + normalized.Events.Consumes = sortedEventDeclarations(m.Events.Consumes) + normalized.UI = append([]UIExtension(nil), m.UI...) + sort.Slice(normalized.UI, func(i, j int) bool { return normalized.UI[i].ID < normalized.UI[j].ID }) + normalized.Needs = append([]Need(nil), m.Needs...) + sort.Slice(normalized.Needs, func(i, j int) bool { return normalized.Needs[i].ID < normalized.Needs[j].ID }) + normalized.Permissions = append([]Permission(nil), m.Permissions...) + sort.Slice(normalized.Permissions, func(i, j int) bool { return normalized.Permissions[i].ID < normalized.Permissions[j].ID }) + return json.Marshal(normalized) +} + +func (m *Manifest) Digest() (string, error) { + canonical, err := m.CanonicalBytes() + if err != nil { + return "", err + } + sum := sha256.Sum256(canonical) + return "sha256:" + hex.EncodeToString(sum[:]), nil +} + +// AdmitInformation binds a runtime GetSolutionInformation advertisement to this +// packaged manifest. The advertised manifest digest must equal this manifest's +// digest, and every lifecycle operation the runtime advertises must have been +// declared in the packaged manifest. A runtime may implement a subset of the +// declared operations, but never one the audited manifest did not declare. +func (m *Manifest) AdmitInformation(info *solutionv0.GetSolutionInformationResponse) error { + if info == nil { + return fmt.Errorf("solution information is required") + } + digest, err := m.Digest() + if err != nil { + return err + } + if advertised := info.GetArtifact().GetManifestDigest(); advertised != digest { + return fmt.Errorf("manifest digest mismatch: advertised %q, packaged %q", advertised, digest) + } + capabilities := info.GetCapabilities() + for _, operation := range []struct { + name string + advertised bool + declared bool + }{ + {"create", capabilities.GetSupportsCreate(), m.Lifecycle.Create}, + {"update", capabilities.GetSupportsUpdate(), m.Lifecycle.Update}, + {"package", capabilities.GetSupportsPackage(), m.Lifecycle.Package}, + {"render", capabilities.GetSupportsRender(), m.Lifecycle.Render}, + } { + if operation.advertised && !operation.declared { + return fmt.Errorf("runtime advertises %s but the packaged manifest does not declare it", operation.name) + } + } + return nil +} + +func sortedAPIDeclarations(values []APIDeclaration) []APIDeclaration { + out := append([]APIDeclaration(nil), values...) + sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID }) + return out +} + +func sortedEventDeclarations(values []EventDeclaration) []EventDeclaration { + out := append([]EventDeclaration(nil), values...) + sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID }) + return out +} diff --git a/solution/manifest/manifest_test.go b/solution/manifest/manifest_test.go new file mode 100644 index 00000000..ee08510d --- /dev/null +++ b/solution/manifest/manifest_test.go @@ -0,0 +1,212 @@ +package manifest_test + +import ( + "strings" + "testing" + + solutionv0 "github.com/codefly-dev/core/generated/go/codefly/services/solution/v0" + "github.com/codefly-dev/core/solution/manifest" + "github.com/stretchr/testify/require" +) + +func TestManifestLoadValidateAndCanonicalDigestOrderIndependent(t *testing.T) { + first, err := manifest.Load([]byte(validManifest)) + require.NoError(t, err) + + reordered := strings.Replace(validManifest, + " - id: gateway\n protocol: grpc\n - id: dashboard\n protocol: http", + " - id: dashboard\n protocol: http\n - id: gateway\n protocol: grpc", 1) + require.NotEqual(t, validManifest, reordered) + second, err := manifest.Load([]byte(reordered)) + require.NoError(t, err) + + firstBytes, err := first.CanonicalBytes() + require.NoError(t, err) + secondBytes, err := second.CanonicalBytes() + require.NoError(t, err) + require.Equal(t, firstBytes, secondBytes) + + firstDigest, err := first.Digest() + require.NoError(t, err) + secondDigest, err := second.Digest() + require.NoError(t, err) + require.Equal(t, firstDigest, secondDigest) + require.True(t, strings.HasPrefix(firstDigest, "sha256:")) +} + +func TestManifestRejectsUnknownFieldAndMultipleDocuments(t *testing.T) { + _, err := manifest.Load([]byte(validManifest + "\nunknown_field: true\n")) + require.ErrorContains(t, err, "field unknown_field not found") + + _, err = manifest.Load([]byte(validManifest + "\n---\nschema_version: codefly.solution-manifest/v0\n")) + require.ErrorContains(t, err, "multiple YAML documents") +} + +func TestManifestRejectsBadIdentity(t *testing.T) { + for _, tc := range []struct { + name string + mutate func(string) string + message string + }{ + {"schema version", func(s string) string { + return strings.Replace(s, "schema_version: codefly.solution-manifest/v0", "schema_version: codefly.solution-manifest/v9", 1) + }, "unsupported solution manifest schema version"}, + {"protocol version", func(s string) string { + return strings.Replace(s, "protocol_version: codefly.solution/v0", "protocol_version: codefly.solution/v9", 1) + }, "unsupported solution protocol version"}, + {"agent kind", func(s string) string { + return strings.Replace(s, "kind: codefly:solution", "kind: codefly:provider", 1) + }, "agent.kind must be"}, + {"agent version latest", func(s string) string { + return strings.Replace(s, "version: 1.2.3", "version: latest", 1) + }, "must be concrete"}, + {"agent version non-semver", func(s string) string { + return strings.Replace(s, "version: 1.2.3", "version: v1", 1) + }, "must be semantic"}, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := manifest.Load([]byte(tc.mutate(validManifest))) + require.ErrorContains(t, err, tc.message) + }) + } +} + +func TestManifestRejectsMalformedDeclarations(t *testing.T) { + for _, tc := range []struct { + name string + mutate func(string) string + message string + }{ + {"duplicate exposed api", func(s string) string { + return strings.Replace(s, " - id: dashboard\n protocol: http", " - id: gateway\n protocol: http", 1) + }, "duplicated"}, + {"invalid protocol", func(s string) string { + return strings.Replace(s, " protocol: http", " protocol: smtp", 1) + }, "protocol"}, + {"invalid permission risk", func(s string) string { + return strings.Replace(s, " risk: medium", " risk: catastrophic", 1) + }, "risk is invalid"}, + {"permission missing reason", func(s string) string { + return strings.Replace(s, " reason: Write manifests into the gitops repository.\n", "", 1) + }, "valid id, action, and reason"}, + {"permission empty resource", func(s string) string { + return strings.Replace(s, ` resource: "gitops:fixture"`, ` resource: ""`, 1) + }, "resource must be a bounded resource identifier"}, + {"permission wildcard resource", func(s string) string { + return strings.Replace(s, ` resource: "gitops:fixture"`, ` resource: "*"`, 1) + }, "resource must be a bounded resource identifier"}, + {"service missing name", func(s string) string { + return strings.Replace(s, " - id: api\n name: API\n", " - id: api\n name: \"\"\n", 1) + }, "services[0].name is required"}, + {"ui missing slot", func(s string) string { + return strings.Replace(s, " slot: solution.summary", " slot: \"\"", 1) + }, "ui[0].slot is required"}, + {"need missing kind", func(s string) string { + return strings.Replace(s, " kind: repository", " kind: \"\"", 1) + }, "needs[0].kind is required"}, + {"no lifecycle operation", func(s string) string { + return strings.Replace(s, + " create: true\n update: true\n package: true\n render: true", + " create: false\n update: false\n package: false\n render: false", 1) + }, "at least one operation"}, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := manifest.Load([]byte(tc.mutate(validManifest))) + require.ErrorContains(t, err, tc.message) + }) + } +} + +func TestManifestAcceptsDescriptorOnlySolution(t *testing.T) { + descriptorOnly := strings.Replace(validManifest, + "services:\n - id: api\n name: API\n", + "", 1) + loaded, err := manifest.Load([]byte(descriptorOnly)) + require.NoError(t, err) + require.Empty(t, loaded.Services) +} + +func TestAdmitInformationBindsCapabilitiesToPackagedManifest(t *testing.T) { + packaged, err := manifest.Load([]byte(validManifest)) + require.NoError(t, err) + digest, err := packaged.Digest() + require.NoError(t, err) + + info := func(d string, caps *solutionv0.SolutionCapabilities) *solutionv0.GetSolutionInformationResponse { + return &solutionv0.GetSolutionInformationResponse{ + Artifact: &solutionv0.SolutionArtifact{ManifestDigest: d}, + Capabilities: caps, + } + } + allCaps := &solutionv0.SolutionCapabilities{ + SupportsCreate: true, SupportsUpdate: true, SupportsPackage: true, SupportsRender: true, + } + + require.NoError(t, packaged.AdmitInformation(info(digest, allCaps))) + + // A runtime may implement a strict subset of the declared operations. + require.NoError(t, packaged.AdmitInformation(info(digest, &solutionv0.SolutionCapabilities{SupportsCreate: true}))) + // Advertising nothing is a valid subset. + require.NoError(t, packaged.AdmitInformation(info(digest, nil))) + + // Advertising against a different manifest is rejected. + err = packaged.AdmitInformation(info("sha256:"+strings.Repeat("0", 64), allCaps)) + require.ErrorContains(t, err, "manifest digest mismatch") + + // Advertising a capability the packaged manifest never declared is rejected, + // even though the digest matches. + renderOnly, err := manifest.Load([]byte(strings.Replace(validManifest, + " create: true\n update: true\n package: true\n render: true", + " create: true\n update: false\n package: false\n render: false", 1))) + require.NoError(t, err) + renderOnlyDigest, err := renderOnly.Digest() + require.NoError(t, err) + err = renderOnly.AdmitInformation(info(renderOnlyDigest, allCaps)) + require.ErrorContains(t, err, "runtime advertises update but the packaged manifest does not declare it") + + require.ErrorContains(t, packaged.AdmitInformation(nil), "solution information is required") +} + +const validManifest = ` +schema_version: codefly.solution-manifest/v0 +protocol_version: codefly.solution/v0 +agent: + kind: codefly:solution + publisher: codefly.dev + name: fixture + version: 1.2.3 +services: + - id: api + name: API +api: + exposes: + - id: gateway + protocol: grpc + - id: dashboard + protocol: http + consumes: + - id: billing + protocol: rest +events: + emits: + - id: solution.rendered + consumes: + - id: workspace.provisioned +ui: + - id: overview + slot: solution.summary +needs: + - id: gitops-repo + kind: repository +permissions: + - id: gitops-write + action: gitops.write + resource: "gitops:fixture" + reason: Write manifests into the gitops repository. + risk: medium +lifecycle: + create: true + update: true + package: true + render: true +`