Documentation ¶
Index ¶
- Constants
- Variables
- func RegistryAuthFromEnv(imageName string) (*types.AuthConfig, error)
- type BuildArg
- type BuildContext
- type BuildOptions
- type BuildSpec
- func (b *BuildSpec) DependsOnFiles(files []string, manifestPath string) (bool, error)
- func (b *BuildSpec) GetBaseImage(rootPath string) (string, error)
- func (b *BuildSpec) GetDockerfilePath(rootPath string) string
- func (b *BuildSpec) ReadDockerfile(rootPath string) (string, error)
- func (b *BuildSpec) Verify(manifestPath string, log logger.Logger) error
- type BuildStatus
- type ChartSpec
- type Command
- type DeployOptions
- type Directory
- type DockerEnvSpec
- type DockerVolumeSpec
- type Entity
- type EnvSpec
- type File
- type KubernetesEnvSpec
- type Module
- func (m *Module) Build(options *BuildOptions) (err error)
- func (m *Module) CachedDigest(resource string) (string, error)
- func (m *Module) DependsOnFiles(files []string) (bool, error)
- func (m *Module) Deploy(options *DeployOptions) (string, error)
- func (m *Module) Dir() string
- func (m *Module) GetAffectedModules(files []string) ([]*Module, error)
- func (m *Module) HasEnvironment() bool
- func (m *Module) ListImages() ([]string, error)
- func (m *Module) RestartContainers(restartImages []string, verbose bool, kubeContext string) error
- func (m *Module) RunTests(options *TestOptions, log logger.Logger) error
- func (m *Module) RunTests2(options *TestOptions, log logger.Logger) error
- func (m *Module) Status() BuildStatus
- func (m *Module) UpdateResources(kubeContext string, verbose, force bool) error
- func (m *Module) WaitForCompletion() error
- func (m *Module) WaitForReady(kubeContext, repository, tag string) error
- type ModuleSpec
- type Process
- type TestOptions
- type TestSpec
- type Variable
- type WaitingMessage
Constants ¶
View Source
const FILE_SYNC_HASH_LIMIT = 100 * 1000
Files over this size will be hashed asynchronously.
Variables ¶
View Source
var DefaultTag = "latest"
View Source
var ErrMissingChartSource = fmt.Errorf("missing chart source")
View Source
var ErrMissingImageName = fmt.Errorf("missing image name")
View Source
var ErrMissingReleaseName = fmt.Errorf("missing release name")
View Source
var ErrModuleNotCached = fmt.Errorf("module is not cached")
View Source
var ErrMultipleChartSources = fmt.Errorf("multiple chart sources not allowed")
View Source
var ErrMultipleTestEnv = fmt.Errorf("multiple test environments defined")
View Source
var ErrNoEnvironment = fmt.Errorf("no environment in spec")
View Source
var ErrNoTestEnv = fmt.Errorf("no test environment")
View Source
var ErrNoTests = fmt.Errorf("no tests configured")
func (t *TestSpec) runKubernetes(
rootPath string, options *TestOptions, spec *ModuleSpec, restartImages []string, log logger.Logger, ) error { isKind := options.Kind != "" || options.Transient var client *kubernetes.Clientset var kubeContext string image := sanitizeImageName(options.Repository, t.Build.Name, "latest") imagePullPolicy := corev1.PullAlways if isKind { cli, err := dockerclient.NewClientWithOpts(client.FromEnv) if err != nil { return err } name := options.Kind if name == "" { name = "test-" + uuid.New().String()[:8] } provider := cluster.NewProvider() exists := false if !options.Transient { clusters, err := provider.List() if err != nil { return err } for _, cluster := range clusters { if cluster == options.Kind { exists = true break } } } if exists { log.Info("Using existing kind cluster", zap.String("name", options.Kind)) } else { log := log.With( zap.String("name", name), zap.Bool("transient", options.Transient), ) log.Info("Creating cluster") ready := make(chan int, 1) go func() { start := time.Now() for { select { case <-time.After(5 * time.Second): log.Info("Still creating cluster", zap.String("elapsed", time.Since(start).String())) case <-ready: return } } }() kindConfig := generateKindConfig("kind-registry", 5000) err := provider.Create(name, cluster.CreateWithRawConfig([]byte(kindConfig))) ready <- 0 if err != nil { return fmt.Errorf("create cluster: %v", err) } if options.Transient { defer func() { log.Info("Deleting transient cluster") if err := func() error { if err := provider.Delete( name, "", ); err != nil { return err } return nil }(); err != nil { log.Error("Error cleaning up transient cluster", zap.String("message", err.Error())) } else { log.Info("Deleted transient cluster") } }() } } client, kubeContext, err = clientForKindCluster(name, provider) if err != nil { return err } if err := waitForCluster(client, log); err != nil { return err } if !options.SkipBuild { if options.NoRegistry { imagePullPolicy = corev1.PullNever images, err := getSpecImages(spec, rootPath) if err != nil { return err } images = append(images, image) if err := loadImagesOnCluster( images, name, provider, options.Concurrency, log, ); err != nil { return err } } else { if err := registry.EnsureLocalRegistryRunning(cli, log); err != nil { return err } log := log.With(zap.String("image", image)) log.Info("Pushing image to local registry") if err := cli.NetworkConnect( context.TODO(), "kind", "kind-registry", &networktypes.EndpointSettings{}, ); err != nil && !strings.Contains(err.Error(), "Error response from daemon: endpoint with name kind-registry already exists in network kind") { return err } resp, err := cli.ImagePush( context.TODO(), image, types.ImagePushOptions{ RegistryAuth: "this_can_be_anything", }, ) if err != nil { return err } termFd, isTerm := term.GetFdInfo(os.Stderr) if err := jsonmessage.DisplayJSONMessagesStream( resp, os.Stderr, termFd, isTerm, nil, ); err != nil { return fmt.Errorf("push: %v", err) } log.Info("Pushed image") } } } else if options.Context != "" { // Use existing kubernetes context from ~/.kube/config var err error kubeContext = options.Context client, _, err = util.ClientsetForContext(options.Context) if err != nil { return err } // TODO: push image to registry } else { // We didn't specify an existing cluster and we didn't // request a transient cluster. It's unclear where the // user is expecting these tests to run. return ErrUnknownCluster } start := time.Now() log.Debug("Checking RBAC...") if err := createTestRBAC(client, log); err != nil { return err } if err := applyManifests( kubeContext, rootPath, t.Env.Kubernetes.Resources, ); err != nil { return err } if err := t.installCharts( rootPath, kubeContext, options, log, ); err != nil { return err } //log.Info("Restarting deployments", zap.String("restartImages", fmt.Sprintf("%#v", restartImages))) //if err := restartDeployments(client, restartImages); err != nil { // return err //} namespace := "default" pods := client.CoreV1().Pods(namespace) log = log.With( zap.String("t.Name", t.Name), zap.String("namespace", namespace), zap.String("image", image), ) if err := deleteOldPods(pods, t.Name, log); err != nil { return err } // Wait for the rest of the the cluster to be Ready if err := waitForFullReady(client, log); err != nil { return err } log.Debug("Creating test pod") podName := t.Name + "-" + uuid.New().String()[:8] var env []corev1.EnvVar for _, v := range t.Variables { env = append(env, corev1.EnvVar{ Name: v.Name, Value: v.Value, }) } pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: podName, Namespace: namespace, Labels: map[string]string{ "kb": t.Name, }, }, Spec: corev1.PodSpec{ ServiceAccountName: "kb-test", RestartPolicy: corev1.RestartPolicyNever, Containers: []corev1.Container{{ Name: t.Name, Image: image, ImagePullPolicy: imagePullPolicy, Command: t.Build.Command, Env: env, }}, }, } var err error if pod, err = pods.Create( context.TODO(), pod, metav1.CreateOptions{}, ); err != nil { return err } log.Debug("Created pod") if !options.Transient { defer func() { log.Warn("TODO: clean up pod") }() } timeout := 90 * time.Second delay := time.Second start = time.Now() scheduled := false for deadline := time.Now().Add(timeout); time.Now().Before(deadline); { pod, err = pods.Get( context.TODO(), podName, metav1.GetOptions{}, ) if err != nil { return err } switch pod.Status.Phase { case corev1.PodPending: if !scheduled { for _, condition := range pod.Status.Conditions { if condition.Status == "PodScheduled" { deadline = time.Now().Add(30 * time.Second) scheduled = true } } } for _, status := range pod.Status.ContainerStatuses { if status.State.Terminated != nil { if code := status.State.Terminated.ExitCode; code != 0 { return fmt.Errorf("pod failed with exit code '%d'", code) } return nil } if status.State.Waiting != nil { if strings.Contains(status.State.Waiting.Reason, "Err") { return fmt.Errorf("pod failed with '%s'", status.State.Waiting.Reason) } } } log.Info("Still waiting on pod", zap.String("phase", string(pod.Status.Phase)), zap.Bool("scheduled", scheduled), zap.String("elapsed", time.Since(start).String()), zap.String("timeout", timeout.String())) time.Sleep(delay) continue case corev1.PodRunning: fallthrough case corev1.PodSucceeded: fallthrough case corev1.PodFailed: for _, status := range pod.Status.ContainerStatuses { if status.State.Terminated != nil { if strings.Contains(status.State.Terminated.Reason, "Err") { return ErrTestFailed } } } req := pods.GetLogs(pod.Name, &corev1.PodLogOptions{ Follow: true, }) r, err := req.Stream(context.TODO()) if err != nil { return err } rd := bufio.NewReader(r) for { message, err := rd.ReadString('\n') if err != nil { if err == io.EOF { break } return err } //log.Info("Test output", zap.String("message", message)) //fmt.Println(message) } default: return fmt.Errorf("unexpected phase '%s'", pod.Status.Phase) } if pod, err = pods.Get( context.TODO(), pod.Name, metav1.GetOptions{}, ); err != nil { return err } if pod.Status.Phase == corev1.PodRunning { time.Sleep(delay) log.Warn("Log stream terminated prematurely. Retailing logs...") continue } else if pod.Status.Phase == corev1.PodSucceeded { return nil } else if pod.Status.Phase == corev1.PodFailed { // This should NOT happen. Container terminated status // should exist if the phase is Failed. return ErrTestFailed } else { return fmt.Errorf("unexpected pod phase '%s'", pod.Status.Phase) } } return ErrPodTimeout }
View Source
var ErrPodTimeout = fmt.Errorf("pod timed out")
View Source
var ErrTestFailed = fmt.Errorf("test failed")
View Source
var ErrUnknownCluster = fmt.Errorf("unknown cluster")
Functions ¶
func RegistryAuthFromEnv ¶
func RegistryAuthFromEnv(imageName string) (*types.AuthConfig, error)
Types ¶
type BuildContext ¶
func (BuildContext) Archive ¶
func (c BuildContext) Archive() ([]byte, error)
func (BuildContext) Digest ¶
func (c BuildContext) Digest(include *gogitignore.GitIgnore) (string, error)
type BuildOptions ¶
type BuildOptions struct { NoCache bool `json:"nocache,omitempty" yaml:"nocache,omitempty"` Squash bool `json:"squash,omitempty" yaml:"squash,omitempty"` Tag string `json:"tag,omitempty" yaml:"tag,omitempty"` Builder string `json:"builder,omitempty" yaml:"builder,omitempty"` NoPush bool `json:"noPush,omitempty" yaml:"noPush,omitempty"` NoPushDeps bool `json:"noPushDeps,omitempty" yaml:"noPushDeps,omitempty"` Repository string `json:"repository,omitempty" yaml:"repository,omitempty"` Context string `json:"context,omitempty" yaml:"context,omitempty"` Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` Force bool `json:"force,omitempty"` // If true, will always run docker build regardless of kb digest SkipHooks bool `json:"skipHooks,omitempty"` // If true, skip before/after build hooks Verbose bool `json:"verbose,omitempty" yaml:"verbose,omitempty"` BuildArgs []string `json:"buildArgs,omitempty" yaml:"buildArgs,omitempty"` Platform string `json:"platform,omitempty" yaml:"platform,omitempty"` Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"` Progress string `json:"progress,omitempty" yaml:"progress,omitempty"` }
type BuildSpec ¶
type BuildSpec struct { Name string `json:"name" yaml:"name"` Dockerfile string `json:"dockerfile,omitempty" yaml:"dockerfile,omitempty"` Context string `json:"context,omitempty" yaml:"context,omitempty"` Platform string `json:"platform,omitempty" yaml:"platform,omitempty"` BuildArgs []*BuildArg `json:"buildArgs,omitempty" yaml:"buildArgs,omitempty"` Target string `json:"target,omitempty" yaml:"target,omitempty"` DefaultTag string `json:"defaultTag,omitempty" yaml:"defaultTag,omitempty"` TagPrefix string `json:"tagPrefix,omitempty" yaml:"tagPrefix,omitempty"` TagSuffix string `json:"tagSuffix,omitempty" yaml:"tagSuffix,omitempty"` Command []string `json:"command,omitempty" yaml:"command,omitempty"` SkipPush bool `json:"skipPush,omitempty" yaml:"skipPush,omitempty"` NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"` Before []Command `json:"before,omitempty" yaml:"before,omitempty"` After []Command `json:"after,omitempty" yaml:"after,omitempty"` }
func (*BuildSpec) DependsOnFiles ¶
DependsOnFiles all inputs are absolute paths
func (*BuildSpec) GetDockerfilePath ¶
func (*BuildSpec) ReadDockerfile ¶
type BuildStatus ¶
type BuildStatus int32
const ( BuildStatusPending BuildStatus = 0 BuildStatusInProgress BuildStatus = 1 BuildStatusFailed BuildStatus = 2 BuildStatusSucceeded BuildStatus = 3 )
func (BuildStatus) String ¶
func (b BuildStatus) String() string
type ChartSpec ¶
type ChartSpec struct { ReleaseName string `json:"releaseName" yaml:"releaseName"` Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` RepoURL string `json:"repoURL,omitempty" yaml:"repoURL,omitempty"` TargetRevision string `json:"targetRevision,omitempty" yaml:"targetRevision,omitempty"` Values map[interface{}]interface{} `json:"values,omitempty" yaml:"values,omitempty"` ValuesFiles []string `json:"valuesFiles,omitempty" yaml:"valuesFiles,omitempty"` }
type DeployOptions ¶
type DeployOptions struct { Kind string `json:"kind"` KubeContext string `json:"kubeContext"` Repository string `json:"repository"` Tag string `json:"tag"` NoAutoRestart bool `json:"noAutoRestart"` RestartImages []string `json:"restartImages"` Wait bool `json:"wait"` Verbose bool `json:"verbose,omitempty"` Force bool `json:"force,omitempty"` }
type DockerEnvSpec ¶
type DockerEnvSpec struct {
Volumes []*DockerVolumeSpec `json:"volumes,omitempty"`
}
func (*DockerEnvSpec) Verify ¶
func (d *DockerEnvSpec) Verify(manifestPath string) error
type DockerVolumeSpec ¶
type EnvSpec ¶
type EnvSpec struct { Kubernetes *KubernetesEnvSpec `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty"` Docker *DockerEnvSpec `json:"docker,omitempty" yaml:"docker,omitempty"` }
type KubernetesEnvSpec ¶
type Module ¶
type Module struct { Spec *ModuleSpec Path string Dependencies []*Module // BuiltImages []string // contains filtered or unexported fields }
func (*Module) Build ¶
func (m *Module) Build(options *BuildOptions) (err error)
func (*Module) GetAffectedModules ¶
func (*Module) HasEnvironment ¶
func (*Module) ListImages ¶
func (*Module) RestartContainers ¶
func (*Module) RunTests2 ¶
func (m *Module) RunTests2(options *TestOptions, log logger.Logger) error
func (*Module) Status ¶
func (m *Module) Status() BuildStatus
func (*Module) UpdateResources ¶
func (*Module) WaitForCompletion ¶
func (*Module) WaitForReady ¶
type ModuleSpec ¶
type ModuleSpec struct { Dependencies []string `json:"dependencies,omitempty" yaml:"dependencies,omitempty"` Build *BuildSpec `json:"build" yaml:"build"` Env EnvSpec `json:"env,omitempty" yaml:"env,omitempty"` Test []*TestSpec `json:"test,omitempty" yaml:"test,omitempty"` }
func (*ModuleSpec) RunTests ¶
func (s *ModuleSpec) RunTests( options *TestOptions, manifestPath string, p *Process, log logger.Logger, ) error
type Process ¶
type Process struct {
// contains filtered or unexported fields
}
func (*Process) GetModuleFromBuildSpec ¶
type TestOptions ¶
type TestOptions struct { BuildOptions KubeContext string `json:"kubeContext,omitempty"` Kind string `json:"kind,omitempty"` Transient bool `json:"transient,omitempty"` Namespace string `json:"namespace,omitempty"` SkipBuild bool `json:"skipBuild,omitempty"` SkipTestBuild bool `json:"skipTestBuild,omitempty"` SkipDeploy bool `json:"skipDeploy,omitempty"` Timeout string `json:"timeout,omitempty"` Args []string `json:"args,omitempty"` TestImagePullPolicy string `json:"testImagePullPolicy,omitempty"` }
type TestSpec ¶
type TestSpec struct { Name string `json:"name"` Build *BuildSpec `json:"build"` Variables []*Variable `json:"variables,omitempty" yaml:"variables,omitempty"` Env EnvSpec `json:"env,omitempty" yaml:"env,omitempty"` DefaultTimeout string `json:"defaultTimeout,omitempty" yaml:"defaultTimeout,omitempty"` }
type WaitingMessage ¶
type WaitingMessage struct {
// contains filtered or unexported fields
}
func NewWaitingMessage ¶
func (*WaitingMessage) Stop ¶
func (w *WaitingMessage) Stop()
Click to show internal directories.
Click to hide internal directories.