-
Notifications
You must be signed in to change notification settings - Fork 13
/
platform.go
275 lines (219 loc) · 7.99 KB
/
platform.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package libcnb
import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
"github.com/buildpacks/libcnb/v2/internal"
)
const (
// BindingProvider is the key for a binding's provider.
BindingProvider = "provider"
// BindingType is the key for a binding's type.
BindingType = "type"
// EnvServiceBindings is the name of the environment variable that contains the path to service bindings directory.
//
// See the Service Binding Specification for Kubernetes for more details - https://k8s-service-bindings.github.io/spec/
EnvServiceBindings = "SERVICE_BINDING_ROOT"
// EnvBuildpackDirectory is the name of the environment variable that contains the path to the buildpack
EnvBuildpackDirectory = "CNB_BUILDPACK_DIR"
// EnvExtensionDirectory is the name of the environment variable that contains the path to the extension
EnvExtensionDirectory = "CNB_EXTENSION_DIR"
// EnvVcapServices is the name of the environment variable that contains the bindings in cloudfoundry
EnvVcapServices = "VCAP_SERVICES"
// EnvLayersDirectory is the name of the environment variable that contains the root path to all buildpack layers
EnvLayersDirectory = "CNB_LAYERS_DIR"
// EnvOutputDirectory is the name of the environment variable that contains the path to the output directory
EnvOutputDirectory = "CNB_OUTPUT_DIR"
// EnvPlatformDirectory is the name of the environment variable that contains the path to the platform directory
EnvPlatformDirectory = "CNB_PLATFORM_DIR"
// EnvDetectBuildPlanPath is the name of the environment variable that contains the path to the build plan
EnvDetectPlanPath = "CNB_BUILD_PLAN_PATH"
// EnvBuildPlanPath is the name of the environment variable that contains the path to the build plan
EnvBuildPlanPath = "CNB_BP_PLAN_PATH"
// Deprecated: EnvStackID is the name of the environment variable that contains the stack id
EnvStackID = "CNB_STACK_ID"
// EnvTargetOS contains the name of the os
EnvTargetOS = "CNB_TARGET_OS"
// EnvTargetArch contains the architecture
EnvTargetArch = "CNB_TARGET_ARCH"
// EnvTargetOS contains the variant of the architecture
EnvTargetArchVariant = "CNB_TARGET_ARCH_VARIANT"
// EnvTargetDistroName contains the name of the ditro
EnvTargetDistroName = "CNB_TARGET_DISTRO_NAME"
// EnvTargetDistroVersion contains the version of the distro
EnvTargetDistroVersion = "CNB_TARGET_DISTRO_VERSION"
// DefaultPlatformBindingsLocation is the typical location for bindings, which exists under the platform directory
//
// Not guaranteed to exist, but often does. This should only be used as a fallback if EnvServiceBindings and EnvPlatformDirectory are not set
DefaultPlatformBindingsLocation = "/platform/bindings"
)
// Binding is a projection of metadata about an external entity to be bound to.
type Binding struct {
// Name is the name of the binding
Name string
// Path is the path to the binding directory.
Path string
// Type is the type of the binding.
Type string
// Provider is the optional provider of the binding.
Provider string
// Secret is the secret of the binding.
Secret map[string]string
}
// NewBinding creates a new Binding initialized with a secret.
func NewBinding(name string, path string, secret map[string]string) Binding {
b := Binding{
Name: name,
Path: path,
Secret: make(map[string]string),
}
for k, v := range secret {
switch k {
case BindingType:
b.Type = strings.TrimSpace(v)
case BindingProvider:
b.Provider = strings.TrimSpace(v)
default:
b.Secret[k] = strings.TrimSpace(v)
}
}
return b
}
// NewBindingFromPath creates a new binding from the files located at a path.
func NewBindingFromPath(path string) (Binding, error) {
secret, err := internal.NewConfigMapFromPath(path)
if err != nil {
return Binding{}, fmt.Errorf("unable to create new config map from %s\n%w", path, err)
}
return NewBinding(filepath.Base(path), path, secret), nil
}
func (b Binding) String() string {
var s []string
for k := range b.Secret {
s = append(s, k)
}
sort.Strings(s)
return fmt.Sprintf("{Name: %s Path: %s Type: %s Provider: %s Secret: %s}",
b.Name, b.Path, b.Type, b.Provider, s)
}
// SecretFilePath return the path to a secret file with the given name.
func (b Binding) SecretFilePath(name string) (string, bool) {
if _, ok := b.Secret[name]; !ok {
return "", false
}
return filepath.Join(b.Path, name), true
}
// Bindings is a collection of bindings keyed by their name.
type Bindings []Binding
// NewBindingsFromPath creates a new instance from all the bindings at a given path.
func NewBindingsFromPath(path string) (Bindings, error) {
files, err := os.ReadDir(path)
if err != nil && errors.Is(err, fs.ErrNotExist) {
return Bindings{}, nil
} else if err != nil {
return nil, fmt.Errorf("unable to list directory %s\n%w", path, err)
}
bindings := Bindings{}
for _, file := range files {
bindingPath := filepath.Join(path, file.Name())
if strings.HasPrefix(filepath.Base(bindingPath), ".") {
// ignore hidden files
continue
}
binding, err := NewBindingFromPath(bindingPath)
if err != nil {
return nil, fmt.Errorf("unable to create new binding from %s\n%w", file, err)
}
bindings = append(bindings, binding)
}
return bindings, nil
}
type vcapServicesBinding struct {
Name string `json:"name"`
Label string `json:"label"`
Credentials map[string]interface{} `json:"credentials"`
}
func toJSONString(input interface{}) (string, error) {
switch in := input.(type) {
case string:
return in, nil
default:
jsonProperty, err := json.Marshal(in)
if err != nil {
return "", err
}
return string(jsonProperty), nil
}
}
// NewBindingsFromVcapServicesEnv creates a new instance from all the bindings given from the VCAP_SERVICES.
func NewBindingsFromVcapServicesEnv(content string) (Bindings, error) {
var contentTyped map[string][]vcapServicesBinding
err := json.Unmarshal([]byte(content), &contentTyped)
if err != nil {
return Bindings{}, err
}
bindings := Bindings{}
for p, bArray := range contentTyped {
for _, b := range bArray {
secret := map[string]string{}
for k, v := range b.Credentials {
secret[k], err = toJSONString(v)
if err != nil {
return nil, err
}
}
bindings = append(bindings, Binding{
Name: b.Name,
Type: b.Label,
Provider: p,
Secret: secret,
})
}
}
return bindings, nil
}
// NewBindings creates a new bindings from all the bindings at the path defined by $SERVICE_BINDING_ROOT.
// If that isn't defined, bindings are read from <platform>/bindings.
// If that isn't defined, bindings are read from $VCAP_SERVICES.
// If that isn't defined, the specified platform path will be used
func NewBindings(platformDir string) (Bindings, error) {
if path, ok := os.LookupEnv(EnvServiceBindings); ok {
return NewBindingsFromPath(path)
}
if path, ok := os.LookupEnv(EnvPlatformDirectory); ok {
return NewBindingsFromPath(filepath.Join(path, "bindings"))
}
if content, ok := os.LookupEnv(EnvVcapServices); ok {
return NewBindingsFromVcapServicesEnv(content)
}
return NewBindingsFromPath(filepath.Join(platformDir, "bindings"))
}
// Platform is the contents of the platform directory.
type Platform struct {
// Bindings are the external bindings available to the application.
Bindings Bindings
// Environment is the environment exposed by the platform.
Environment map[string]string
// Path is the path to the platform.
Path string
}