/
newe2e_task_runazcopy.go
254 lines (206 loc) · 8.21 KB
/
newe2e_task_runazcopy.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
package e2etest
import (
"fmt"
"github.com/Azure/azure-storage-azcopy/v10/common"
"os"
"os/exec"
"reflect"
"strings"
)
// AzCopyJobPlan todo probably load the job plan directly? WI#26418256
type AzCopyJobPlan struct{}
// AzCopyStdout shouldn't be used or relied upon right now! This will be fleshed out eventually. todo WI#26418258
type AzCopyStdout struct {
RawOutput []string
}
func (a *AzCopyStdout) Write(p []byte) (n int, err error) {
str := string(p)
lines := strings.Split(str, "\n")
a.RawOutput = append(a.RawOutput, lines...)
return len(p), nil
}
func (a *AzCopyStdout) String() string {
return strings.Join(a.RawOutput, "\n")
}
type AzCopyVerb string
const ( // initially supporting a limited set of verbs
AzCopyVerbCopy AzCopyVerb = "copy"
AzCopyVerbSync AzCopyVerb = "sync"
AzCopyVerbRemove AzCopyVerb = "remove"
)
type AzCopyTarget struct {
ResourceManager
AuthType ExplicitCredentialTypes // Expects *one* credential type that the Resource supports. Assumes SAS (or GCP/S3) if not present.
Opts CreateAzCopyTargetOptions
// todo: SAS permissions
// todo: specific OAuth types (e.g. MSI, etc.)
}
type CreateAzCopyTargetOptions struct {
// SASTokenOptions expects a GenericSignatureValues, which can contain account signatures, or a service signature.
SASTokenOptions GenericSignatureValues
Scheme string
}
func CreateAzCopyTarget(rm ResourceManager, authType ExplicitCredentialTypes, a Asserter, opts ...CreateAzCopyTargetOptions) AzCopyTarget {
var validTypes ExplicitCredentialTypes
if rrm, ok := rm.(RemoteResourceManager); ok {
validTypes = rrm.ValidAuthTypes()
}
if validTypes != EExplicitCredentialType.None() {
a.AssertNow(fmt.Sprintf("expected only one auth type, got %s", authType), Equal{}, authType.Count(), 1)
a.AssertNow(fmt.Sprintf("expected authType to be contained within valid types (got %s, needed %s)", authType, validTypes), Equal{}, validTypes.Includes(authType), true)
} else {
a.AssertNow("Expected no auth types", Equal{}, authType, EExplicitCredentialType.None())
}
return AzCopyTarget{rm, authType, FirstOrZero(opts)}
}
type AzCopyCommand struct {
Verb AzCopyVerb
// Passing a ResourceManager assumes SAS (or GCP/S3) auth is intended.
// Passing an AzCopyTarget will allow you to specify an exact credential type.
// When OAuth, S3, GCP, AcctKey, etc. the appropriate env flags should auto-populate.
Targets []ResourceManager
Flags any // check SampleFlags
Environment *AzCopyEnvironment
ShouldFail bool
}
type AzCopyEnvironment struct {
// `env:"XYZ"` is re-used but does not inherit the traits of config's env trait. Merely used for low-code mapping.
LogLocation *string `env:"AZCOPY_LOG_LOCATION,defaultfunc:DefaultLogLoc"`
JobPlanLocation *string `env:"AZCOPY_JOB_PLAN_LOCATION,defaultfunc:DefaultPlanLoc"`
AutoLoginMode *string `env:"AZCOPY_AUTO_LOGIN_TYPE"`
AutoLoginTenantID *string `env:"AZCOPY_TENANT_ID"`
ServicePrincipalAppID *string `env:"AZCOPY_SPA_APPLICATION_ID"`
ServicePrincipalClientSecret *string `env:"AZCOPY_SPA_CLIENT_SECRET"`
InheritEnvironment bool
}
func (env *AzCopyEnvironment) generateAzcopyDir(a ScenarioAsserter) {
dir, err := os.MkdirTemp("", "azcopytests*")
a.NoError("create tempdir", err)
env.LogLocation = &dir
env.JobPlanLocation = &dir
a.Cleanup(func(a ScenarioAsserter) {
err := os.RemoveAll(dir)
a.NoError("remove tempdir", err)
})
}
func (env *AzCopyEnvironment) DefaultLogLoc(a ScenarioAsserter) string {
if env.JobPlanLocation != nil {
env.LogLocation = env.JobPlanLocation
} else if env.LogLocation == nil {
env.generateAzcopyDir(a)
}
return *env.LogLocation
}
func (env *AzCopyEnvironment) DefaultPlanLoc(a ScenarioAsserter) string {
if env.LogLocation != nil {
env.JobPlanLocation = env.LogLocation
} else if env.JobPlanLocation == nil {
env.generateAzcopyDir(a)
}
return *env.JobPlanLocation
}
func (c *AzCopyCommand) applyTargetAuth(a Asserter, target ResourceManager) string {
intendedAuthType := EExplicitCredentialType.SASToken()
var opts GetURIOptions
if tgt, ok := target.(AzCopyTarget); ok {
count := tgt.AuthType.Count()
a.AssertNow("target auth type must be single", Equal{}, count <= 1, true)
if count == 1 {
intendedAuthType = tgt.AuthType
}
opts.AzureOpts.SASValues = tgt.Opts.SASTokenOptions
opts.RemoteOpts.Scheme = tgt.Opts.Scheme
} else if target.Location() == common.ELocation.S3() {
intendedAuthType = EExplicitCredentialType.S3()
} else if target.Location() == common.ELocation.GCP() {
intendedAuthType = EExplicitCredentialType.GCP()
}
switch intendedAuthType {
case EExplicitCredentialType.PublicAuth(), EExplicitCredentialType.None():
return target.URI() // no SAS, no nothing.
case EExplicitCredentialType.SASToken():
opts.AzureOpts.WithSAS = true
return target.URI(opts)
case EExplicitCredentialType.OAuth():
// Only set it if it wasn't already configured. If it was manually configured,
// special testing may be occurring, and this may be indicated to just get a SAS-less URI.
// Alternatively, we may have already configured it here once before.
if c.Environment.AutoLoginMode == nil && c.Environment.ServicePrincipalAppID == nil && c.Environment.ServicePrincipalClientSecret == nil && c.Environment.AutoLoginTenantID == nil {
c.Environment.AutoLoginMode = pointerTo("SPN") // TODO! There are two other modes for this. These probably can't apply in automated scenarios, but it's worth having tests for that we run before every release! WI#26625161
if GlobalConfig.StaticResources() {
oAuthInfo := GlobalConfig.E2EAuthConfig.StaticStgAcctInfo.StaticOAuth
a.AssertNow("At least NEW_E2E_STATIC_APPLICATION_ID and NEW_E2E_STATIC_CLIENT_SECRET must be specified to use OAuth.", Empty{true}, oAuthInfo.ApplicationID, oAuthInfo.ClientSecret)
c.Environment.ServicePrincipalAppID = &oAuthInfo.ApplicationID
c.Environment.ServicePrincipalClientSecret = &oAuthInfo.ClientSecret
c.Environment.AutoLoginTenantID = common.Iff(oAuthInfo.TenantID != "", &oAuthInfo.TenantID, nil)
} else {
// oauth should reliably work
oAuthInfo := GlobalConfig.E2EAuthConfig.SubscriptionLoginInfo
c.Environment.ServicePrincipalAppID = &oAuthInfo.ApplicationID
c.Environment.ServicePrincipalClientSecret = &oAuthInfo.ClientSecret
c.Environment.AutoLoginTenantID = common.Iff(oAuthInfo.TenantID != "", &oAuthInfo.TenantID, nil)
}
}
return target.URI() // Generate like public
default:
a.Error("unsupported credential type")
return target.URI()
}
}
// RunAzCopy todo define more cleanly, implement
func RunAzCopy(a ScenarioAsserter, commandSpec AzCopyCommand) (*AzCopyStdout, *AzCopyJobPlan) {
if a.Dryrun() {
return nil, &AzCopyJobPlan{}
}
// separate these from the struct so their execution order is fixed
args := func() []string {
if commandSpec.Environment == nil {
commandSpec.Environment = &AzCopyEnvironment{}
}
out := []string{GlobalConfig.AzCopyExecutableConfig.ExecutablePath, string(commandSpec.Verb)}
for _, v := range commandSpec.Targets {
out = append(out, commandSpec.applyTargetAuth(a, v))
}
if commandSpec.Flags != nil {
flags := MapFromTags(reflect.ValueOf(commandSpec.Flags), "flag", a)
for k, v := range flags {
out = append(out, fmt.Sprintf("--%s=%s", k, v))
}
}
return out
}()
env := func() []string {
out := make([]string, 0)
env := MapFromTags(reflect.ValueOf(commandSpec.Environment), "env", a)
for k, v := range env {
out = append(out, fmt.Sprintf("%s=%s", k, v))
}
if commandSpec.Environment.InheritEnvironment {
out = append(out, os.Environ()...)
}
return out
}()
out := &AzCopyStdout{}
command := exec.Cmd{
Path: GlobalConfig.AzCopyExecutableConfig.ExecutablePath,
Args: args,
Env: env,
Stdout: out, // todo
}
in, err := command.StdinPipe()
a.NoError("get stdin pipe", err)
err = command.Start()
a.Assert("run command", IsNil{}, err)
if isLaunchedByDebugger {
beginAzCopyDebugging(in)
}
err = command.Wait()
a.Assert("wait for finalize", IsNil{}, err)
a.Assert("expected exit code",
common.Iff[Assertion](commandSpec.ShouldFail, Not{Equal{}}, Equal{}),
0, command.ProcessState.ExitCode())
if err != nil {
a.Log("AzCopy output:\n%s", out.String())
}
return out, &AzCopyJobPlan{}
}