-
Notifications
You must be signed in to change notification settings - Fork 4
/
generate.go
180 lines (140 loc) · 5.18 KB
/
generate.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
package ubinodejsextension
import (
"bytes"
_ "embed"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"text/template"
"github.com/Masterminds/semver/v3"
"github.com/paketo-buildpacks/libnodejs"
"github.com/paketo-buildpacks/packit/v2"
"github.com/paketo-buildpacks/packit/v2/draft"
postal "github.com/paketo-buildpacks/packit/v2/postal"
"github.com/paketo-buildpacks/packit/v2/scribe"
)
var PACKAGES = "make gcc gcc-c++ libatomic_ops git openssl-devel nodejs npm nodejs-nodemon nss_wrapper which python3"
var DEFAULT_USER_ID = 1002
var DEFAULT_GROUP_ID = 1000
type DuringBuildPermissions struct {
CNB_USER_ID, CNB_GROUP_ID int
}
//go:embed templates/build.Dockerfile
var buildDockerfileTemplate string
type BuildDockerfileProps struct {
NODEJS_VERSION uint64
CNB_USER_ID, CNB_GROUP_ID int
CNB_STACK_ID, PACKAGES string
}
//go:embed templates/run.Dockerfile
var runDockerfileTemplate string
type RunDockerfileProps struct {
Source string
}
//go:generate faux --interface DependencyManager --output fakes/dependency_manager.go
type DependencyManager interface {
Resolve(path, id, version, stack string) (postal.Dependency, error)
Deliver(dependency postal.Dependency, cnbPath, layerPath, platformPath string) error
GenerateBillOfMaterials(dependencies ...postal.Dependency) []packit.BOMEntry
}
func Generate(dependencyManager DependencyManager, logger scribe.Emitter, duringBuildPermissions DuringBuildPermissions) packit.GenerateFunc {
return func(context packit.GenerateContext) (packit.GenerateResult, error) {
logger.Title("%s %s", context.Info.Name, context.Info.Version)
logger.Process("Resolving Node Engine version")
// Find the version with the highest priority
entryResolver := draft.NewPlanner()
highestPriorityNodeVersion, allNodeVersionsInPriorityOrder := libnodejs.ResolveNodeVersion(entryResolver.Resolve, context.Plan)
if highestPriorityNodeVersion.Name == "" {
return packit.GenerateResult{}, packit.Fail.WithMessage("Node.js no longer requested by build plan")
}
logger.Candidates(allNodeVersionsInPriorityOrder)
// Search and fetch the version from the extension.toml
nodeVersion, _ := highestPriorityNodeVersion.Metadata["version"].(string)
extensionFilePath := filepath.Join(context.CNBPath, "extension.toml")
dependency, err := dependencyManager.Resolve(extensionFilePath, highestPriorityNodeVersion.Name, nodeVersion, context.Stack)
if err != nil {
return packit.GenerateResult{}, err
}
selectedNodeVersion, err := semver.NewVersion(dependency.Version)
if err != nil {
return packit.GenerateResult{}, err
}
selectedNodeMajorVersion := selectedNodeVersion.Major()
var selectedNodeRunImage string
bpNodeRunExtension, bpNodeRunExtensionEnvExists := os.LookupEnv("BP_UBI_RUN_IMAGE_OVERRIDE")
if !bpNodeRunExtensionEnvExists || bpNodeRunExtension == "" {
selectedNodeRunImage = dependency.Source
} else {
logger.Process("Using run image specified by BP_UBI_RUN_IMAGE_OVERRIDE %s", bpNodeRunExtension)
selectedNodeRunImage = bpNodeRunExtension
}
logger.Process("Selected Node Engine Major version %d", selectedNodeMajorVersion)
// These variables have to be fetched from the env
CNB_STACK_ID := os.Getenv("CNB_STACK_ID")
// Generating build.Dockerfile
buildDockerfileContent, err := FillPropsToTemplate(BuildDockerfileProps{
NODEJS_VERSION: selectedNodeMajorVersion,
CNB_USER_ID: duringBuildPermissions.CNB_USER_ID,
CNB_GROUP_ID: duringBuildPermissions.CNB_GROUP_ID,
CNB_STACK_ID: CNB_STACK_ID,
PACKAGES: PACKAGES,
}, buildDockerfileTemplate)
if err != nil {
return packit.GenerateResult{}, err
}
// Generating run.Dockerfile
runDockerfileContent, err := FillPropsToTemplate(RunDockerfileProps{
Source: selectedNodeRunImage,
}, runDockerfileTemplate)
if err != nil {
return packit.GenerateResult{}, err
}
return packit.GenerateResult{
ExtendConfig: packit.ExtendConfig{Build: packit.ExtendImageConfig{Args: []packit.ExtendImageConfigArg{}}},
BuildDockerfile: strings.NewReader(buildDockerfileContent),
RunDockerfile: strings.NewReader(runDockerfileContent),
}, nil
}
}
func FillPropsToTemplate(properties interface{}, templateString string) (result string, Error error) {
templ, err := template.New("template").Parse(templateString)
if err != nil {
return "", err
}
var buf bytes.Buffer
err = templ.Execute(&buf, properties)
if err != nil {
panic(err)
}
return buf.String(), nil
}
func GetDuringBuildPermissions(filepath string) DuringBuildPermissions {
defaultPermissions := DuringBuildPermissions{
CNB_USER_ID: DEFAULT_USER_ID,
CNB_GROUP_ID: DEFAULT_GROUP_ID,
}
re := regexp.MustCompile(`cnb:x:(\d+):(\d+)::`)
etcPasswdFile, err := os.ReadFile(filepath)
if err != nil {
return defaultPermissions
}
etcPasswdContent := string(etcPasswdFile)
matches := re.FindStringSubmatch(etcPasswdContent)
if len(matches) != 3 {
return defaultPermissions
}
CNB_USER_ID, err := strconv.Atoi(matches[1])
if err != nil {
return defaultPermissions
}
CNB_GROUP_ID, err := strconv.Atoi(matches[2])
if err != nil {
return defaultPermissions
}
return DuringBuildPermissions{
CNB_USER_ID: CNB_USER_ID,
CNB_GROUP_ID: CNB_GROUP_ID,
}
}