-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathrun.go
More file actions
165 lines (147 loc) · 5.64 KB
/
Copy pathrun.go
File metadata and controls
165 lines (147 loc) · 5.64 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
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
// Copyright func-e contributors
// SPDX-License-Identifier: Apache-2.0
package api
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"time"
"github.com/tetratelabs/func-e/internal/envoy"
"github.com/tetratelabs/func-e/internal/globals"
"github.com/tetratelabs/func-e/internal/version"
)
// EnsurePatchVersion ensures we either have a valid version.PatchVersion or an error
// If remote lookup of the latest patch fails, this logs and falls back to the last installed one
// NOTE: Warnings and errors include the platform because a release isn't available at the same time for all platforms.
func EnsurePatchVersion(ctx context.Context, o *globals.GlobalOpts, v version.Version) (version.PatchVersion, error) {
if mv, ok := v.(version.MinorVersion); ok {
o.Logf("looking up the latest patch for Envoy version %s\n", mv)
evs, err := o.GetEnvoyVersions(ctx)
var patchVersions []version.PatchVersion
if err == nil {
patchVersions = versionsForPlatform(evs.Versions, o.Platform)
if pv := version.FindLatestPatchVersion(patchVersions, mv); pv != "" {
return pv, nil
}
err = fmt.Errorf("%s does not contain an Envoy release for version %s on platform %s", o.EnvoyVersionsURL, mv, o.Platform)
}
// Attempt the last installed version instead of raising an error. There may not be one!
if rows, e := getInstalledVersions(o.HomeDir); e == nil {
for _, r := range rows {
patchVersions = append(patchVersions, r.version)
}
if pv := version.FindLatestPatchVersion(patchVersions, mv); pv != "" {
o.Logf("couldn't look up an Envoy release for version %s on platform %s: using last installed version\n", mv, o.Platform)
return pv, nil
}
}
return "", err
} // version.Version is a union type, so the only other option is a patch!
vv, ok := v.(version.PatchVersion)
if !ok {
panic(fmt.Sprintf("unexpected version type %T", v))
}
return vv, nil
}
// Run runs Envoy with the given arguments.
// Returns nil when Envoy exits cleanly, including when interrupted by signals (SIGINT/SIGTERM).
// This matches Envoy's behavior of returning exit code 0 on graceful shutdown.
func Run(ctx context.Context, o *globals.GlobalOpts, args []string) error {
if err := initializeRunOpts(ctx, o); err != nil {
return err
}
r := envoy.NewRuntime(&o.RunOpts, o.Logf)
stdoutLog, err := os.OpenFile(filepath.Join(r.GetRunDir(), "stdout.log"), os.O_CREATE|os.O_WRONLY, 0o600)
if err != nil {
return fmt.Errorf("couldn't create stdout log file: %w", err)
}
defer stdoutLog.Close() //nolint
r.OutFile = stdoutLog
r.Out = io.MultiWriter(o.EnvoyOut, stdoutLog)
stderrLog, err := os.OpenFile(filepath.Join(r.GetRunDir(), "stderr.log"), os.O_CREATE|os.O_WRONLY, 0o600)
if err != nil {
return fmt.Errorf("couldn't create stderr log file: %w", err)
}
defer stderrLog.Close() //nolint
r.ErrFile = stderrLog
r.Err = io.MultiWriter(o.EnvoyErr, stderrLog)
return r.Run(ctx, args)
}
// setEnvoyVersion makes sure the $FUNC_E_HOME/version exists.
func setEnvoyVersion(ctx context.Context, o *globals.GlobalOpts) (err error) {
var v version.Version
if v, _, err = envoy.CurrentVersion(o.HomeDir); err != nil {
return err
} else if v != nil { // We found an existing version, but it might be in MinorVersion format!
o.EnvoyVersion, err = EnsurePatchVersion(ctx, o, v)
return err
}
// First time install: look up the latest version, which may be newer than version.LastKnownEnvoy!
o.Logf("looking up the latest Envoy version\n")
var evs *version.ReleaseVersions
if evs, err = o.GetEnvoyVersions(ctx); err != nil {
return fmt.Errorf("couldn't lookup the latest Envoy version from %s: %w", o.EnvoyVersionsURL, err)
}
o.EnvoyVersion = version.FindLatestVersion(versionsForPlatform(evs.Versions, o.Platform))
if o.EnvoyVersion == "" {
return fmt.Errorf("%s does not contain an Envoy release for platform %s", o.EnvoyVersionsURL, o.Platform)
}
// Persist it as a minor version, so that each invocation checks for the latest patch.
return envoy.WriteCurrentVersion(o.EnvoyVersion.ToMinor(), o.HomeDir)
}
// initializeRunOpts initializes the api options
func initializeRunOpts(ctx context.Context, o *globals.GlobalOpts) error {
runOpts := &o.RunOpts
if o.EnvoyPath == "" { // not overridden for tests
envoyPath, err := envoy.InstallIfNeeded(ctx, o)
if err != nil {
return err
}
o.EnvoyPath = envoyPath
}
if runOpts.RunDir == "" { // not overridden for tests
runID := strconv.FormatInt(time.Now().UnixNano(), 10)
runDir := filepath.Join(filepath.Join(o.HomeDir, "runs"), runID)
// Eagerly create the run dir, so that errors raise early
if err := os.MkdirAll(runDir, 0o750); err != nil {
return fmt.Errorf("validation error: unable to create working directory %q, so we cannot run envoy", runDir)
}
runOpts.RunDir = runDir
}
return nil
}
func versionsForPlatform(vs map[version.PatchVersion]version.Release, p version.Platform) []version.PatchVersion {
var patchVersions []version.PatchVersion
for k, v := range vs {
if _, ok := v.Tarballs[p]; ok {
patchVersions = append(patchVersions, k)
}
}
return patchVersions
}
type versionReleaseDate struct {
version version.PatchVersion
releaseDate version.ReleaseDate
}
func getInstalledVersions(homeDir string) ([]versionReleaseDate, error) {
var rows []versionReleaseDate
files, err := os.ReadDir(filepath.Join(homeDir, "versions"))
if os.IsNotExist(err) {
return rows, nil
} else if err != nil {
return nil, err
}
for _, f := range files {
pv := version.NewPatchVersion(f.Name())
if i, err := f.Info(); f.IsDir() && pv != "" && err == nil {
rows = append(rows, versionReleaseDate{
pv,
version.ReleaseDate(i.ModTime().Format("2006-01-02")),
})
}
}
return rows, nil
}