feat: proxy image factory requests done by the providers through Omni

Introduce the new API for creating schematics using raw YAML data.

Proxied API becomes useful when Omni has auth to the image factory, or
if it gets support for several image factory endpoints.

Fixes: https://github.com/siderolabs/omni/issues/2787

Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
This commit is contained in:
Artem Chernyshev 2026-05-06 20:57:10 +03:00
parent e7aee25c72
commit 39ee2f01ea
No known key found for this signature in database
GPG Key ID: 9BAC0E08F5067BB8
11 changed files with 614 additions and 209 deletions

File diff suppressed because it is too large Load Diff

View File

@ -379,6 +379,33 @@ func local_request_ManagementService_CreateSchematic_0(ctx context.Context, mars
return msg, metadata, err
}
func request_ManagementService_CreateSchematicFromRaw_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq CreateSchematicFromRawRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.CreateSchematicFromRaw(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_ManagementService_CreateSchematicFromRaw_0(ctx context.Context, marshaler runtime.Marshaler, server ManagementServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq CreateSchematicFromRawRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.CreateSchematicFromRaw(ctx, &protoReq)
return msg, metadata, err
}
func request_ManagementService_GetSupportBundle_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (ManagementService_GetSupportBundleClient, runtime.ServerMetadata, error) {
var (
protoReq GetSupportBundleRequest
@ -935,6 +962,26 @@ func RegisterManagementServiceHandlerServer(ctx context.Context, mux *runtime.Se
}
forward_ManagementService_CreateSchematic_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_ManagementService_CreateSchematicFromRaw_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/management.ManagementService/CreateSchematicFromRaw", runtime.WithHTTPPathPattern("/management.ManagementService/CreateSchematicFromRaw"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_ManagementService_CreateSchematicFromRaw_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_ManagementService_CreateSchematicFromRaw_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_ManagementService_GetSupportBundle_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport")
@ -1410,6 +1457,23 @@ func RegisterManagementServiceHandlerClient(ctx context.Context, mux *runtime.Se
}
forward_ManagementService_CreateSchematic_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_ManagementService_CreateSchematicFromRaw_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/management.ManagementService/CreateSchematicFromRaw", runtime.WithHTTPPathPattern("/management.ManagementService/CreateSchematicFromRaw"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_ManagementService_CreateSchematicFromRaw_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_ManagementService_CreateSchematicFromRaw_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_ManagementService_GetSupportBundle_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@ -1631,6 +1695,7 @@ var (
pattern_ManagementService_KubernetesUpgradePreChecks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"management.ManagementService", "KubernetesUpgradePreChecks"}, ""))
pattern_ManagementService_KubernetesSyncManifests_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"management.ManagementService", "KubernetesSyncManifests"}, ""))
pattern_ManagementService_CreateSchematic_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"management.ManagementService", "CreateSchematic"}, ""))
pattern_ManagementService_CreateSchematicFromRaw_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"management.ManagementService", "CreateSchematicFromRaw"}, ""))
pattern_ManagementService_GetSupportBundle_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"management.ManagementService", "GetSupportBundle"}, ""))
pattern_ManagementService_ReadAuditLog_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"management.ManagementService", "ReadAuditLog"}, ""))
pattern_ManagementService_MaintenanceUpgrade_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"management.ManagementService", "MaintenanceUpgrade"}, ""))
@ -1659,6 +1724,7 @@ var (
forward_ManagementService_KubernetesUpgradePreChecks_0 = runtime.ForwardResponseMessage
forward_ManagementService_KubernetesSyncManifests_0 = runtime.ForwardResponseStream
forward_ManagementService_CreateSchematic_0 = runtime.ForwardResponseMessage
forward_ManagementService_CreateSchematicFromRaw_0 = runtime.ForwardResponseMessage
forward_ManagementService_GetSupportBundle_0 = runtime.ForwardResponseStream
forward_ManagementService_ReadAuditLog_0 = runtime.ForwardResponseStream
forward_ManagementService_MaintenanceUpgrade_0 = runtime.ForwardResponseMessage

View File

@ -175,6 +175,10 @@ message CreateSchematicRequest {
SchematicBootloader bootloader = 10;
}
message CreateSchematicFromRawRequest {
bytes raw_schematic = 1;
}
message CreateSchematicResponse {
string schematic_id = 1;
string pxe_url = 2;
@ -356,6 +360,7 @@ service ManagementService {
rpc KubernetesUpgradePreChecks(KubernetesUpgradePreChecksRequest) returns (KubernetesUpgradePreChecksResponse);
rpc KubernetesSyncManifests(KubernetesSyncManifestRequest) returns (stream KubernetesSyncManifestResponse);
rpc CreateSchematic(CreateSchematicRequest) returns (CreateSchematicResponse);
rpc CreateSchematicFromRaw(CreateSchematicFromRawRequest) returns (CreateSchematicResponse);
rpc GetSupportBundle(GetSupportBundleRequest) returns (stream GetSupportBundleResponse);
rpc ReadAuditLog(ReadAuditLogRequest) returns (stream ReadAuditLogResponse);
rpc MaintenanceUpgrade(MaintenanceUpgradeRequest) returns (MaintenanceUpgradeResponse);

View File

@ -35,6 +35,7 @@ const (
ManagementService_KubernetesUpgradePreChecks_FullMethodName = "/management.ManagementService/KubernetesUpgradePreChecks"
ManagementService_KubernetesSyncManifests_FullMethodName = "/management.ManagementService/KubernetesSyncManifests"
ManagementService_CreateSchematic_FullMethodName = "/management.ManagementService/CreateSchematic"
ManagementService_CreateSchematicFromRaw_FullMethodName = "/management.ManagementService/CreateSchematicFromRaw"
ManagementService_GetSupportBundle_FullMethodName = "/management.ManagementService/GetSupportBundle"
ManagementService_ReadAuditLog_FullMethodName = "/management.ManagementService/ReadAuditLog"
ManagementService_MaintenanceUpgrade_FullMethodName = "/management.ManagementService/MaintenanceUpgrade"
@ -66,6 +67,7 @@ type ManagementServiceClient interface {
KubernetesUpgradePreChecks(ctx context.Context, in *KubernetesUpgradePreChecksRequest, opts ...grpc.CallOption) (*KubernetesUpgradePreChecksResponse, error)
KubernetesSyncManifests(ctx context.Context, in *KubernetesSyncManifestRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[KubernetesSyncManifestResponse], error)
CreateSchematic(ctx context.Context, in *CreateSchematicRequest, opts ...grpc.CallOption) (*CreateSchematicResponse, error)
CreateSchematicFromRaw(ctx context.Context, in *CreateSchematicFromRawRequest, opts ...grpc.CallOption) (*CreateSchematicResponse, error)
GetSupportBundle(ctx context.Context, in *GetSupportBundleRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GetSupportBundleResponse], error)
ReadAuditLog(ctx context.Context, in *ReadAuditLogRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ReadAuditLogResponse], error)
MaintenanceUpgrade(ctx context.Context, in *MaintenanceUpgradeRequest, opts ...grpc.CallOption) (*MaintenanceUpgradeResponse, error)
@ -236,6 +238,16 @@ func (c *managementServiceClient) CreateSchematic(ctx context.Context, in *Creat
return out, nil
}
func (c *managementServiceClient) CreateSchematicFromRaw(ctx context.Context, in *CreateSchematicFromRawRequest, opts ...grpc.CallOption) (*CreateSchematicResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateSchematicResponse)
err := c.cc.Invoke(ctx, ManagementService_CreateSchematicFromRaw_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *managementServiceClient) GetSupportBundle(ctx context.Context, in *GetSupportBundleRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GetSupportBundleResponse], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &ManagementService_ServiceDesc.Streams[2], ManagementService_GetSupportBundle_FullMethodName, cOpts...)
@ -391,6 +403,7 @@ type ManagementServiceServer interface {
KubernetesUpgradePreChecks(context.Context, *KubernetesUpgradePreChecksRequest) (*KubernetesUpgradePreChecksResponse, error)
KubernetesSyncManifests(*KubernetesSyncManifestRequest, grpc.ServerStreamingServer[KubernetesSyncManifestResponse]) error
CreateSchematic(context.Context, *CreateSchematicRequest) (*CreateSchematicResponse, error)
CreateSchematicFromRaw(context.Context, *CreateSchematicFromRawRequest) (*CreateSchematicResponse, error)
GetSupportBundle(*GetSupportBundleRequest, grpc.ServerStreamingServer[GetSupportBundleResponse]) error
ReadAuditLog(*ReadAuditLogRequest, grpc.ServerStreamingServer[ReadAuditLogResponse]) error
MaintenanceUpgrade(context.Context, *MaintenanceUpgradeRequest) (*MaintenanceUpgradeResponse, error)
@ -452,6 +465,9 @@ func (UnimplementedManagementServiceServer) KubernetesSyncManifests(*KubernetesS
func (UnimplementedManagementServiceServer) CreateSchematic(context.Context, *CreateSchematicRequest) (*CreateSchematicResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CreateSchematic not implemented")
}
func (UnimplementedManagementServiceServer) CreateSchematicFromRaw(context.Context, *CreateSchematicFromRawRequest) (*CreateSchematicResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CreateSchematicFromRaw not implemented")
}
func (UnimplementedManagementServiceServer) GetSupportBundle(*GetSupportBundleRequest, grpc.ServerStreamingServer[GetSupportBundleResponse]) error {
return status.Error(codes.Unimplemented, "method GetSupportBundle not implemented")
}
@ -729,6 +745,24 @@ func _ManagementService_CreateSchematic_Handler(srv interface{}, ctx context.Con
return interceptor(ctx, in, info, handler)
}
func _ManagementService_CreateSchematicFromRaw_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateSchematicFromRawRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ManagementServiceServer).CreateSchematicFromRaw(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ManagementService_CreateSchematicFromRaw_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ManagementServiceServer).CreateSchematicFromRaw(ctx, req.(*CreateSchematicFromRawRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ManagementService_GetSupportBundle_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(GetSupportBundleRequest)
if err := stream.RecvMsg(m); err != nil {
@ -982,6 +1016,10 @@ var ManagementService_ServiceDesc = grpc.ServiceDesc{
MethodName: "CreateSchematic",
Handler: _ManagementService_CreateSchematic_Handler,
},
{
MethodName: "CreateSchematicFromRaw",
Handler: _ManagementService_CreateSchematicFromRaw_Handler,
},
{
MethodName: "MaintenanceUpgrade",
Handler: _ManagementService_MaintenanceUpgrade_Handler,

View File

@ -485,6 +485,27 @@ func (m *CreateSchematicRequest) CloneMessageVT() proto.Message {
return m.CloneVT()
}
func (m *CreateSchematicFromRawRequest) CloneVT() *CreateSchematicFromRawRequest {
if m == nil {
return (*CreateSchematicFromRawRequest)(nil)
}
r := new(CreateSchematicFromRawRequest)
if rhs := m.RawSchematic; rhs != nil {
tmpBytes := make([]byte, len(rhs))
copy(tmpBytes, rhs)
r.RawSchematic = tmpBytes
}
if len(m.unknownFields) > 0 {
r.unknownFields = make([]byte, len(m.unknownFields))
copy(r.unknownFields, m.unknownFields)
}
return r
}
func (m *CreateSchematicFromRawRequest) CloneMessageVT() proto.Message {
return m.CloneVT()
}
func (m *CreateSchematicResponse) CloneVT() *CreateSchematicResponse {
if m == nil {
return (*CreateSchematicResponse)(nil)
@ -1620,6 +1641,25 @@ func (this *CreateSchematicRequest) EqualMessageVT(thatMsg proto.Message) bool {
}
return this.EqualVT(that)
}
func (this *CreateSchematicFromRawRequest) EqualVT(that *CreateSchematicFromRawRequest) bool {
if this == that {
return true
} else if this == nil || that == nil {
return false
}
if string(this.RawSchematic) != string(that.RawSchematic) {
return false
}
return string(this.unknownFields) == string(that.unknownFields)
}
func (this *CreateSchematicFromRawRequest) EqualMessageVT(thatMsg proto.Message) bool {
that, ok := thatMsg.(*CreateSchematicFromRawRequest)
if !ok {
return false
}
return this.EqualVT(that)
}
func (this *CreateSchematicResponse) EqualVT(that *CreateSchematicResponse) bool {
if this == that {
return true
@ -3533,6 +3573,46 @@ func (m *CreateSchematicRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error
return len(dAtA) - i, nil
}
func (m *CreateSchematicFromRawRequest) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *CreateSchematicFromRawRequest) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *CreateSchematicFromRawRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.RawSchematic) > 0 {
i -= len(m.RawSchematic)
copy(dAtA[i:], m.RawSchematic)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.RawSchematic)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *CreateSchematicResponse) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
@ -5341,6 +5421,20 @@ func (m *CreateSchematicRequest) SizeVT() (n int) {
return n
}
func (m *CreateSchematicFromRawRequest) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.RawSchematic)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
n += len(m.unknownFields)
return n
}
func (m *CreateSchematicResponse) SizeVT() (n int) {
if m == nil {
return 0
@ -8884,6 +8978,91 @@ func (m *CreateSchematicRequest) UnmarshalVT(dAtA []byte) error {
}
return nil
}
func (m *CreateSchematicFromRawRequest) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: CreateSchematicFromRawRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: CreateSchematicFromRawRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RawSchematic", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.RawSchematic = append(m.RawSchematic[:0], dAtA[iNdEx:postIndex]...)
if m.RawSchematic == nil {
m.RawSchematic = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return protohelpers.ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *CreateSchematicResponse) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0

View File

@ -154,6 +154,15 @@ func (client *Client) CreateSchematic(ctx context.Context, req *management.Creat
return schematic, nil
}
// CreateSchematicFromRaw creates a schematic from the raw schematic string using the image factory.
func (client *Client) CreateSchematicFromRaw(ctx context.Context, schematic []byte) (*management.CreateSchematicResponse, error) {
req := &management.CreateSchematicFromRawRequest{
RawSchematic: schematic,
}
return client.conn.CreateSchematicFromRaw(ctx, req)
}
// CreateServiceAccount creates a service account and returns the public key ID.
func (client *Client) CreateServiceAccount(ctx context.Context, name, armoredPGPPublicKey, role string, useUserRole bool) (string, error) {
resp, err := client.conn.CreateServiceAccount(ctx, &management.CreateServiceAccountRequest{

View File

@ -256,7 +256,7 @@ func BuildTalosClient(ctx context.Context, config, context, sideroV1KeysDir stri
}
// BuildImageFactoryClient builds an Image Factory client using the Image Factory base url from Omni configuration.
func BuildImageFactoryClient(ctx context.Context, omniState state.State) (*imagefactory.Client, error) {
func BuildImageFactoryClient(ctx context.Context, omniState state.State) (*imagefactory.DirectClient, error) {
featuresConfig, err := safe.ReaderGetByID[*omni.FeaturesConfig](ctx, omniState, omni.FeaturesConfigID)
if err != nil {
return nil, fmt.Errorf("error reading features config %q: %w", omniState, err)
@ -266,7 +266,7 @@ func BuildImageFactoryClient(ctx context.Context, omniState state.State) (*image
return nil, fmt.Errorf("image factory base URL is empty")
}
c, err := imagefactory.NewClient(imagefactory.ClientOptions{FactoryEndpoint: featuresConfig.TypedSpec().Value.ImageFactoryBaseUrl})
c, err := imagefactory.NewDirectClient(imagefactory.ClientOptions{FactoryEndpoint: featuresConfig.TypedSpec().Value.ImageFactoryBaseUrl})
if err != nil {
return nil, fmt.Errorf("failed to set up image factory client: %w", err)
}

View File

@ -12,11 +12,12 @@ import (
"github.com/siderolabs/image-factory/pkg/client"
"github.com/siderolabs/image-factory/pkg/schematic"
omniclient "github.com/siderolabs/omni/client/pkg/client"
"github.com/siderolabs/omni/client/pkg/constants"
)
// Client goes directly to the image factory using the provided endpoint (or default one).
type Client struct {
// DirectClient goes directly to the image factory using the provided endpoint (or default one).
type DirectClient struct {
client *client.Client
}
@ -26,7 +27,7 @@ type ClientOptions struct {
}
// EnsureSchematic creates the schematic in the image factory.
func (se Client) EnsureSchematic(ctx context.Context, schematic schematic.Schematic) (string, error) {
func (se DirectClient) EnsureSchematic(ctx context.Context, schematic schematic.Schematic) (string, error) {
schematicID, err := schematic.ID()
if err != nil {
return "", fmt.Errorf("failed to generate schematic ID: %w", err)
@ -42,8 +43,8 @@ func (se Client) EnsureSchematic(ctx context.Context, schematic schematic.Schema
return schematicID, nil
}
// NewClient creates new Omni based image factory client.
func NewClient(options ClientOptions) (*Client, error) {
// NewDirectClient creates new Omni based image factory client.
func NewDirectClient(options ClientOptions) (*DirectClient, error) {
if options.FactoryEndpoint == "" {
options.FactoryEndpoint = constants.ImageFactoryBaseURL
}
@ -53,7 +54,34 @@ func NewClient(options ClientOptions) (*Client, error) {
return nil, err
}
return &Client{
return &DirectClient{
client: client,
}, nil
}
// ProxiedClient is the image factory client which proxies the requests through Omni API.
type ProxiedClient struct {
client *omniclient.Client
}
// NewProxiedClient creates new proxied image factory client.
func NewProxiedClient(client *omniclient.Client) (*ProxiedClient, error) {
return &ProxiedClient{
client: client,
}, nil
}
// EnsureSchematic creates the schematic in the image factory through Omni API.
func (pc *ProxiedClient) EnsureSchematic(ctx context.Context, schematic schematic.Schematic) (string, error) {
data, err := schematic.Marshal()
if err != nil {
return "", fmt.Errorf("failed to marshal schematic: %w", err)
}
resp, err := pc.client.Management().CreateSchematicFromRaw(ctx, data)
if err != nil {
return "", fmt.Errorf("failed to create schematic through Omni API: %w", err)
}
return resp.GetSchematicId(), nil
}

View File

@ -27,7 +27,6 @@ import (
"github.com/siderolabs/omni/client/pkg/infra/internal/resources"
"github.com/siderolabs/omni/client/pkg/infra/provision"
"github.com/siderolabs/omni/client/pkg/omni/resources/infra"
omnires "github.com/siderolabs/omni/client/pkg/omni/resources/omni"
)
// ProviderConfig defines the schema, human-readable provider name and description.
@ -105,36 +104,36 @@ func (provider *Provider[T]) Run(ctx context.Context, logger *zap.Logger, opts .
omni.WithProviderID(provider.id),
))
var (
c *client.Client
err error
)
switch {
case options.state != nil:
st = options.state
case options.omniEndpoint != "":
client, err := client.New(options.omniEndpoint, options.clientOptions...)
c, err = client.New(options.omniEndpoint, options.clientOptions...)
if err != nil {
return err
}
state, err := NewState(client)
var state *State
state, err = NewState(c)
if err != nil {
return err
}
defer client.Close() //nolint:errcheck
defer c.Close() //nolint:errcheck
st = state.State()
default:
return fmt.Errorf("invalid infra provider configuration: either WithOmniEndpoint or WithState option should be used")
}
features, err := safe.ReaderGetByID[*omnires.FeaturesConfig](ctx, st, omnires.FeaturesConfigID)
if err != nil {
return err
}
if options.imageFactory == nil {
options.imageFactory, err = imagefactory.NewClient(imagefactory.ClientOptions{
FactoryEndpoint: features.TypedSpec().Value.ImageFactoryBaseUrl,
})
options.imageFactory, err = imagefactory.NewProxiedClient(c)
if err != nil {
return err
}

View File

@ -191,6 +191,10 @@ export type CreateSchematicRequest = {
bootloader?: SchematicBootloader
}
export type CreateSchematicFromRawRequest = {
raw_schematic?: Uint8Array
}
export type CreateSchematicResponse = {
schematic_id?: string
pxe_url?: string
@ -370,6 +374,9 @@ export class ManagementService {
static CreateSchematic(req: CreateSchematicRequest, ...options: fm.fetchOption[]): Promise<CreateSchematicResponse> {
return fm.fetchReq<CreateSchematicRequest, CreateSchematicResponse>("POST", `/management.ManagementService/CreateSchematic`, req, ...options)
}
static CreateSchematicFromRaw(req: CreateSchematicFromRawRequest, ...options: fm.fetchOption[]): Promise<CreateSchematicResponse> {
return fm.fetchReq<CreateSchematicFromRawRequest, CreateSchematicResponse>("POST", `/management.ManagementService/CreateSchematicFromRaw`, req, ...options)
}
static GetSupportBundle(req: GetSupportBundleRequest, entityNotifier: fm.NotifyStreamEntityArrival<GetSupportBundleResponse>, ...options: fm.fetchOption[]): Promise<void> {
return fm.fetchStreamingRequest<GetSupportBundleRequest, GetSupportBundleResponse>("POST", `/management.ManagementService/GetSupportBundle`, req, entityNotifier, ...options)
}

View File

@ -124,6 +124,30 @@ func (s *managementServer) CreateSchematic(ctx context.Context, request *managem
}, nil
}
// CreateSchematicFromRaw implements managementServer.
func (s *managementServer) CreateSchematicFromRaw(ctx context.Context, request *management.CreateSchematicFromRawRequest) (*management.CreateSchematicResponse, error) {
if _, err := auth.CheckGRPC(ctx, auth.WithValidSignature(true)); err != nil {
return nil, err
}
var schematicRequest schematic.Schematic
if err := yaml.Unmarshal(request.RawSchematic, &schematicRequest); err != nil {
return nil, fmt.Errorf("failed to unmarshal raw schematic: %w", err)
}
s.logger.Info("ensure schematic from raw", zap.Reflect("schematic", schematicRequest))
schematicInfo, err := s.imageFactoryClient.EnsureSchematic(ctx, schematicRequest)
if err != nil {
return nil, fmt.Errorf("failed to ensure schematic: %w", err)
}
return &management.CreateSchematicResponse{
SchematicId: schematicInfo.FullID,
}, nil
}
func (s *managementServer) getOverlay(ctx context.Context, req *management.CreateSchematicRequest) (schematic.Overlay, error) {
if !quirks.New(req.TalosVersion).SupportsOverlay() {
return schematic.Overlay{}, nil