Beispiel #1
0
func transformBuildError(err error, baseName, commandName, commandPath string, groups errorGroups) {
	switch t := err.(type) {
	case newapp.ErrNoMatch:
		groups.Add(
			"no-matches",
			heredoc.Docf(`
				The '%[1]s' command will match arguments to the following types:

				  1. Images tagged into image streams in the current project or the 'openshift' project
				     - if you don't specify a tag, we'll add ':latest'
				  2. Images in the Docker Hub, on remote registries, or on the local Docker engine
				  3. Git repository URLs or local paths that point to Git repositories

				--allow-missing-images can be used to force the use of an image that was not matched

				See '%[1]s -h' for examples.`, commandPath,
			),
			t,
			t.Errs...,
		)
		return
	}
	switch err {
	case newcmd.ErrNoInputs:
		groups.Add("", "", usageError(commandPath, newBuildNoInput, baseName, commandName))
		return
	}
	transformError(err, baseName, commandName, commandPath, groups)
}
Beispiel #2
0
func ExampleDocf() {
	libName := "github.com/MakeNowJust/heredoc"
	author := "TSUYUSATO Kitsune (@MakeNowJust)"
	fmt.Printf(heredoc.Docf(`
		Library Name  : %s
		Author        : %s
		Repository URL: http://%s.git
	`, libName, author, libName))
	// Output:
	// Library Name  : github.com/MakeNowJust/heredoc
	// Author        : TSUYUSATO Kitsune (@MakeNowJust)
	// Repository URL: http://github.com/MakeNowJust/heredoc.git
}
Beispiel #3
0
func TestDocf(t *testing.T) {
	// test case
	str := `
		int: %3d
		string: %s
	`
	i := 42
	s := "Hello"
	expect := "int:  42\nstring: Hello\n"

	result := heredoc.Docf(str, i, s)
	if result != expect {
		t.Errorf("test failed: expected=> %#v, result=> %#v", expect, result)
	}
}
Beispiel #4
0
func withFakeCommand(t *testing.T, envValues string, block func()) {
	_exec.Command = func(name string, arg ...string) *exec.Cmd {
		valueFile, _ := ioutil.TempFile("", "valueFile")
		valueFile.WriteString(heredoc.Doc(envValues))
		valueFile.Close()
		dummy, _ := ioutil.TempFile("", "dummy")
		dummy.WriteString(heredoc.Docf(`
			#!/bin/sh
			cat %s
		`, valueFile.Name()))
		dummy.Close()
		t.Logf("dummy: %s", dummy.Name())
		os.Chmod(dummy.Name(), 0755)
		return exec.Command(dummy.Name())
	}
	block()
}
Beispiel #5
0
func transformError(err error, baseName, commandName, commandPath string, groups errorGroups) {
	switch t := err.(type) {
	case newcmd.ErrRequiresExplicitAccess:
		if t.Input.Token != nil && t.Input.Token.ServiceAccount {
			groups.Add(
				"explicit-access-installer",
				heredoc.Doc(`
					WARNING: This will allow the pod to create and manage resources within your namespace -
					ensure you trust the image with those permissions before you continue.

					You can see more information about the image by adding the --dry-run flag.
					If you trust the provided image, include the flag --grant-install-rights.`,
				),
				fmt.Errorf("installing %q requires an 'installer' service account with project editor access", t.Match.Value),
			)
		} else {
			groups.Add(
				"explicit-access-you",
				heredoc.Doc(`
					WARNING: This will allow the pod to act as you across the entire cluster - ensure you
					trust the image with those permissions before you continue.

					You can see more information about the image by adding the --dry-run flag.
					If you trust the provided image, include the flag --grant-install-rights.`,
				),
				fmt.Errorf("installing %q requires that you grant the image access to run with your credentials", t.Match.Value),
			)
		}
		return
	case newapp.ErrNoMatch:
		groups.Add(
			"no-matches",
			heredoc.Docf(`
				The '%[1]s' command will match arguments to the following types:

				  1. Images tagged into image streams in the current project or the 'openshift' project
				     - if you don't specify a tag, we'll add ':latest'
				  2. Images in the Docker Hub, on remote registries, or on the local Docker engine
				  3. Templates in the current project or the 'openshift' project
				  4. Git repository URLs or local paths that point to Git repositories

				--allow-missing-images can be used to point to an image that does not exist yet.

				See '%[1]s -h' for examples.`, commandPath,
			),
			t,
			t.Errs...,
		)
		return
	case newapp.ErrMultipleMatches:
		buf := &bytes.Buffer{}
		for i, match := range t.Matches {

			// If we have more than 5 matches, stop output and recommend searching
			// after the fifth
			if i >= 5 {
				groups.Add(
					"multiple-matches",
					heredoc.Docf(`
						The argument %[1]q could apply to the following Docker images, OpenShift image streams, or templates:

						%[2]sTo view a full list of matches, use '%[3]s %[4]s -S %[1]s'`, t.Value, buf.String(), baseName, commandName,
					),
					t,
					t.Errs...,
				)

				return
			}

			fmt.Fprintf(buf, "* %s\n", match.Description)
			fmt.Fprintf(buf, "  Use %[1]s to specify this image or template\n\n", match.Argument)
		}

		groups.Add(
			"multiple-matches",
			heredoc.Docf(`
					The argument %[1]q could apply to the following Docker images, OpenShift image streams, or templates:

					%[2]s`, t.Value, buf.String(),
			),
			t,
			t.Errs...,
		)
		return
	case newapp.ErrPartialMatch:
		buf := &bytes.Buffer{}
		fmt.Fprintf(buf, "* %s\n", t.Match.Description)
		fmt.Fprintf(buf, "  Use %[1]s to specify this image or template\n\n", t.Match.Argument)

		groups.Add(
			"partial-match",
			heredoc.Docf(`
					The argument %[1]q only partially matched the following Docker image, OpenShift image stream, or template:

					%[2]s`, t.Value, buf.String(),
			),
			t,
			t.Errs...,
		)
		return
	case newapp.ErrNoTagsFound:
		buf := &bytes.Buffer{}
		fmt.Fprintf(buf, "  Use --allow-missing-imagestream-tags to use this image stream\n\n")
		groups.Add(
			"no-tags",
			heredoc.Docf(`
					The image stream %[1]q exists, but it has no tags.

					%[2]s`, t.Match.Name, buf.String(),
			),
			t,
			t.Errs...,
		)
		return
	}
	switch err {
	case errNoTokenAvailable:
		// TODO: improve by allowing token generation
		groups.Add("", "", fmt.Errorf("to install components you must be logged in with an OAuth token (instead of only a certificate)"))
	case newcmd.ErrNoInputs:
		// TODO: suggest things to the user
		groups.Add("", "", usageError(commandPath, newAppNoInput, baseName, commandName))
	default:
		groups.Add("", "", err)
	}
}
Beispiel #6
0
// Generate accepts a path to an app.json file and generates a template from it
func (g *Generator) Generate(body []byte) (*templateapi.Template, error) {
	appJSON := &AppJSON{}
	if err := json.Unmarshal(body, appJSON); err != nil {
		return nil, err
	}

	glog.V(4).Infof("app.json: %#v", appJSON)

	name := g.Name
	if len(name) == 0 && len(g.LocalPath) > 0 {
		name = filepath.Base(g.LocalPath)
	}

	template := &templateapi.Template{}
	template.Name = name
	template.Annotations = make(map[string]string)
	template.Annotations["openshift.io/website"] = appJSON.Website
	template.Annotations["k8s.io/display-name"] = appJSON.Name
	template.Annotations["k8s.io/description"] = appJSON.Description
	template.Annotations["tags"] = strings.Join(appJSON.Keywords, ",")
	template.Annotations["iconURL"] = appJSON.Logo

	// create parameters and environment for containers
	allEnv := make(app.Environment)
	for k, v := range appJSON.Env {
		if v.EnvVar != nil {
			allEnv[k] = fmt.Sprintf("${%s}", k)
		}
	}
	envVars := allEnv.List()
	for _, v := range envVars {
		env := appJSON.Env[v.Name]
		if env.EnvVar == nil {
			continue
		}
		e := env.EnvVar
		displayName := v.Name
		displayName = strings.Join(strings.Split(strings.ToLower(displayName), "_"), " ")
		displayName = strings.ToUpper(displayName[:1]) + displayName[1:]
		param := templateapi.Parameter{
			Name:        v.Name,
			DisplayName: displayName,
			Description: e.Description,
			Value:       e.Value,
		}
		switch e.Generator {
		case "secret":
			param.Generate = "expression"
			param.From = "[a-zA-Z0-9]{14}"
		}
		if len(param.Value) == 0 && e.Default != nil {
			switch t := e.Default.(type) {
			case string:
				param.Value = t
			case float64, float32:
				out, _ := json.Marshal(t)
				param.Value = string(out)
			}
		}
		template.Parameters = append(template.Parameters, param)
	}

	warnings := make(map[string][]string)

	if len(appJSON.Formation) == 0 {
		glog.V(4).Infof("No formation in app.json, adding a default web")
		// TODO: read Procfile for command?
		appJSON.Formation = map[string]Formation{
			"web": {
				Quantity: 1,
			},
		}
		msg := "adding a default formation 'web' with scale 1"
		warnings[msg] = append(warnings[msg], "app.json")
	}

	formations := sets.NewString()
	for k := range appJSON.Formation {
		formations.Insert(k)
	}

	var primaryFormation = "web"
	if _, ok := appJSON.Formation["web"]; !ok || len(appJSON.Formation) == 1 {
		for k := range appJSON.Formation {
			primaryFormation = k
			break
		}
	}

	imageGen := app.NewImageRefGenerator()

	buildPath := appJSON.Repository
	if len(buildPath) == 0 && len(g.LocalPath) > 0 {
		buildPath = g.LocalPath
	}
	if len(buildPath) == 0 {
		return nil, fmt.Errorf("app.json did not contain a repository URL and no local path was specified")
	}

	repo, err := app.NewSourceRepository(buildPath, generate.StrategyDocker)
	if err != nil {
		return nil, err
	}

	var ports []string

	var pipelines app.PipelineGroup
	baseImage := g.BaseImage
	if len(baseImage) == 0 {
		baseImage = appJSON.Image
	}
	if len(baseImage) == 0 {
		return nil, fmt.Errorf("Docker image required: provide an --image flag or 'image' key in app.json")
	}

	fakeDockerfile := heredoc.Docf(`
      # Generated from app.json
      FROM %s
    `, baseImage)

	dockerfilePath := filepath.Join(buildPath, "Dockerfile")
	if df, err := app.NewDockerfileFromFile(dockerfilePath); err == nil {
		repo.Info().Dockerfile = df
		repo.Info().Path = dockerfilePath
		ports = dockerfile.LastExposedPorts(df.AST())
	}
	// TODO: look for procfile for more info?

	image, err := imageGen.FromNameAndPorts(baseImage, ports)
	if err != nil {
		return nil, err
	}
	image.AsImageStream = true
	image.TagDirectly = true
	image.ObjectName = name
	image.Tag = "from"

	pipeline, err := app.NewPipelineBuilder(name, nil, false).To(name).NewBuildPipeline(name, image, repo)
	if err != nil {
		return nil, err
	}

	// TODO: this should not be necessary
	pipeline.Build.Source.Name = name
	pipeline.Build.Source.DockerfileContents = fakeDockerfile
	pipeline.Name = name
	pipeline.Image.ObjectName = name
	glog.V(4).Infof("created pipeline %+v", pipeline)

	pipelines = append(pipelines, pipeline)

	var errs []error

	// create deployments for each formation
	var group app.PipelineGroup
	for _, component := range formations.List() {
		componentName := fmt.Sprintf("%s-%s", name, component)
		if formations.Len() == 1 {
			componentName = name
		}
		formationName := component
		formation := appJSON.Formation[component]

		inputImage := pipelines[0].Image

		inputImage.ContainerFn = func(c *kapi.Container) {
			for _, s := range ports {
				if port, err := strconv.Atoi(s); err == nil {
					c.Ports = append(c.Ports, kapi.ContainerPort{ContainerPort: int32(port)})
				}
			}
			if len(formation.Command) > 0 {
				c.Args = []string{formation.Command}
			} else {
				msg := "no command defined, defaulting to command in the Procfile"
				warnings[msg] = append(warnings[msg], formationName)
				c.Args = []string{"/bin/sh", "-c", fmt.Sprintf("$(grep %s Procfile | cut -f 2 -d :)", formationName)}
			}
			c.Env = append(c.Env, envVars...)

			c.Resources = resourcesForProfile(formation.Size)
		}

		pipeline, err := app.NewPipelineBuilder(componentName, nil, true).To(componentName).NewImagePipeline(componentName, inputImage)
		if err != nil {
			errs = append(errs, err)
			break
		}

		if err := pipeline.NeedsDeployment(nil, nil, false); err != nil {
			return nil, err
		}

		if cmd, ok := appJSON.Scripts["postdeploy"]; ok && primaryFormation == component {
			pipeline.Deployment.PostHook = &app.DeploymentHook{Shell: cmd}
			delete(appJSON.Scripts, "postdeploy")
		}

		group = append(group, pipeline)
	}
	if err := group.Reduce(); err != nil {
		return nil, err
	}
	pipelines = append(pipelines, group...)

	if len(errs) > 0 {
		return nil, utilerrs.NewAggregate(errs)
	}

	acceptors := app.Acceptors{app.NewAcceptUnique(kapi.Scheme), app.AcceptNew}
	objects := app.Objects{}
	accept := app.NewAcceptFirst()
	for _, p := range pipelines {
		accepted, err := p.Objects(accept, acceptors)
		if err != nil {
			return nil, fmt.Errorf("can't setup %q: %v", p.From, err)
		}
		objects = append(objects, accepted...)
	}

	// create services for each object with a name based on alias.
	var services []*kapi.Service
	for _, obj := range objects {
		switch t := obj.(type) {
		case *deployapi.DeploymentConfig:
			ports := app.UniqueContainerToServicePorts(app.AllContainerPorts(t.Spec.Template.Spec.Containers...))
			if len(ports) == 0 {
				continue
			}
			svc := app.GenerateService(t.ObjectMeta, t.Spec.Selector)
			svc.Spec.Ports = ports
			services = append(services, svc)
		}
	}
	for _, svc := range services {
		objects = append(objects, svc)
	}

	template.Objects = objects

	// generate warnings
	warnUnusableAppJSONElements("app.json", appJSON, warnings)
	if len(warnings) > 0 {
		allWarnings := sets.NewString()
		for msg, services := range warnings {
			allWarnings.Insert(fmt.Sprintf("%s: %s", strings.Join(services, ","), msg))
		}
		if template.Annotations == nil {
			template.Annotations = make(map[string]string)
		}
		template.Annotations[app.GenerationWarningAnnotation] = fmt.Sprintf("not all app.json fields were honored:\n* %s", strings.Join(allWarnings.List(), "\n* "))
	}

	return template, nil
}
Beispiel #7
0
// FindRestartingPods inspects all Pods to see if they've restarted more than the threshold. logsCommandName is the name of
// the command that should be invoked to see pod logs. securityPolicyCommandPattern is a format string accepting two replacement
// variables for fmt.Sprintf - 1, the namespace of the current pod, 2 the service account of the pod.
func FindRestartingPods(g osgraph.Graph, f osgraph.Namer, logsCommandName, securityPolicyCommandPattern string) []osgraph.Marker {
	markers := []osgraph.Marker{}

	for _, uncastPodNode := range g.NodesByKind(kubegraph.PodNodeKind) {
		podNode := uncastPodNode.(*kubegraph.PodNode)
		pod, ok := podNode.Object().(*kapi.Pod)
		if !ok {
			continue
		}

		for _, containerStatus := range pod.Status.ContainerStatuses {
			switch {
			case containerCrashLoopBackOff(containerStatus):
				var suggestion string
				switch {
				case containerIsNonRoot(pod, containerStatus.Name):
					suggestion = heredoc.Docf(`
						The container is starting and exiting repeatedly. This usually means the container is unable
						to start, misconfigured, or limited by security restrictions. Check the container logs with

						  %s %s -c %s

						Current security policy prevents your containers from being run as the root user. Some images
						may fail expecting to be able to change ownership or permissions on directories. Your admin
						can grant you access to run containers that need to run as the root user with this command:

						  %s
						`, logsCommandName, pod.Name, containerStatus.Name, fmt.Sprintf(securityPolicyCommandPattern, pod.Namespace, pod.Spec.ServiceAccountName))
				default:
					suggestion = heredoc.Docf(`
						The container is starting and exiting repeatedly. This usually means the container is unable
						to start, misconfigured, or limited by security restrictions. Check the container logs with

						  %s %s -c %s
						`, logsCommandName, pod.Name, containerStatus.Name)
				}
				markers = append(markers, osgraph.Marker{
					Node: podNode,

					Severity: osgraph.ErrorSeverity,
					Key:      CrashLoopingPodError,
					Message: fmt.Sprintf("container %q in %s is crash-looping", containerStatus.Name,
						f.ResourceName(podNode)),
					Suggestion: osgraph.Suggestion(suggestion),
				})
			case containerRestartedRecently(containerStatus, nowFn()):
				markers = append(markers, osgraph.Marker{
					Node: podNode,

					Severity: osgraph.WarningSeverity,
					Key:      RestartingPodWarning,
					Message: fmt.Sprintf("container %q in %s has restarted within the last 10 minutes", containerStatus.Name,
						f.ResourceName(podNode)),
				})
			case containerRestartedFrequently(containerStatus):
				markers = append(markers, osgraph.Marker{
					Node: podNode,

					Severity: osgraph.WarningSeverity,
					Key:      RestartingPodWarning,
					Message: fmt.Sprintf("container %q in %s has restarted %d times", containerStatus.Name,
						f.ResourceName(podNode), containerStatus.RestartCount),
				})
			}
		}
	}

	return markers
}
Beispiel #8
0
// Shortcut heredoc.Docf
func Df(raw string, args ...interface{}) string {
	return heredoc.Docf(raw, args...)
}