// Copyright Istio 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 // // http://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 istioio import ( "fmt" "os" "path" "path/filepath" "strings" "istio.io/istio/pkg/test/env" "istio.io/istio/pkg/test/scopes" "istio.io/istio/pkg/test/framework" ) const ( snippetsFileExtension = ".snippets.txt" snippetsFileHeaderFormat = `# Created by %s. DO NOT EDIT THIS FILE MANUALLY! ` ) // Step builds a step of the test pipeline. type Step interface { // run this step. run(ctx Context) } // Builder builds a test of a documented workflow from http://istio.io. type Builder struct { snippetsFileName string steps []Step cleanupSteps []Step } // NewBuilder returns an instance of an example test. The name of the snippets file must be provided. // If the snippets file name does not end with ".snippets.txt", the extension will be appended automatically. func NewBuilder(snippetsFileName string) *Builder { if snippetsFileName == "" { panic("must provide the snippets file name") } // Add the appropriate suffix if it's missing. if !strings.HasSuffix(snippetsFileName, snippetsFileExtension) { snippetsFileName += snippetsFileExtension } return &Builder{ snippetsFileName: snippetsFileName, } } // Add a step to be run. func (b *Builder) Add(steps ...Step) *Builder { b.steps = append(b.steps, steps...) return b } // Defer registers a function to be executed when the test completes. func (b *Builder) Defer(steps ...Step) *Builder { b.cleanupSteps = append(b.cleanupSteps, steps...) return b } // BuildAndRun is a utility method for building and running the test function in one step. func (b *Builder) BuildAndRun(ctx framework.TestContext) { b.Build()(ctx) } // Build a run function for the test func (b *Builder) Build() func(ctx framework.TestContext) { return func(ctx framework.TestContext) { scopes.CI.Infof("Executing test %s (%d steps)", ctx.Name(), len(b.steps)) snippetFile, err := os.Create(filepath.Join(ctx.WorkDir(), b.snippetsFileName)) if err != nil { ctx.Fatalf("failed creating snippets file: %v", err) } defer func() { _ = snippetFile.Close() }() // Write the header to the snippets file. if _, err := snippetFile.WriteString(fmt.Sprintf(snippetsFileHeaderFormat, ctx.Name())); err != nil { ctx.Fatalf("failed writing header to snippets file: %v", err) } eCtx := Context{ TestContext: ctx, snippetFile: snippetFile, snippetMap: make(map[string]string), } // create a symbolic link to samples/, for easy access samplesSymlink := path.Join(ctx.WorkDir(), "samples") if _, err := os.Stat(samplesSymlink); os.IsNotExist(err) { err = os.Symlink(path.Join(env.IstioSrc, "samples"), samplesSymlink) if err != nil { scopes.CI.Warnf("Could not create symlink to samples/ directory at %s", samplesSymlink) } else { defer func() { _ = os.Remove(samplesSymlink) }() } } // Run cleanup functions at the end. defer func() { for _, step := range b.cleanupSteps { step.run(eCtx) } }() for _, step := range b.steps { step.run(eCtx) } } }