blob: 1e93b9da094e9f753f4c62ad932d71dea524068b [file] [log] [blame]
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you 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 setup
import (
"fmt"
"io/ioutil"
"os"
"time"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"github.com/apache/skywalking-infra-e2e/internal/config"
"github.com/apache/skywalking-infra-e2e/internal/constant"
"github.com/apache/skywalking-infra-e2e/internal/logger"
"github.com/apache/skywalking-infra-e2e/internal/util"
)
func RunStepsAndWait(steps []config.Step, timeout int, k8sCluster *util.K8sClusterInfo) error {
var waitTimeout time.Duration
if timeout <= 0 {
waitTimeout = constant.DefaultWaitTimeout
} else {
waitTimeout = time.Duration(timeout) * time.Second
}
logger.Log.Debugf("wait timeout is %d seconds", int(waitTimeout.Seconds()))
// record time now
timeNow := time.Now()
for _, step := range steps {
logger.Log.Infof("processing setup step [%s]", step.Name)
if step.Path != "" && step.Command == "" {
if k8sCluster == nil {
return fmt.Errorf("not support path")
}
manifest := config.Manifest{
Path: step.Path,
Waits: step.Waits,
}
err := createManifestAndWait(k8sCluster.Client, k8sCluster.Interface, manifest, waitTimeout)
if err != nil {
return err
}
} else if step.Command != "" && step.Path == "" {
command := config.Run{
Command: step.Command,
Waits: step.Waits,
}
err := RunCommandsAndWait(command, waitTimeout)
if err != nil {
return err
}
} else {
return fmt.Errorf("step parameter error, one Path or one Command should be specified, but got %+v", step)
}
waitTimeout = NewTimeout(timeNow, waitTimeout)
timeNow = time.Now()
if waitTimeout <= 0 {
return fmt.Errorf("setup timeout")
}
}
return nil
}
// createManifestAndWait creates manifests in k8s cluster and concurrent waits according to the manifests' wait conditions.
func createManifestAndWait(c *kubernetes.Clientset, dc dynamic.Interface, manifest config.Manifest, timeout time.Duration) error {
waitSet := util.NewWaitSet(timeout)
kubeConfigYaml, err := ioutil.ReadFile(kubeConfigPath)
if err != nil {
return err
}
waits := manifest.Waits
err = createByManifest(c, dc, manifest)
if err != nil {
return err
}
// len() for nil slices is defined as zero
if len(waits) == 0 {
logger.Log.Info("no wait-for strategy is provided")
return nil
}
for idx := range waits {
wait := waits[idx]
logger.Log.Infof("waiting for %+v", wait)
options, err := getWaitOptions(kubeConfigYaml, &wait)
if err != nil {
return err
}
waitSet.WaitGroup.Add(1)
go concurrentlyWait(&wait, options, waitSet)
}
go func() {
waitSet.WaitGroup.Wait()
close(waitSet.FinishChan)
}()
select {
case <-waitSet.FinishChan:
logger.Log.Infof("create and wait for manifest ready success")
case err := <-waitSet.ErrChan:
logger.Log.Errorf("failed to wait for manifest to be ready")
return err
case <-time.After(waitSet.Timeout):
return fmt.Errorf("wait for manifest ready timeout after %d seconds", int(timeout.Seconds()))
}
return nil
}
// RunCommandsAndWait Concurrently run commands and wait for conditions.
func RunCommandsAndWait(run config.Run, timeout time.Duration) error {
waitSet := util.NewWaitSet(timeout)
commands := run.Command
if len(commands) < 1 {
return nil
}
waitSet.WaitGroup.Add(1)
go executeCommandsAndWait(commands, run.Waits, waitSet)
go func() {
waitSet.WaitGroup.Wait()
close(waitSet.FinishChan)
}()
select {
case <-waitSet.FinishChan:
logger.Log.Infof("all commands executed successfully")
case err := <-waitSet.ErrChan:
logger.Log.Errorf("execute command error")
return err
case <-time.After(waitSet.Timeout):
return fmt.Errorf("wait for commands run timeout after %d seconds", int(timeout.Seconds()))
}
return nil
}
func executeCommandsAndWait(commands string, waits []config.Wait, waitSet *util.WaitSet) {
defer waitSet.WaitGroup.Done()
// executes commands
logger.Log.Infof("executing commands [%s]", commands)
result, err := util.ExecuteCommand(commands)
if err != nil {
err = fmt.Errorf("commands: [%s] runs error: %s", commands, err)
waitSet.ErrChan <- err
}
logger.Log.Infof("executed commands [%s], result: %s", commands, result)
// waits for conditions meet
for idx := range waits {
wait := waits[idx]
logger.Log.Infof("waiting for %+v", wait)
kubeConfigYaml, err := ioutil.ReadFile(kubeConfigPath)
if err != nil {
err = fmt.Errorf("read kube config failed: %s", err)
waitSet.ErrChan <- err
}
options, err := getWaitOptions(kubeConfigYaml, &wait)
if err != nil {
err = fmt.Errorf("commands: [%s] get wait options error: %s", commands, err)
waitSet.ErrChan <- err
}
err = options.RunWait()
if err != nil {
err = fmt.Errorf("commands: [%s] waits error: %s", commands, err)
waitSet.ErrChan <- err
return
}
logger.Log.Infof("wait %+v condition met", wait)
}
}
// NewTimeout calculates new timeout since timeBefore.
func NewTimeout(timeBefore time.Time, timeout time.Duration) time.Duration {
elapsed := time.Since(timeBefore)
newTimeout := timeout - elapsed
return newTimeout
}
func GetIdentity() string {
runID := os.Getenv("GITHUB_RUN_ID")
if runID == "" {
return "skywalking_e2e"
}
return runID
}