-
Notifications
You must be signed in to change notification settings - Fork 11
/
application.go
190 lines (151 loc) · 4.78 KB
/
application.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
package winfsinjector
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
yaml "gopkg.in/yaml.v2"
)
var (
readFile = os.ReadFile
removeAll = os.RemoveAll
)
type Application struct {
injector injector
releaseCreator releaseCreator
zipper zipper
}
//go:generate counterfeiter -o ./fakes/file_info.go --fake-name FileInfo os.FileInfo
//go:generate counterfeiter -o ./fakes/injector.go --fake-name Injector . injector
type injector interface {
AddReleaseToMetadata(releasePath, releaseName, releaseVersion, extractedTileDir string) error
}
//go:generate counterfeiter -o ./fakes/zipper.go --fake-name Zipper . zipper
type zipper interface {
Zip(dir, zipFile string) error
Unzip(zipFile, dest string) error
}
//go:generate counterfeiter -o ./fakes/release_creator.go --fake-name ReleaseCreator . releaseCreator
type releaseCreator interface {
CreateRelease(releaseName, imageName, releaseDir, tarballPath, imageTag, registry, version string) error
}
func NewApplication(releaseCreator releaseCreator, injector injector, zipper zipper) Application {
return Application{
injector: injector,
releaseCreator: releaseCreator,
zipper: zipper,
}
}
func (a Application) Run(inputTile, outputTile, registry, workingDir string) error {
if inputTile == "" {
return errors.New("--input-tile is required")
}
if outputTile == "" {
return errors.New("--output-tile is required")
}
extractedTileDir := filepath.Join(workingDir, "extracted-tile")
err := a.zipper.Unzip(inputTile, extractedTileDir)
if err != nil {
return err
}
injectedReleaseTarball := filepath.Join(extractedTileDir, "releases/windows*fs*")
matches, _ := filepath.Glob(injectedReleaseTarball)
if len(matches) > 0 {
fmt.Println("File system has already been injected in the tile; skipping injection")
return nil
}
embeddedReleaseDir := filepath.Join(extractedTileDir, "embed/windowsfs-release")
if _, err := os.Stat(embeddedReleaseDir); os.IsNotExist(err) {
fmt.Println("No file system found; skipping injection")
return nil
}
releaseVersion, err := a.extractReleaseVersion(embeddedReleaseDir)
if err != nil {
return err
}
if runtime.GOOS == "windows" {
cmd := exec.Command("git", "config", "core.filemode", "false")
cmd.Dir = embeddedReleaseDir
stdoutStderr, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("unable to fix file permissions for windows: %s, %s", stdoutStderr, err)
}
cmd = exec.Command("git", "submodule", "foreach", "git", "config", "core.filemode", "false")
cmd.Dir = embeddedReleaseDir
stdoutStderr, err = cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("unable to fix file permissions for windows: %s, %s", stdoutStderr, err)
}
}
releaseName, err := a.extractReleaseName(embeddedReleaseDir)
if err != nil {
return err
}
imageName := "cloudfoundry/windows2016fs"
imageTag, err := a.determineImageTag(embeddedReleaseDir)
if err != nil {
return err
}
tarballPath := filepath.Join(extractedTileDir, "releases", fmt.Sprintf("%s-%s.tgz", releaseName, releaseVersion))
err = a.releaseCreator.CreateRelease(releaseName, imageName, embeddedReleaseDir, tarballPath, imageTag, registry, releaseVersion)
if err != nil {
return err
}
err = a.injector.AddReleaseToMetadata(tarballPath, releaseName, releaseVersion, extractedTileDir)
if err != nil {
return err
}
err = removeAll(embeddedReleaseDir)
if err != nil {
return err
}
return a.zipper.Zip(extractedTileDir, outputTile)
}
func (a Application) extractReleaseVersion(releaseDir string) (string, error) {
rawReleaseVersion, err := readFile(filepath.Join(releaseDir, "VERSION"))
if err != nil {
return "", err
}
return strings.TrimSuffix(string(rawReleaseVersion), "\n"), nil
}
func (a Application) extractReleaseName(releaseDir string) (string, error) {
contents, err := readFile(filepath.Join(releaseDir, "config", "final.yml"))
if err != nil {
return "", err
}
type NameFile struct {
Name string `yaml:"name"`
}
var f NameFile
err = yaml.Unmarshal(contents, &f)
if err != nil {
return "", err
}
return f.Name, nil
}
func (a Application) determineImageTag(releaseDir string) (string, error) {
var (
blobs = map[string]interface{}{}
blobPath = filepath.Join(releaseDir, "config", "blobs.yml")
blobPattern = regexp.MustCompile(`windows.*fs\/windows.*fs-(\d+\.\d+\.\d+)\.tgz`)
)
data, err := readFile(blobPath)
if err != nil {
return "", err
}
err = yaml.Unmarshal(data, &blobs)
if err != nil {
return "", err
}
for key, _ := range blobs {
matches := blobPattern.FindStringSubmatch(key)
if len(matches) == 2 {
return matches[1], nil
}
}
return "", errors.New("unable to parse tag from embedded rootfs: Please confirm that you are using the appropriate winfs-injector version for this tile")
}