From 8718cd9d4a2d19ba477d493fced62f0a482ac341 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Tue, 5 May 2026 16:25:02 +0200 Subject: [PATCH] feat(cas): add managed flag Signed-off-by: Sylwester Piskozub --- .../controlplane/v1/response_messages.pb.go | 17 ++++- .../controlplane/v1/response_messages.proto | 2 + .../controlplane/v1/response_messages.ts | 16 +++++ ...rolplane.v1.CASBackendItem.jsonschema.json | 8 +++ ...controlplane.v1.CASBackendItem.schema.json | 8 +++ .../internal/service/casbackend.go | 1 + app/controlplane/pkg/biz/casbackend.go | 13 ++++ app/controlplane/pkg/data/casbackend.go | 2 + app/controlplane/pkg/data/ent/casbackend.go | 13 +++- .../pkg/data/ent/casbackend/casbackend.go | 10 +++ .../pkg/data/ent/casbackend/where.go | 15 +++++ .../pkg/data/ent/casbackend_create.go | 65 +++++++++++++++++++ .../pkg/data/ent/casbackend_update.go | 34 ++++++++++ .../ent/migrate/migrations/20260504100323.sql | 2 + .../pkg/data/ent/migrate/migrations/atlas.sum | 3 +- .../pkg/data/ent/migrate/schema.go | 5 +- app/controlplane/pkg/data/ent/mutation.go | 56 +++++++++++++++- app/controlplane/pkg/data/ent/runtime.go | 4 ++ .../pkg/data/ent/schema/casbackend.go | 2 + 19 files changed, 268 insertions(+), 8 deletions(-) create mode 100644 app/controlplane/pkg/data/ent/migrate/migrations/20260504100323.sql diff --git a/app/controlplane/api/controlplane/v1/response_messages.pb.go b/app/controlplane/api/controlplane/v1/response_messages.pb.go index c81b0c385..5224123a2 100644 --- a/app/controlplane/api/controlplane/v1/response_messages.pb.go +++ b/app/controlplane/api/controlplane/v1/response_messages.pb.go @@ -2336,7 +2336,9 @@ type CASBackendItem struct { ValidationError *string `protobuf:"bytes,12,opt,name=validation_error,json=validationError,proto3,oneof" json:"validation_error,omitempty"` UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` // Wether it's the fallback backend in the organization - Fallback bool `protobuf:"varint,14,opt,name=fallback,proto3" json:"fallback,omitempty"` + Fallback bool `protobuf:"varint,14,opt,name=fallback,proto3" json:"fallback,omitempty"` + // Whether this backend is provisioned and operated by Chainloop using + IsManaged bool `protobuf:"varint,15,opt,name=is_managed,json=isManaged,proto3" json:"is_managed,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2469,6 +2471,13 @@ func (x *CASBackendItem) GetFallback() bool { return false } +func (x *CASBackendItem) GetIsManaged() bool { + if x != nil { + return x.IsManaged + } + return false +} + type APITokenItem struct { state protoimpl.MessageState `protogen:"open.v1"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` @@ -3199,7 +3208,7 @@ const file_controlplane_v1_response_messages_proto_rawDesc = "" + ".POLICY_VIOLATION_BLOCKING_STRATEGY_UNSPECIFIED\x10\x00\x12,\n" + "(POLICY_VIOLATION_BLOCKING_STRATEGY_BLOCK\x10\x01\x12/\n" + "+POLICY_VIOLATION_BLOCKING_STRATEGY_ADVISORY\x10\x02B\x1e\n" + - "\x1c_api_token_max_days_inactive\"\x91\x06\n" + + "\x1c_api_token_max_days_inactive\"\xb0\x06\n" + "\x0eCASBackendItem\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + "\x04name\x18\v \x01(\tR\x04name\x12\x1a\n" + @@ -3217,7 +3226,9 @@ const file_controlplane_v1_response_messages_proto_rawDesc = "" + "\x10validation_error\x18\f \x01(\tH\x00R\x0fvalidationError\x88\x01\x01\x129\n" + "\n" + "updated_at\x18\r \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x12\x1a\n" + - "\bfallback\x18\x0e \x01(\bR\bfallback\x1a%\n" + + "\bfallback\x18\x0e \x01(\bR\bfallback\x12\x1d\n" + + "\n" + + "is_managed\x18\x0f \x01(\bR\tisManaged\x1a%\n" + "\x06Limits\x12\x1b\n" + "\tmax_bytes\x18\x01 \x01(\x03R\bmaxBytes\"n\n" + "\x10ValidationStatus\x12!\n" + diff --git a/app/controlplane/api/controlplane/v1/response_messages.proto b/app/controlplane/api/controlplane/v1/response_messages.proto index 2bafd0c24..13ee58e3a 100644 --- a/app/controlplane/api/controlplane/v1/response_messages.proto +++ b/app/controlplane/api/controlplane/v1/response_messages.proto @@ -407,6 +407,8 @@ message CASBackendItem { google.protobuf.Timestamp updated_at = 13; // Wether it's the fallback backend in the organization bool fallback = 14; + // Whether this backend is provisioned and operated by Chainloop using + bool is_managed = 15; message Limits { // Max number of bytes allowed to be stored in this backend diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts b/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts index 0b9fb7a0d..42d2646fb 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts @@ -941,6 +941,8 @@ export interface CASBackendItem { updatedAt?: Date; /** Wether it's the fallback backend in the organization */ fallback: boolean; + /** Whether this backend is provisioned and operated by Chainloop using */ + isManaged: boolean; } export enum CASBackendItem_ValidationStatus { @@ -4492,6 +4494,7 @@ function createBaseCASBackendItem(): CASBackendItem { validationError: undefined, updatedAt: undefined, fallback: false, + isManaged: false, }; } @@ -4539,6 +4542,9 @@ export const CASBackendItem = { if (message.fallback === true) { writer.uint32(112).bool(message.fallback); } + if (message.isManaged === true) { + writer.uint32(120).bool(message.isManaged); + } return writer; }, @@ -4647,6 +4653,13 @@ export const CASBackendItem = { message.fallback = reader.bool(); continue; + case 15: + if (tag !== 120) { + break; + } + + message.isManaged = reader.bool(); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -4674,6 +4687,7 @@ export const CASBackendItem = { validationError: isSet(object.validationError) ? String(object.validationError) : undefined, updatedAt: isSet(object.updatedAt) ? fromJsonTimestamp(object.updatedAt) : undefined, fallback: isSet(object.fallback) ? Boolean(object.fallback) : false, + isManaged: isSet(object.isManaged) ? Boolean(object.isManaged) : false, }; }, @@ -4695,6 +4709,7 @@ export const CASBackendItem = { message.validationError !== undefined && (obj.validationError = message.validationError); message.updatedAt !== undefined && (obj.updatedAt = message.updatedAt.toISOString()); message.fallback !== undefined && (obj.fallback = message.fallback); + message.isManaged !== undefined && (obj.isManaged = message.isManaged); return obj; }, @@ -4720,6 +4735,7 @@ export const CASBackendItem = { message.validationError = object.validationError ?? undefined; message.updatedAt = object.updatedAt ?? undefined; message.fallback = object.fallback ?? false; + message.isManaged = object.isManaged ?? false; return message; }, }; diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.CASBackendItem.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.CASBackendItem.jsonschema.json index 84c6561c5..a58c0b81b 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.CASBackendItem.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.CASBackendItem.jsonschema.json @@ -10,6 +10,10 @@ "description": "Is it an inline backend?\n inline means that the content is stored in the attestation itself", "type": "boolean" }, + "^(is_managed)$": { + "description": "Whether this backend is provisioned and operated by Chainloop using", + "type": "boolean" + }, "^(updated_at)$": { "$ref": "google.protobuf.Timestamp.jsonschema.json" }, @@ -61,6 +65,10 @@ "description": "Is it an inline backend?\n inline means that the content is stored in the attestation itself", "type": "boolean" }, + "isManaged": { + "description": "Whether this backend is provisioned and operated by Chainloop using", + "type": "boolean" + }, "limits": { "$ref": "controlplane.v1.CASBackendItem.Limits.jsonschema.json", "description": "Limits for this backend" diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.CASBackendItem.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.CASBackendItem.schema.json index 9860a549d..b8bc0f222 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.CASBackendItem.schema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.CASBackendItem.schema.json @@ -10,6 +10,10 @@ "description": "Is it an inline backend?\n inline means that the content is stored in the attestation itself", "type": "boolean" }, + "^(isManaged)$": { + "description": "Whether this backend is provisioned and operated by Chainloop using", + "type": "boolean" + }, "^(updatedAt)$": { "$ref": "google.protobuf.Timestamp.schema.json" }, @@ -61,6 +65,10 @@ "description": "Is it an inline backend?\n inline means that the content is stored in the attestation itself", "type": "boolean" }, + "is_managed": { + "description": "Whether this backend is provisioned and operated by Chainloop using", + "type": "boolean" + }, "limits": { "$ref": "controlplane.v1.CASBackendItem.Limits.schema.json", "description": "Limits for this backend" diff --git a/app/controlplane/internal/service/casbackend.go b/app/controlplane/internal/service/casbackend.go index 8ed129164..8763e3c3b 100644 --- a/app/controlplane/internal/service/casbackend.go +++ b/app/controlplane/internal/service/casbackend.go @@ -203,6 +203,7 @@ func bizCASBackendToPb(in *biz.CASBackend) *pb.CASBackendItem { Default: in.Default, Fallback: in.Fallback, IsInline: in.Inline, + IsManaged: in.Managed, } if in.Limits != nil { diff --git a/app/controlplane/pkg/biz/casbackend.go b/app/controlplane/pkg/biz/casbackend.go index a54849e6a..89ef89132 100644 --- a/app/controlplane/pkg/biz/casbackend.go +++ b/app/controlplane/pkg/biz/casbackend.go @@ -72,6 +72,8 @@ type CASBackend struct { Inline bool // It's a fallback backend, used when the default backend is unreachable Fallback bool + // Managed indicates this backend is provisioned and operated by Chainloop + Managed bool Limits *CASBackendLimits } @@ -88,6 +90,7 @@ type CASBackendOpts struct { Provider CASBackendProvider Default *bool Fallback *bool + Managed *bool ValidationStatus CASBackendValidationStatus ValidationError *string } @@ -450,6 +453,11 @@ func (uc *CASBackendUseCase) Update(ctx context.Context, orgID, id string, descr return nil, NewErrValidationStr("inline backends cannot have their max_bytes updated") } + // Managed backends are owned and operated by Chainloop and cannot be modified by users. + if before.Managed { + return nil, NewErrValidationStr("managed CAS backends cannot be modified") + } + // Validate max_bytes if provided if maxBytes != nil && *maxBytes < MinCASBackendMaxBytes { return nil, NewErrValidationStr(fmt.Sprintf("max_bytes must be at least %s", bytefmt.ByteSize(uint64(MinCASBackendMaxBytes)))) @@ -612,6 +620,11 @@ func (uc *CASBackendUseCase) SoftDelete(ctx context.Context, orgID, id string) e return NewErrValidation(errors.New("can't delete the inline CAS backend")) } + // Prevent deletion of managed backends - they are owned and operated by Chainloop + if backend.Managed { + return NewErrValidation(errors.New("can't delete a managed CAS backend")) + } + if err := uc.repo.SoftDelete(ctx, backendUUID); err != nil { return err } diff --git a/app/controlplane/pkg/data/casbackend.go b/app/controlplane/pkg/data/casbackend.go index 140daf599..5ee01a252 100644 --- a/app/controlplane/pkg/data/casbackend.go +++ b/app/controlplane/pkg/data/casbackend.go @@ -155,6 +155,7 @@ func (r *CASBackendRepo) Create(ctx context.Context, opts *biz.CASBackendCreateO SetNillableFallback(opts.Fallback). SetProvider(opts.Provider). SetNillableDefault(opts.Default). + SetNillableManaged(opts.Managed). SetSecretName(opts.SecretName). SetMaxBlobSizeBytes(opts.MaxBytes). Save(ctx) @@ -404,6 +405,7 @@ func entCASBackendToBiz(backend *ent.CASBackend) *biz.CASBackend { Inline: backend.Provider == biz.CASBackendInline, Limits: limits, Fallback: backend.Fallback, + Managed: backend.Managed, } if org := backend.Edges.Organization; org != nil { diff --git a/app/controlplane/pkg/data/ent/casbackend.go b/app/controlplane/pkg/data/ent/casbackend.go index 7e87697b1..db504d669 100644 --- a/app/controlplane/pkg/data/ent/casbackend.go +++ b/app/controlplane/pkg/data/ent/casbackend.go @@ -46,6 +46,8 @@ type CASBackend struct { DeletedAt time.Time `json:"deleted_at,omitempty"` // Fallback holds the value of the "fallback" field. Fallback bool `json:"fallback,omitempty"` + // Managed holds the value of the "managed" field. + Managed bool `json:"managed,omitempty"` // MaxBlobSizeBytes holds the value of the "max_blob_size_bytes" field. MaxBlobSizeBytes int64 `json:"max_blob_size_bytes,omitempty"` // Edges holds the relations/edges for other nodes in the graph. @@ -91,7 +93,7 @@ func (*CASBackend) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) for i := range columns { switch columns[i] { - case casbackend.FieldDefault, casbackend.FieldFallback: + case casbackend.FieldDefault, casbackend.FieldFallback, casbackend.FieldManaged: values[i] = new(sql.NullBool) case casbackend.FieldMaxBlobSizeBytes: values[i] = new(sql.NullInt64) @@ -202,6 +204,12 @@ func (_m *CASBackend) assignValues(columns []string, values []any) error { } else if value.Valid { _m.Fallback = value.Bool } + case casbackend.FieldManaged: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field managed", values[i]) + } else if value.Valid { + _m.Managed = value.Bool + } case casbackend.FieldMaxBlobSizeBytes: if value, ok := values[i].(*sql.NullInt64); !ok { return fmt.Errorf("unexpected type %T for field max_blob_size_bytes", values[i]) @@ -300,6 +308,9 @@ func (_m *CASBackend) String() string { builder.WriteString("fallback=") builder.WriteString(fmt.Sprintf("%v", _m.Fallback)) builder.WriteString(", ") + builder.WriteString("managed=") + builder.WriteString(fmt.Sprintf("%v", _m.Managed)) + builder.WriteString(", ") builder.WriteString("max_blob_size_bytes=") builder.WriteString(fmt.Sprintf("%v", _m.MaxBlobSizeBytes)) builder.WriteByte(')') diff --git a/app/controlplane/pkg/data/ent/casbackend/casbackend.go b/app/controlplane/pkg/data/ent/casbackend/casbackend.go index f07e8e9f0..8fe9e8c68 100644 --- a/app/controlplane/pkg/data/ent/casbackend/casbackend.go +++ b/app/controlplane/pkg/data/ent/casbackend/casbackend.go @@ -43,6 +43,8 @@ const ( FieldDeletedAt = "deleted_at" // FieldFallback holds the string denoting the fallback field in the database. FieldFallback = "fallback" + // FieldManaged holds the string denoting the managed field in the database. + FieldManaged = "managed" // FieldMaxBlobSizeBytes holds the string denoting the max_blob_size_bytes field in the database. FieldMaxBlobSizeBytes = "max_blob_size_bytes" // EdgeOrganization holds the string denoting the organization edge name in mutations. @@ -81,6 +83,7 @@ var Columns = []string{ FieldDefault, FieldDeletedAt, FieldFallback, + FieldManaged, FieldMaxBlobSizeBytes, } @@ -124,6 +127,8 @@ var ( DefaultDefault bool // DefaultFallback holds the default value on creation for the "fallback" field. DefaultFallback bool + // DefaultManaged holds the default value on creation for the "managed" field. + DefaultManaged bool // DefaultID holds the default value on creation for the "id" field. DefaultID func() uuid.UUID ) @@ -223,6 +228,11 @@ func ByFallback(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldFallback, opts...).ToFunc() } +// ByManaged orders the results by the managed field. +func ByManaged(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldManaged, opts...).ToFunc() +} + // ByMaxBlobSizeBytes orders the results by the max_blob_size_bytes field. func ByMaxBlobSizeBytes(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldMaxBlobSizeBytes, opts...).ToFunc() diff --git a/app/controlplane/pkg/data/ent/casbackend/where.go b/app/controlplane/pkg/data/ent/casbackend/where.go index 47e3f3a38..9325be1f6 100644 --- a/app/controlplane/pkg/data/ent/casbackend/where.go +++ b/app/controlplane/pkg/data/ent/casbackend/where.go @@ -112,6 +112,11 @@ func Fallback(v bool) predicate.CASBackend { return predicate.CASBackend(sql.FieldEQ(FieldFallback, v)) } +// Managed applies equality check predicate on the "managed" field. It's identical to ManagedEQ. +func Managed(v bool) predicate.CASBackend { + return predicate.CASBackend(sql.FieldEQ(FieldManaged, v)) +} + // MaxBlobSizeBytes applies equality check predicate on the "max_blob_size_bytes" field. It's identical to MaxBlobSizeBytesEQ. func MaxBlobSizeBytes(v int64) predicate.CASBackend { return predicate.CASBackend(sql.FieldEQ(FieldMaxBlobSizeBytes, v)) @@ -712,6 +717,16 @@ func FallbackNEQ(v bool) predicate.CASBackend { return predicate.CASBackend(sql.FieldNEQ(FieldFallback, v)) } +// ManagedEQ applies the EQ predicate on the "managed" field. +func ManagedEQ(v bool) predicate.CASBackend { + return predicate.CASBackend(sql.FieldEQ(FieldManaged, v)) +} + +// ManagedNEQ applies the NEQ predicate on the "managed" field. +func ManagedNEQ(v bool) predicate.CASBackend { + return predicate.CASBackend(sql.FieldNEQ(FieldManaged, v)) +} + // MaxBlobSizeBytesEQ applies the EQ predicate on the "max_blob_size_bytes" field. func MaxBlobSizeBytesEQ(v int64) predicate.CASBackend { return predicate.CASBackend(sql.FieldEQ(FieldMaxBlobSizeBytes, v)) diff --git a/app/controlplane/pkg/data/ent/casbackend_create.go b/app/controlplane/pkg/data/ent/casbackend_create.go index edacbf94f..a1caf1db9 100644 --- a/app/controlplane/pkg/data/ent/casbackend_create.go +++ b/app/controlplane/pkg/data/ent/casbackend_create.go @@ -177,6 +177,20 @@ func (_c *CASBackendCreate) SetNillableFallback(v *bool) *CASBackendCreate { return _c } +// SetManaged sets the "managed" field. +func (_c *CASBackendCreate) SetManaged(v bool) *CASBackendCreate { + _c.mutation.SetManaged(v) + return _c +} + +// SetNillableManaged sets the "managed" field if the given value is not nil. +func (_c *CASBackendCreate) SetNillableManaged(v *bool) *CASBackendCreate { + if v != nil { + _c.SetManaged(*v) + } + return _c +} + // SetMaxBlobSizeBytes sets the "max_blob_size_bytes" field. func (_c *CASBackendCreate) SetMaxBlobSizeBytes(v int64) *CASBackendCreate { _c.mutation.SetMaxBlobSizeBytes(v) @@ -282,6 +296,10 @@ func (_c *CASBackendCreate) defaults() { v := casbackend.DefaultFallback _c.mutation.SetFallback(v) } + if _, ok := _c.mutation.Managed(); !ok { + v := casbackend.DefaultManaged + _c.mutation.SetManaged(v) + } if _, ok := _c.mutation.ID(); !ok { v := casbackend.DefaultID() _c.mutation.SetID(v) @@ -330,6 +348,9 @@ func (_c *CASBackendCreate) check() error { if _, ok := _c.mutation.Fallback(); !ok { return &ValidationError{Name: "fallback", err: errors.New(`ent: missing required field "CASBackend.fallback"`)} } + if _, ok := _c.mutation.Managed(); !ok { + return &ValidationError{Name: "managed", err: errors.New(`ent: missing required field "CASBackend.managed"`)} + } if _, ok := _c.mutation.MaxBlobSizeBytes(); !ok { return &ValidationError{Name: "max_blob_size_bytes", err: errors.New(`ent: missing required field "CASBackend.max_blob_size_bytes"`)} } @@ -424,6 +445,10 @@ func (_c *CASBackendCreate) createSpec() (*CASBackend, *sqlgraph.CreateSpec) { _spec.SetField(casbackend.FieldFallback, field.TypeBool, value) _node.Fallback = value } + if value, ok := _c.mutation.Managed(); ok { + _spec.SetField(casbackend.FieldManaged, field.TypeBool, value) + _node.Managed = value + } if value, ok := _c.mutation.MaxBlobSizeBytes(); ok { _spec.SetField(casbackend.FieldMaxBlobSizeBytes, field.TypeInt64, value) _node.MaxBlobSizeBytes = value @@ -639,6 +664,18 @@ func (u *CASBackendUpsert) UpdateFallback() *CASBackendUpsert { return u } +// SetManaged sets the "managed" field. +func (u *CASBackendUpsert) SetManaged(v bool) *CASBackendUpsert { + u.Set(casbackend.FieldManaged, v) + return u +} + +// UpdateManaged sets the "managed" field to the value that was provided on create. +func (u *CASBackendUpsert) UpdateManaged() *CASBackendUpsert { + u.SetExcluded(casbackend.FieldManaged) + return u +} + // SetMaxBlobSizeBytes sets the "max_blob_size_bytes" field. func (u *CASBackendUpsert) SetMaxBlobSizeBytes(v int64) *CASBackendUpsert { u.Set(casbackend.FieldMaxBlobSizeBytes, v) @@ -864,6 +901,20 @@ func (u *CASBackendUpsertOne) UpdateFallback() *CASBackendUpsertOne { }) } +// SetManaged sets the "managed" field. +func (u *CASBackendUpsertOne) SetManaged(v bool) *CASBackendUpsertOne { + return u.Update(func(s *CASBackendUpsert) { + s.SetManaged(v) + }) +} + +// UpdateManaged sets the "managed" field to the value that was provided on create. +func (u *CASBackendUpsertOne) UpdateManaged() *CASBackendUpsertOne { + return u.Update(func(s *CASBackendUpsert) { + s.UpdateManaged() + }) +} + // SetMaxBlobSizeBytes sets the "max_blob_size_bytes" field. func (u *CASBackendUpsertOne) SetMaxBlobSizeBytes(v int64) *CASBackendUpsertOne { return u.Update(func(s *CASBackendUpsert) { @@ -1259,6 +1310,20 @@ func (u *CASBackendUpsertBulk) UpdateFallback() *CASBackendUpsertBulk { }) } +// SetManaged sets the "managed" field. +func (u *CASBackendUpsertBulk) SetManaged(v bool) *CASBackendUpsertBulk { + return u.Update(func(s *CASBackendUpsert) { + s.SetManaged(v) + }) +} + +// UpdateManaged sets the "managed" field to the value that was provided on create. +func (u *CASBackendUpsertBulk) UpdateManaged() *CASBackendUpsertBulk { + return u.Update(func(s *CASBackendUpsert) { + s.UpdateManaged() + }) +} + // SetMaxBlobSizeBytes sets the "max_blob_size_bytes" field. func (u *CASBackendUpsertBulk) SetMaxBlobSizeBytes(v int64) *CASBackendUpsertBulk { return u.Update(func(s *CASBackendUpsert) { diff --git a/app/controlplane/pkg/data/ent/casbackend_update.go b/app/controlplane/pkg/data/ent/casbackend_update.go index 47f69d2ef..390d93f5f 100644 --- a/app/controlplane/pkg/data/ent/casbackend_update.go +++ b/app/controlplane/pkg/data/ent/casbackend_update.go @@ -169,6 +169,20 @@ func (_u *CASBackendUpdate) SetNillableFallback(v *bool) *CASBackendUpdate { return _u } +// SetManaged sets the "managed" field. +func (_u *CASBackendUpdate) SetManaged(v bool) *CASBackendUpdate { + _u.mutation.SetManaged(v) + return _u +} + +// SetNillableManaged sets the "managed" field if the given value is not nil. +func (_u *CASBackendUpdate) SetNillableManaged(v *bool) *CASBackendUpdate { + if v != nil { + _u.SetManaged(*v) + } + return _u +} + // SetMaxBlobSizeBytes sets the "max_blob_size_bytes" field. func (_u *CASBackendUpdate) SetMaxBlobSizeBytes(v int64) *CASBackendUpdate { _u.mutation.ResetMaxBlobSizeBytes() @@ -351,6 +365,9 @@ func (_u *CASBackendUpdate) sqlSave(ctx context.Context) (_node int, err error) if value, ok := _u.mutation.Fallback(); ok { _spec.SetField(casbackend.FieldFallback, field.TypeBool, value) } + if value, ok := _u.mutation.Managed(); ok { + _spec.SetField(casbackend.FieldManaged, field.TypeBool, value) + } if value, ok := _u.mutation.MaxBlobSizeBytes(); ok { _spec.SetField(casbackend.FieldMaxBlobSizeBytes, field.TypeInt64, value) } @@ -589,6 +606,20 @@ func (_u *CASBackendUpdateOne) SetNillableFallback(v *bool) *CASBackendUpdateOne return _u } +// SetManaged sets the "managed" field. +func (_u *CASBackendUpdateOne) SetManaged(v bool) *CASBackendUpdateOne { + _u.mutation.SetManaged(v) + return _u +} + +// SetNillableManaged sets the "managed" field if the given value is not nil. +func (_u *CASBackendUpdateOne) SetNillableManaged(v *bool) *CASBackendUpdateOne { + if v != nil { + _u.SetManaged(*v) + } + return _u +} + // SetMaxBlobSizeBytes sets the "max_blob_size_bytes" field. func (_u *CASBackendUpdateOne) SetMaxBlobSizeBytes(v int64) *CASBackendUpdateOne { _u.mutation.ResetMaxBlobSizeBytes() @@ -801,6 +832,9 @@ func (_u *CASBackendUpdateOne) sqlSave(ctx context.Context) (_node *CASBackend, if value, ok := _u.mutation.Fallback(); ok { _spec.SetField(casbackend.FieldFallback, field.TypeBool, value) } + if value, ok := _u.mutation.Managed(); ok { + _spec.SetField(casbackend.FieldManaged, field.TypeBool, value) + } if value, ok := _u.mutation.MaxBlobSizeBytes(); ok { _spec.SetField(casbackend.FieldMaxBlobSizeBytes, field.TypeInt64, value) } diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/20260504100323.sql b/app/controlplane/pkg/data/ent/migrate/migrations/20260504100323.sql new file mode 100644 index 000000000..94851a26b --- /dev/null +++ b/app/controlplane/pkg/data/ent/migrate/migrations/20260504100323.sql @@ -0,0 +1,2 @@ +-- Modify "cas_backends" table +ALTER TABLE "cas_backends" ADD COLUMN "managed" boolean NOT NULL DEFAULT false; diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum b/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum index c6bff305d..1cc649fe5 100644 --- a/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum +++ b/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:qBVt7XbDlg3MT/tOTxZ6AwotFWXacU/xaweG4KC9WXQ= +h1:+gkhpt5jMJdO8QfSHQorsIxqKkMQ5ZSrt2gr7cmOoQo= 20230706165452_init-schema.sql h1:VvqbNFEQnCvUVyj2iDYVQQxDM0+sSXqocpt/5H64k8M= 20230710111950-cas-backend.sql h1:A8iBuSzZIEbdsv9ipBtscZQuaBp3V5/VMw7eZH6GX+g= 20230712094107-cas-backends-workflow-runs.sql h1:a5rzxpVGyd56nLRSsKrmCFc9sebg65RWzLghKHh5xvI= @@ -131,3 +131,4 @@ h1:qBVt7XbDlg3MT/tOTxZ6AwotFWXacU/xaweG4KC9WXQ= 20260416153232.sql h1:xjEfZuMOo1lgZm3VUYGHpNOhpJixncVZuMRg0jiH+7A= 20260418100730.sql h1:lLcPDneBlzyabUAOIEKqCJgtnklHGac4JUnnZAbyD1g= 20260430223706.sql h1:2mpbhdB4JWfMY8w9J8TGUj/O3tIT7dQpWfugTTI1eEQ= +20260504100323.sql h1:FP8y59ZXFUsyskbIfl/1nE7vo4OJcOPuALy3pAJaStQ= diff --git a/app/controlplane/pkg/data/ent/migrate/schema.go b/app/controlplane/pkg/data/ent/migrate/schema.go index b5fbc69d8..1225ac791 100644 --- a/app/controlplane/pkg/data/ent/migrate/schema.go +++ b/app/controlplane/pkg/data/ent/migrate/schema.go @@ -105,6 +105,7 @@ var ( {Name: "default", Type: field.TypeBool, Default: false}, {Name: "deleted_at", Type: field.TypeTime, Nullable: true}, {Name: "fallback", Type: field.TypeBool, Default: false}, + {Name: "managed", Type: field.TypeBool, Default: false}, {Name: "max_blob_size_bytes", Type: field.TypeInt64}, {Name: "organization_cas_backends", Type: field.TypeUUID}, } @@ -116,7 +117,7 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "cas_backends_organizations_cas_backends", - Columns: []*schema.Column{CasBackendsColumns[15]}, + Columns: []*schema.Column{CasBackendsColumns[16]}, RefColumns: []*schema.Column{OrganizationsColumns[0]}, OnDelete: schema.Cascade, }, @@ -125,7 +126,7 @@ var ( { Name: "casbackend_name_organization_cas_backends", Unique: true, - Columns: []*schema.Column{CasBackendsColumns[2], CasBackendsColumns[15]}, + Columns: []*schema.Column{CasBackendsColumns[2], CasBackendsColumns[16]}, Annotation: &entsql.IndexAnnotation{ Where: "deleted_at IS NULL", }, diff --git a/app/controlplane/pkg/data/ent/mutation.go b/app/controlplane/pkg/data/ent/mutation.go index d756b2b7b..47d283788 100644 --- a/app/controlplane/pkg/data/ent/mutation.go +++ b/app/controlplane/pkg/data/ent/mutation.go @@ -1600,6 +1600,7 @@ type CASBackendMutation struct { _default *bool deleted_at *time.Time fallback *bool + managed *bool max_blob_size_bytes *int64 addmax_blob_size_bytes *int64 clearedFields map[string]struct{} @@ -2224,6 +2225,42 @@ func (m *CASBackendMutation) ResetFallback() { m.fallback = nil } +// SetManaged sets the "managed" field. +func (m *CASBackendMutation) SetManaged(b bool) { + m.managed = &b +} + +// Managed returns the value of the "managed" field in the mutation. +func (m *CASBackendMutation) Managed() (r bool, exists bool) { + v := m.managed + if v == nil { + return + } + return *v, true +} + +// OldManaged returns the old "managed" field's value of the CASBackend entity. +// If the CASBackend object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CASBackendMutation) OldManaged(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldManaged is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldManaged requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldManaged: %w", err) + } + return oldValue.Managed, nil +} + +// ResetManaged resets all changes to the "managed" field. +func (m *CASBackendMutation) ResetManaged() { + m.managed = nil +} + // SetMaxBlobSizeBytes sets the "max_blob_size_bytes" field. func (m *CASBackendMutation) SetMaxBlobSizeBytes(i int64) { m.max_blob_size_bytes = &i @@ -2407,7 +2444,7 @@ func (m *CASBackendMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *CASBackendMutation) Fields() []string { - fields := make([]string, 0, 14) + fields := make([]string, 0, 15) if m.location != nil { fields = append(fields, casbackend.FieldLocation) } @@ -2447,6 +2484,9 @@ func (m *CASBackendMutation) Fields() []string { if m.fallback != nil { fields = append(fields, casbackend.FieldFallback) } + if m.managed != nil { + fields = append(fields, casbackend.FieldManaged) + } if m.max_blob_size_bytes != nil { fields = append(fields, casbackend.FieldMaxBlobSizeBytes) } @@ -2484,6 +2524,8 @@ func (m *CASBackendMutation) Field(name string) (ent.Value, bool) { return m.DeletedAt() case casbackend.FieldFallback: return m.Fallback() + case casbackend.FieldManaged: + return m.Managed() case casbackend.FieldMaxBlobSizeBytes: return m.MaxBlobSizeBytes() } @@ -2521,6 +2563,8 @@ func (m *CASBackendMutation) OldField(ctx context.Context, name string) (ent.Val return m.OldDeletedAt(ctx) case casbackend.FieldFallback: return m.OldFallback(ctx) + case casbackend.FieldManaged: + return m.OldManaged(ctx) case casbackend.FieldMaxBlobSizeBytes: return m.OldMaxBlobSizeBytes(ctx) } @@ -2623,6 +2667,13 @@ func (m *CASBackendMutation) SetField(name string, value ent.Value) error { } m.SetFallback(v) return nil + case casbackend.FieldManaged: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetManaged(v) + return nil case casbackend.FieldMaxBlobSizeBytes: v, ok := value.(int64) if !ok { @@ -2754,6 +2805,9 @@ func (m *CASBackendMutation) ResetField(name string) error { case casbackend.FieldFallback: m.ResetFallback() return nil + case casbackend.FieldManaged: + m.ResetManaged() + return nil case casbackend.FieldMaxBlobSizeBytes: m.ResetMaxBlobSizeBytes() return nil diff --git a/app/controlplane/pkg/data/ent/runtime.go b/app/controlplane/pkg/data/ent/runtime.go index f02157bc3..416a47170 100644 --- a/app/controlplane/pkg/data/ent/runtime.go +++ b/app/controlplane/pkg/data/ent/runtime.go @@ -81,6 +81,10 @@ func init() { casbackendDescFallback := casbackendFields[13].Descriptor() // casbackend.DefaultFallback holds the default value on creation for the fallback field. casbackend.DefaultFallback = casbackendDescFallback.Default.(bool) + // casbackendDescManaged is the schema descriptor for managed field. + casbackendDescManaged := casbackendFields[14].Descriptor() + // casbackend.DefaultManaged holds the default value on creation for the managed field. + casbackend.DefaultManaged = casbackendDescManaged.Default.(bool) // casbackendDescID is the schema descriptor for id field. casbackendDescID := casbackendFields[0].Descriptor() // casbackend.DefaultID holds the default value on creation for the id field. diff --git a/app/controlplane/pkg/data/ent/schema/casbackend.go b/app/controlplane/pkg/data/ent/schema/casbackend.go index 16652db59..c59669978 100644 --- a/app/controlplane/pkg/data/ent/schema/casbackend.go +++ b/app/controlplane/pkg/data/ent/schema/casbackend.go @@ -60,6 +60,8 @@ func (CASBackend) Fields() []ent.Field { field.Time("deleted_at").Optional(), // fallback, main cas backend. If true, this backend will be used as a fallback when the default backend is unreachable field.Bool("fallback").Default(false), + // managed indicates this backend is provisioned and operated by Chainloop + field.Bool("managed").Default(false), field.Int64("max_blob_size_bytes"), } }