Skip to content

Commit c52ee88

Browse files
authored
fix(cli): transfer respect resolvers (open-component-model#1888)
<!-- markdownlint-disable MD041 --> #### What this PR does / why we need it So far, we were doing a prefetch of the components. The prefetch used the configured resolvers. Since those components are added to the environment of the TGD and not fetched via transformations, we did not run into issues here. But the Generiererich hard coded against the from spec. This created issues for local blobs, as they now were trying to be fetched from the source repo instead of the correct repo configured by the resolvers. #### Which issue(s) this PR fixes <!-- Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`. --> Contributes to open-component-model/ocm-project#905 #### Testing ##### How to test the changes <!-- Required files to test the changes: .ocmconfig ```yaml type: generic.config.ocm.software/v1 configurations: - type: credentials.config.ocm.software repositories: - repository: type: DockerConfig/v1 dockerConfigFile: "~/.docker/config.json" ``` Commands that test the change: ```bash ocm get cv xxx ocm transfer xxx ``` --> ##### Verification - [x] I have tested the changes locally by running `ocm` --------- Signed-off-by: Fabian Burth <fabian.burth@sap.com>
1 parent 6f2e3a7 commit c52ee88

File tree

12 files changed

+256
-75
lines changed

12 files changed

+256
-75
lines changed

cli/cmd/add/component-version/cmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ func AddComponentVersion(cmd *cobra.Command, _ []string) error {
314314
}
315315

316316
config := ocmctx.FromContext(cmd.Context()).Configuration()
317-
ref, err := compref.ParseRepository(repositoryRef, compref.WithCTFAccessMode(ctfv1.AccessModeReadWrite))
317+
ref, err := compref.ParseRepository(repositoryRef, compref.WithCTFAccessMode(ctfv1.AccessModeCreate+"|"+ctfv1.AccessModeReadWrite))
318318
if err != nil {
319319
return fmt.Errorf("parsing repository reference %q failed: %w", repositoryRef, err)
320320
}

cli/cmd/transfer/component-version/internal/discovery.go

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,14 @@ import (
99
"sync"
1010

1111
descriptor "ocm.software/open-component-model/bindings/go/descriptor/runtime"
12-
descriptorv2 "ocm.software/open-component-model/bindings/go/descriptor/v2"
1312
"ocm.software/open-component-model/bindings/go/repository/component/resolvers"
1413
"ocm.software/open-component-model/bindings/go/runtime"
1514
"ocm.software/open-component-model/bindings/go/signing"
16-
"ocm.software/open-component-model/cli/internal/reference/compref"
1715
)
1816

1917
type discoveryValue struct {
20-
Ref *compref.Ref
21-
Descriptor *descriptor.Descriptor
22-
Digest *descriptorv2.Digest
18+
Descriptor *descriptor.Descriptor
19+
SourceRepository runtime.Typed
2320
}
2421

2522
type resolver struct {
@@ -28,19 +25,25 @@ type resolver struct {
2825
}
2926

3027
func (r *resolver) Resolve(ctx context.Context, key string) (*discoveryValue, error) {
31-
ref, err := compref.Parse(key)
28+
parts := strings.SplitN(key, ":", 2)
29+
if len(parts) != 2 {
30+
return nil, fmt.Errorf("invalid key format %q: expected component:version", key)
31+
}
32+
component, version := parts[0], parts[1]
33+
34+
repoSpec, err := r.repoResolver.GetRepositorySpecificationForComponent(ctx, component, version)
3235
if err != nil {
33-
return nil, fmt.Errorf("failed to parse reference %q: %w", key, err)
36+
return nil, fmt.Errorf("failed getting repository spec for component %s:%s: %w", component, version, err)
3437
}
3538

36-
repo, err := r.repoResolver.GetComponentVersionRepositoryForComponent(ctx, ref.Component, ref.Version)
39+
repo, err := r.repoResolver.GetComponentVersionRepositoryForSpecification(ctx, repoSpec)
3740
if err != nil {
38-
return nil, fmt.Errorf("failed getting component version repository: %w", err)
41+
return nil, fmt.Errorf("failed getting component version repository for spec %v: %w", repoSpec, err)
3942
}
4043

41-
desc, err := repo.GetComponentVersion(ctx, ref.Component, ref.Version)
44+
desc, err := repo.GetComponentVersion(ctx, component, version)
4245
if err != nil {
43-
return nil, fmt.Errorf("failed getting component version %s:%s: %w", ref.Component, ref.Version, err)
46+
return nil, fmt.Errorf("failed getting component version %s:%s: %w", component, version, err)
4447
}
4548

4649
if expected := r.expectedDigest(desc.Component.ToIdentity()); expected != nil {
@@ -52,8 +55,8 @@ func (r *resolver) Resolve(ctx context.Context, key string) (*discoveryValue, er
5255
}
5356

5457
return &discoveryValue{
55-
Ref: ref,
56-
Descriptor: desc,
58+
Descriptor: desc,
59+
SourceRepository: repoSpec,
5760
}, nil
5861
}
5962

@@ -70,22 +73,14 @@ func (d *discoverer) Discover(ctx context.Context, parent *discoveryValue) ([]st
7073
}
7174
var children []string
7275
for _, ref := range parent.Descriptor.Component.References {
73-
childRef := &compref.Ref{
74-
Type: parent.Ref.Type,
75-
Repository: parent.Ref.Repository,
76-
Prefix: parent.Ref.Prefix,
77-
Component: ref.Component,
78-
Version: ref.Version,
79-
}
80-
base := childRef.String()
76+
key := ref.Component + ":" + ref.Version
8177

8278
if ref.Digest.Value != "" {
8379
d.mu.Lock()
84-
// TODO Panic on differing digests
8580
d.discoveredDigests[ref.ToComponentIdentity().String()] = ref.Digest
8681
d.mu.Unlock()
8782
}
88-
children = append(children, base)
83+
children = append(children, key)
8984
}
9085
return children, nil
9186
}

cli/cmd/transfer/component-version/internal/graph.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88

99
"ocm.software/open-component-model/bindings/go/dag"
1010
dagsync "ocm.software/open-component-model/bindings/go/dag/sync"
11-
descriptor "ocm.software/open-component-model/bindings/go/descriptor/runtime"
11+
descruntime "ocm.software/open-component-model/bindings/go/descriptor/runtime"
1212
descriptorv2 "ocm.software/open-component-model/bindings/go/descriptor/v2"
1313
ociv1 "ocm.software/open-component-model/bindings/go/oci/spec/access/v1"
1414
"ocm.software/open-component-model/bindings/go/oci/spec/repository/v1/oci"
@@ -33,11 +33,11 @@ func BuildGraphDefinition(
3333

3434
disc := &discoverer{
3535
recursive: o.Recursive,
36-
discoveredDigests: make(map[string]descriptor.Digest),
36+
discoveredDigests: make(map[string]descruntime.Digest),
3737
}
3838
res := &resolver{
3939
repoResolver: repoResolver,
40-
expectedDigest: func(id runtime.Identity) *descriptor.Digest {
40+
expectedDigest: func(id runtime.Identity) *descruntime.Digest {
4141
disc.mu.Lock()
4242
defer disc.mu.Unlock()
4343
if !disc.recursive {
@@ -51,7 +51,7 @@ func BuildGraphDefinition(
5151
},
5252
}
5353

54-
root := fromSpec.String()
54+
root := fromSpec.Component + ":" + fromSpec.Version
5555

5656
dr := dagsync.NewGraphDiscoverer(&dagsync.GraphDiscovererOptions[string, *discoveryValue]{
5757
Roots: []string{root},
@@ -85,11 +85,15 @@ func BuildGraphDefinition(
8585
func fillGraphDefinitionWithPrefetchedComponents(ctx context.Context, d *dag.DirectedAcyclicGraph[string], toSpec runtime.Typed, tgd *transformv1alpha1.TransformationGraphDefinition, copyMode CopyMode, uploadType UploadType) error {
8686
for _, v := range d.Vertices {
8787
val := v.Attributes[dagsync.AttributeValue].(*discoveryValue)
88-
ref := val.Ref
88+
component := val.Descriptor.Component.Name
89+
version := val.Descriptor.Component.Version
8990

90-
id := identityToTransformationID(ref.Identity())
91+
id := identityToTransformationID(runtime.Identity{
92+
descruntime.IdentityAttributeName: component,
93+
descruntime.IdentityAttributeVersion: version,
94+
})
9195

92-
v2desc, err := descriptor.ConvertToV2(runtime.NewScheme(runtime.WithAllowUnknown()), val.Descriptor)
96+
v2desc, err := descruntime.ConvertToV2(runtime.NewScheme(runtime.WithAllowUnknown()), val.Descriptor)
9397
if err != nil {
9498
return fmt.Errorf("cannot convert to v2: %w", err)
9599
}
@@ -116,8 +120,8 @@ func fillGraphDefinitionWithPrefetchedComponents(ctx context.Context, d *dag.Dir
116120
}
117121
slog.Log(ctx, logLevel,
118122
"Skipping copy of resource since its access type is not a local blob. Only resources with local blob access are copied when CopyModeLocalBlobResources is set.",
119-
"component", ref.Component,
120-
"version", ref.Version,
123+
"component", component,
124+
"version", version,
121125
"resource", resource.ToIdentity().String(),
122126
"accessType", resource.Access.Type.String(),
123127
"copyMode", copyMode)
@@ -139,7 +143,7 @@ func fillGraphDefinitionWithPrefetchedComponents(ctx context.Context, d *dag.Dir
139143
}
140144
}
141145
}
142-
if err := processLocalBlob(resource, acc, id, ref, tgd, toSpec, resourceTransformIDs, i, uploadAsOCIArtifact); err != nil {
146+
if err := processLocalBlob(resource, acc, id, val, tgd, toSpec, resourceTransformIDs, i, uploadAsOCIArtifact); err != nil {
143147
return fmt.Errorf("failed processing local blob resource: %w", err)
144148
}
145149
case *ociv1.OCIImage:
@@ -149,14 +153,14 @@ func fillGraphDefinitionWithPrefetchedComponents(ctx context.Context, d *dag.Dir
149153
uploadAsOCIArtifact = true
150154
}
151155
}
152-
err := processOCIArtifact(resource, id, ref, tgd, toSpec, resourceTransformIDs, i, uploadAsOCIArtifact)
156+
err := processOCIArtifact(resource, id, val, tgd, toSpec, resourceTransformIDs, i, uploadAsOCIArtifact)
153157
if err != nil {
154158
return fmt.Errorf("cannot process OCI artifact resource: %w", err)
155159
}
156160
default:
157161
// No transformation configured for resource with access types not listed above
158162
slog.Info("Unsupported resource access type, skipping resource. Only local blob and OCI artifact resources are supported for transformation.",
159-
"component", ref.Component, "version", ref.Version, "resource", resource.ToIdentity().String(), "accessType", resource.Access.Type.String())
163+
"component", component, "version", version, "resource", resource.ToIdentity().String(), "accessType", resource.Access.Type.String())
160164
}
161165
}
162166

@@ -231,9 +235,14 @@ func fillGraphDefinitionWithPrefetchedComponents(ctx context.Context, d *dag.Dir
231235
descriptorSpec = fmt.Sprintf("${environment.%s}", id)
232236
}
233237

238+
addType, err := ChooseAddType(toSpec)
239+
if err != nil {
240+
return fmt.Errorf("choosing add type for target repository: %w", err)
241+
}
242+
234243
upload := transformv1alpha1.GenericTransformation{
235244
TransformationMeta: meta.TransformationMeta{
236-
Type: ChooseAddType(toSpec),
245+
Type: addType,
237246
ID: id + "Upload",
238247
},
239248
Spec: &runtime.Unstructured{Data: map[string]any{

cli/cmd/transfer/component-version/internal/helpers.go

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,36 +25,67 @@ func AsUnstructured(typed runtime.Typed) *runtime.Unstructured {
2525
return &unstructured
2626
}
2727

28-
func ChooseAddType(repo runtime.Typed) runtime.Type {
29-
switch repo.(type) {
28+
// convertToConcreteRepo converts a runtime.Typed (which may be *runtime.Raw) to a concrete repository type.
29+
func convertToConcreteRepo(repo runtime.Typed) (runtime.Typed, error) {
30+
switch r := repo.(type) {
31+
case *oci.Repository, *ctfv1.Repository:
32+
return repo, nil
33+
case *runtime.Raw:
34+
obj, err := Scheme.NewObject(r.Type)
35+
if err != nil {
36+
return nil, fmt.Errorf("cannot create object for type %s: %w", r.Type, err)
37+
}
38+
if err := Scheme.Convert(r, obj); err != nil {
39+
return nil, fmt.Errorf("cannot convert raw to concrete type: %w", err)
40+
}
41+
return obj, nil
42+
default:
43+
return nil, fmt.Errorf("unknown repository type %T", repo)
44+
}
45+
}
46+
47+
func ChooseAddType(repo runtime.Typed) (runtime.Type, error) {
48+
concreteRepo, err := convertToConcreteRepo(repo)
49+
if err != nil {
50+
return runtime.Type{}, fmt.Errorf("converting repository spec: %w", err)
51+
}
52+
switch concreteRepo.(type) {
3053
case *oci.Repository:
31-
return ociv1alpha1.OCIAddComponentVersionV1alpha1
54+
return ociv1alpha1.OCIAddComponentVersionV1alpha1, nil
3255
case *ctfv1.Repository:
33-
return ociv1alpha1.CTFAddComponentVersionV1alpha1
56+
return ociv1alpha1.CTFAddComponentVersionV1alpha1, nil
3457
default:
35-
panic(fmt.Sprintf("unknown repository type %T", repo))
58+
return runtime.Type{}, fmt.Errorf("unsupported repository type %T for add operation", concreteRepo)
3659
}
3760
}
3861

39-
func ChooseGetLocalResourceType(repo runtime.Typed) runtime.Type {
40-
switch repo.(type) {
62+
func ChooseGetLocalResourceType(repo runtime.Typed) (runtime.Type, error) {
63+
concreteRepo, err := convertToConcreteRepo(repo)
64+
if err != nil {
65+
return runtime.Type{}, fmt.Errorf("converting repository spec: %w", err)
66+
}
67+
switch concreteRepo.(type) {
4168
case *oci.Repository:
42-
return ociv1alpha1.OCIGetLocalResourceV1alpha1
69+
return ociv1alpha1.OCIGetLocalResourceV1alpha1, nil
4370
case *ctfv1.Repository:
44-
return ociv1alpha1.CTFGetLocalResourceV1alpha1
71+
return ociv1alpha1.CTFGetLocalResourceV1alpha1, nil
4572
default:
46-
panic(fmt.Sprintf("unknown repository type %T", repo))
73+
return runtime.Type{}, fmt.Errorf("unsupported repository type %T for get local resource operation", concreteRepo)
4774
}
4875
}
4976

50-
func ChooseAddLocalResourceType(repo runtime.Typed) runtime.Type {
51-
switch repo.(type) {
77+
func ChooseAddLocalResourceType(repo runtime.Typed) (runtime.Type, error) {
78+
concreteRepo, err := convertToConcreteRepo(repo)
79+
if err != nil {
80+
return runtime.Type{}, fmt.Errorf("converting repository spec: %w", err)
81+
}
82+
switch concreteRepo.(type) {
5283
case *oci.Repository:
53-
return ociv1alpha1.OCIAddLocalResourceV1alpha1
84+
return ociv1alpha1.OCIAddLocalResourceV1alpha1, nil
5485
case *ctfv1.Repository:
55-
return ociv1alpha1.CTFAddLocalResourceV1alpha1
86+
return ociv1alpha1.CTFAddLocalResourceV1alpha1, nil
5687
default:
57-
panic(fmt.Sprintf("unknown repository type %T", repo))
88+
return runtime.Type{}, fmt.Errorf("unsupported repository type %T for add local resource operation", concreteRepo)
5889
}
5990
}
6091

cli/cmd/transfer/component-version/internal/localblob.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ import (
1010
"ocm.software/open-component-model/bindings/go/runtime"
1111
transformv1alpha1 "ocm.software/open-component-model/bindings/go/transform/spec/v1alpha1"
1212
"ocm.software/open-component-model/bindings/go/transform/spec/v1alpha1/meta"
13-
"ocm.software/open-component-model/cli/internal/reference/compref"
1413
)
1514

16-
func processLocalBlob(resource descriptorv2.Resource, access *descriptorv2.LocalBlob, id string, ref *compref.Ref, tgd *transformv1alpha1.TransformationGraphDefinition, toSpec runtime.Typed, resourceTransformIDs map[int]string, i int, uploadAsOCIArtifact bool) error {
15+
func processLocalBlob(resource descriptorv2.Resource, _ *descriptorv2.LocalBlob, id string, val *discoveryValue, tgd *transformv1alpha1.TransformationGraphDefinition, toSpec runtime.Typed, resourceTransformIDs map[int]string, i int, uploadAsOCIArtifact bool) error {
16+
component := val.Descriptor.Component.Name
17+
version := val.Descriptor.Component.Version
18+
sourceRepo := val.SourceRepository
19+
1720
// Generate transformation IDs
1821
resourceIdentity := resource.ToIdentity()
1922
resourceID := identityToTransformationID(resourceIdentity)
@@ -26,33 +29,43 @@ func processLocalBlob(resource descriptorv2.Resource, access *descriptorv2.Local
2629
resourceIdentityMap[k] = v
2730
}
2831

32+
getLocalResourceType, err := ChooseGetLocalResourceType(sourceRepo)
33+
if err != nil {
34+
return fmt.Errorf("choosing get local resource type for source repository: %w", err)
35+
}
36+
2937
// Create GetLocalResource transformation
3038
getResourceTransform := transformv1alpha1.GenericTransformation{
3139
TransformationMeta: meta.TransformationMeta{
32-
Type: ChooseGetLocalResourceType(ref.Repository),
40+
Type: getLocalResourceType,
3341
ID: getResourceID,
3442
},
3543
Spec: &runtime.Unstructured{Data: map[string]any{
36-
"repository": AsUnstructured(ref.Repository).Data,
37-
"component": ref.Component,
38-
"version": ref.Version,
44+
"repository": AsUnstructured(sourceRepo).Data,
45+
"component": component,
46+
"version": version,
3947
"resourceIdentity": resourceIdentityMap,
4048
}},
4149
}
4250
tgd.Transformations = append(tgd.Transformations, getResourceTransform)
4351

4452
var addResourceTransform transformv1alpha1.GenericTransformation
4553
if !uploadAsOCIArtifact {
54+
addLocalResourceType, err := ChooseAddLocalResourceType(toSpec)
55+
if err != nil {
56+
return fmt.Errorf("choosing add local resource type for target repository: %w", err)
57+
}
58+
4659
// Create AddLocalResource transformation
4760
addResourceTransform = transformv1alpha1.GenericTransformation{
4861
TransformationMeta: meta.TransformationMeta{
49-
Type: ChooseAddLocalResourceType(toSpec),
62+
Type: addLocalResourceType,
5063
ID: addResourceID,
5164
},
5265
Spec: &runtime.Unstructured{Data: map[string]any{
5366
"repository": AsUnstructured(toSpec).Data,
54-
"component": ref.Component,
55-
"version": ref.Version,
67+
"component": component,
68+
"version": version,
5669
"resource": fmt.Sprintf("${%s.output.resource}", getResourceID),
5770
"file": fmt.Sprintf("${%s.output.file}", getResourceID),
5871
}},

0 commit comments

Comments
 (0)