-
Notifications
You must be signed in to change notification settings - Fork 9
/
rehydrate.go
111 lines (91 loc) · 3.2 KB
/
rehydrate.go
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
111
// Copyright 2022 Namespace Labs Inc; All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
package config
import (
"context"
"github.com/google/go-containerregistry/pkg/v1/remote"
"google.golang.org/protobuf/proto"
"namespacelabs.dev/foundation/internal/artifacts/oci"
"namespacelabs.dev/foundation/internal/artifacts/registry"
"namespacelabs.dev/foundation/internal/fnerrors"
"namespacelabs.dev/foundation/internal/planning"
"namespacelabs.dev/foundation/schema"
"namespacelabs.dev/foundation/std/tasks"
)
const (
envBinaryPb = "config/env.binarypb"
stackBinaryPb = "config/stack.binarypb"
ingressBinaryPb = "config/ingress.binarypb"
computedBinaryPb = "config/computed_configs.binarypb"
)
type Rehydrated struct {
Env *schema.Environment
Stack *schema.Stack
IngressFragments []*schema.IngressFragment
ComputedConfigs *schema.ComputedConfigurations
}
func Rehydrate(ctx context.Context, srv planning.Server, imageID oci.ImageID) (*Rehydrated, error) {
return tasks.Return(ctx, tasks.Action("rehydrate").Scope(srv.PackageName()).Str("ref", imageID), func(ctx context.Context) (*Rehydrated, error) {
// XXX get a cluster's registry instead.
reg, err := registry.GetRegistry(ctx, srv.SealedContext())
if err != nil {
return nil, err
}
ref, remoteOpts, err := oci.ParseRefAndKeychain(ctx, imageID.RepoAndDigest(), reg.Access())
if err != nil {
return nil, fnerrors.New("failed to parse: %w", err)
}
img, err := remote.Image(ref, remoteOpts...)
if err != nil {
return nil, fnerrors.InvocationError("registry", "failed to fetch config image: %w", err)
}
var r Rehydrated
if err := oci.VisitFilesFromImage(img, func(layer, path string, typ byte, contents []byte) error {
switch path {
case envBinaryPb:
r.Env = &schema.Environment{}
if err := proto.Unmarshal(contents, r.Env); err != nil {
return fnerrors.BadInputError("%s: failed to unmarshal: %w", path, err)
}
case stackBinaryPb:
r.Stack = &schema.Stack{}
if err := proto.Unmarshal(contents, r.Stack); err != nil {
return fnerrors.BadInputError("%s: failed to unmarshal: %w", path, err)
}
for _, ep := range r.Stack.Endpoint {
patchEndpoint(ep)
}
case ingressBinaryPb:
list := &schema.IngressFragmentList{}
if err := proto.Unmarshal(contents, list); err != nil {
return fnerrors.BadInputError("%s: failed to unmarshal: %w", path, err)
}
for _, frag := range list.IngressFragment {
patchEndpoint(frag.Endpoint)
}
r.IngressFragments = list.IngressFragment
case computedBinaryPb:
r.ComputedConfigs = &schema.ComputedConfigurations{}
if err := proto.Unmarshal(contents, r.ComputedConfigs); err != nil {
return fnerrors.BadInputError("%s: failed to unmarshal: %w", path, err)
}
}
return nil
}); err != nil {
return nil, err
}
return &r, nil
})
}
func patchEndpoint(ep *schema.Endpoint) {
if ep == nil {
return
}
if ep.DeprecatedPort != nil {
ep.Ports = append(ep.Ports, &schema.Endpoint_PortMap{
Port: ep.DeprecatedPort,
ExportedPort: ep.DeprecatedExportedPort,
})
}
}