-
Notifications
You must be signed in to change notification settings - Fork 274
Expand file tree
/
Copy pathpull.go
More file actions
110 lines (94 loc) · 2.96 KB
/
Copy pathpull.go
File metadata and controls
110 lines (94 loc) · 2.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package catalognext
import (
"context"
"errors"
"fmt"
"time"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
"github.com/docker/mcp-gateway/pkg/db"
"github.com/docker/mcp-gateway/pkg/oci"
"github.com/docker/mcp-gateway/pkg/telemetry"
"github.com/docker/mcp-gateway/pkg/workingset"
)
func Pull(ctx context.Context, dao db.DAO, ociService oci.Service, refStr string) error {
telemetry.Init()
start := time.Now()
var success bool
defer func() {
duration := time.Since(start)
telemetry.RecordCatalogOperation(ctx, "pull", refStr, float64(duration.Milliseconds()), success)
}()
catalog, err := pullCatalog(ctx, dao, ociService, refStr)
if err != nil {
return err
}
fmt.Printf("Catalog %s pulled\n", catalog.Ref)
success = true
return nil
}
func pullCatalog(ctx context.Context, dao db.DAO, ociService oci.Service, refStr string) (*db.Catalog, error) {
ref, err := name.ParseReference(refStr)
if err != nil {
return nil, fmt.Errorf("failed to parse OCI reference %s: %w", refStr, err)
}
source := oci.FullName(ref)
catalogArtifact, err := oci.ReadArtifact[CatalogArtifact](refStr, MCPCatalogArtifactType)
if err != nil {
if isNotFoundError(err) {
return nil, fmt.Errorf("catalog not found: %s", refStr)
}
return nil, fmt.Errorf("failed to read OCI catalog: %w", err)
}
catalog := Catalog{
CatalogArtifact: catalogArtifact,
Ref: oci.FullNameWithoutDigest(ref),
Source: SourcePrefixOCI + source,
}
// Resolve any unresolved snapshots first
for i := range len(catalog.Servers) {
if catalog.Servers[i].Snapshot != nil {
continue
}
switch catalog.Servers[i].Type {
case workingset.ServerTypeImage:
serverSnapshot, err := workingset.ResolveImageSnapshot(ctx, ociService, catalog.Servers[i].Image)
if err != nil {
return nil, fmt.Errorf("failed to resolve image snapshot: %w", err)
}
catalog.Servers[i].Snapshot = serverSnapshot
case workingset.ServerTypeRegistry:
// TODO(cody): Ignore until supported
}
}
if err := catalog.Validate(); err != nil {
return nil, fmt.Errorf("invalid catalog: %w", err)
}
dbCatalog, err := catalog.ToDb()
if err != nil {
return nil, fmt.Errorf("failed to convert catalog to db: %w", err)
}
err = dao.UpsertCatalog(ctx, dbCatalog)
if err != nil {
return nil, fmt.Errorf("failed to create catalog: %w", err)
}
err = dao.RecordPull(ctx, refStr)
if err != nil {
return nil, fmt.Errorf("failed to record pull record: %w", err)
}
return &dbCatalog, nil
}
// isNotFoundError checks if the error is an OCI registry "not found" response
// (MANIFEST_UNKNOWN or NAME_UNKNOWN).
func isNotFoundError(err error) bool {
var transportErr *transport.Error
if !errors.As(err, &transportErr) {
return false
}
for _, diagnostic := range transportErr.Errors {
if diagnostic.Code == transport.ManifestUnknownErrorCode || diagnostic.Code == transport.NameUnknownErrorCode {
return true
}
}
return false
}