<commit_before>/**
 * Licensed to the 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.  The 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 builder

import (
	"fmt"
	"os"
	"path/filepath"

	"mynewt.apache.org/newt/newt/project"
	"mynewt.apache.org/newt/util"
)

func (b *Builder) Download() error {
	if b.target.App() == nil {
		return util.NewNewtError("app package not specified")
	}

	/*
	 * Populate the package list and feature sets.
	 */
	err := b.PrepBuild()
	if err != nil {
		return err
	}

	if b.Bsp.DownloadScript == "" {
		/*
		 *
		 */
		return nil
	}

	bspPath := b.Bsp.BasePath()
	downloadScript := filepath.Join(bspPath, b.Bsp.DownloadScript)
	binBaseName := b.AppBinBasePath()
	featureString := b.FeatureString()

	downloadCmd := fmt.Sprintf("%s %s %s %s",
		downloadScript, bspPath, binBaseName, featureString)

	rsp, err := util.ShellCommand(downloadCmd)
	fmt.Printf("%s", rsp)

	return err
}

func (b *Builder) Debug() error {
	if b.target.App() == nil {
		return util.NewNewtError("app package not specified")
	}

	/*
	 * Populate the package list and feature sets.
	 */
	err := b.PrepBuild()
	if err != nil {
		return err
	}

	bspPath := b.Bsp.BasePath()
	debugScript := filepath.Join(bspPath, b.Bsp.DebugScript)
	binBaseName := b.AppBinBasePath()
	featureString := b.FeatureString()

	os.Chdir(project.GetProject().Path())

	cmdLine := []string{debugScript, bspPath, binBaseName, featureString}
	return util.ShellInteractiveCommand(cmdLine)
}
<commit_msg>Print status message when asked to download with BSP with no download script.<commit_after>/**
 * Licensed to the 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.  The 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 builder

import (
	"fmt"
	"os"
	"path/filepath"

	"mynewt.apache.org/newt/newt/project"
	"mynewt.apache.org/newt/util"
)

func (b *Builder) Download() error {
	if b.target.App() == nil {
		return util.NewNewtError("app package not specified")
	}

	/*
	 * Populate the package list and feature sets.
	 */
	err := b.PrepBuild()
	if err != nil {
		return err
	}

	if b.Bsp.DownloadScript == "" {
		/*
		 *
		 */
		util.StatusMessage(util.VERBOSITY_DEFAULT,
			"No download script for BSP %s\n", b.Bsp.Name())
		return nil
	}

	bspPath := b.Bsp.BasePath()
	downloadScript := filepath.Join(bspPath, b.Bsp.DownloadScript)
	binBaseName := b.AppBinBasePath()
	featureString := b.FeatureString()

	downloadCmd := fmt.Sprintf("%s %s %s %s",
		downloadScript, bspPath, binBaseName, featureString)

	rsp, err := util.ShellCommand(downloadCmd)
	fmt.Printf("%s", rsp)

	return err
}

func (b *Builder) Debug() error {
	if b.target.App() == nil {
		return util.NewNewtError("app package not specified")
	}

	/*
	 * Populate the package list and feature sets.
	 */
	err := b.PrepBuild()
	if err != nil {
		return err
	}

	bspPath := b.Bsp.BasePath()
	debugScript := filepath.Join(bspPath, b.Bsp.DebugScript)
	binBaseName := b.AppBinBasePath()
	featureString := b.FeatureString()

	os.Chdir(project.GetProject().Path())

	cmdLine := []string{debugScript, bspPath, binBaseName, featureString}
	return util.ShellInteractiveCommand(cmdLine)
}
<|endoftext|>


<commit_before>package core

import (
	"time"

  "github.com/v2ray/v2ray-core/log"
)

const (
	updateIntervalSec = 10
	cacheDurationSec  = 120
)

type UserSet interface {
	AddUser(user User) error
	GetUser(timeHash []byte) (*ID, bool)
}

type TimedUserSet struct {
	validUserIds []ID
	userHashes   map[string]int
}

type hashEntry struct {
	hash    string
	timeSec int64
}

func NewTimedUserSet() UserSet {
	vuSet := new(TimedUserSet)
	vuSet.validUserIds = make([]ID, 0, 16)
	vuSet.userHashes = make(map[string]int)

	go vuSet.updateUserHash(time.Tick(updateIntervalSec * time.Second))
	return vuSet
}

func (us *TimedUserSet) updateUserHash(tick <-chan time.Time) {
	now := time.Now().UTC()
	lastSec := now.Unix() - cacheDurationSec

	hash2Remove := make(chan hashEntry, cacheDurationSec*2*len(us.validUserIds))
	lastSec2Remove := now.Unix()
	for {
		now := <-tick
		nowSec := now.UTC().Unix()

    remove2Sec := nowSec - cacheDurationSec
		if remove2Sec > lastSec2Remove {
			for lastSec2Remove+1 < remove2Sec {
				entry := <-hash2Remove
				lastSec2Remove = entry.timeSec
				delete(us.userHashes, entry.hash)
			}
		}

    for lastSec < nowSec + cacheDurationSec {
      for idx, id := range us.validUserIds {
				idHash := id.TimeHash(lastSec)
				hash2Remove <- hashEntry{string(idHash), lastSec}
				us.userHashes[string(idHash)] = idx
			}
      lastSec ++
    }
	}
}

func (us *TimedUserSet) AddUser(user User) error {
	id := user.Id
	us.validUserIds = append(us.validUserIds, id)
	return nil
}

func (us TimedUserSet) GetUser(userHash []byte) (*ID, bool) {
	idIndex, found := us.userHashes[string(userHash)]
	if found {
		return &us.validUserIds[idIndex], true
	}
	return nil, false
}
<commit_msg>build break<commit_after>package core

import (
	"time"
)

const (
	updateIntervalSec = 10
	cacheDurationSec  = 120
)

type UserSet interface {
	AddUser(user User) error
	GetUser(timeHash []byte) (*ID, bool)
}

type TimedUserSet struct {
	validUserIds []ID
	userHashes   map[string]int
}

type hashEntry struct {
	hash    string
	timeSec int64
}

func NewTimedUserSet() UserSet {
	vuSet := new(TimedUserSet)
	vuSet.validUserIds = make([]ID, 0, 16)
	vuSet.userHashes = make(map[string]int)

	go vuSet.updateUserHash(time.Tick(updateIntervalSec * time.Second))
	return vuSet
}

func (us *TimedUserSet) updateUserHash(tick <-chan time.Time) {
	now := time.Now().UTC()
	lastSec := now.Unix() - cacheDurationSec

	hash2Remove := make(chan hashEntry, cacheDurationSec*2*len(us.validUserIds))
	lastSec2Remove := now.Unix()
	for {
		now := <-tick
		nowSec := now.UTC().Unix()

    remove2Sec := nowSec - cacheDurationSec
		if remove2Sec > lastSec2Remove {
			for lastSec2Remove+1 < remove2Sec {
				entry := <-hash2Remove
				lastSec2Remove = entry.timeSec
				delete(us.userHashes, entry.hash)
			}
		}

    for lastSec < nowSec + cacheDurationSec {
      for idx, id := range us.validUserIds {
				idHash := id.TimeHash(lastSec)
				hash2Remove <- hashEntry{string(idHash), lastSec}
				us.userHashes[string(idHash)] = idx
			}
      lastSec ++
    }
	}
}

func (us *TimedUserSet) AddUser(user User) error {
	id := user.Id
	us.validUserIds = append(us.validUserIds, id)
	return nil
}

func (us TimedUserSet) GetUser(userHash []byte) (*ID, bool) {
	idIndex, found := us.userHashes[string(userHash)]
	if found {
		return &us.validUserIds[idIndex], true
	}
	return nil, false
}
<|endoftext|>


<commit_before>package build

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/api/server/httputils"
	"github.com/docker/docker/builder"
	"github.com/docker/docker/builder/dockerfile"
	"github.com/docker/docker/daemon/daemonbuilder"
	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/chrootarchive"
	"github.com/docker/docker/pkg/ioutils"
	"github.com/docker/docker/pkg/progress"
	"github.com/docker/docker/pkg/streamformatter"
	"github.com/docker/docker/reference"
	"github.com/docker/docker/utils"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"github.com/docker/go-units"
	"golang.org/x/net/context"
)

// sanitizeRepoAndTags parses the raw "t" parameter received from the client
// to a slice of repoAndTag.
// It also validates each repoName and tag.
func sanitizeRepoAndTags(names []string) ([]reference.Named, error) {
	var (
		repoAndTags []reference.Named
		// This map is used for deduplicating the "-t" parameter.
		uniqNames = make(map[string]struct{})
	)
	for _, repo := range names {
		if repo == "" {
			continue
		}

		ref, err := reference.ParseNamed(repo)
		if err != nil {
			return nil, err
		}

		ref = reference.WithDefaultTag(ref)

		if _, isCanonical := ref.(reference.Canonical); isCanonical {
			return nil, errors.New("build tag cannot contain a digest")
		}

		if _, isTagged := ref.(reference.NamedTagged); !isTagged {
			ref, err = reference.WithTag(ref, reference.DefaultTag)
		}

		nameWithTag := ref.String()

		if _, exists := uniqNames[nameWithTag]; !exists {
			uniqNames[nameWithTag] = struct{}{}
			repoAndTags = append(repoAndTags, ref)
		}
	}
	return repoAndTags, nil
}

func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) {
	version := httputils.VersionFromContext(ctx)
	options := &types.ImageBuildOptions{}
	if httputils.BoolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") {
		options.Remove = true
	} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
		options.Remove = true
	} else {
		options.Remove = httputils.BoolValue(r, "rm")
	}
	if httputils.BoolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") {
		options.PullParent = true
	}

	options.Dockerfile = r.FormValue("dockerfile")
	options.SuppressOutput = httputils.BoolValue(r, "q")
	options.NoCache = httputils.BoolValue(r, "nocache")
	options.ForceRemove = httputils.BoolValue(r, "forcerm")
	options.MemorySwap = httputils.Int64ValueOrZero(r, "memswap")
	options.Memory = httputils.Int64ValueOrZero(r, "memory")
	options.CPUShares = httputils.Int64ValueOrZero(r, "cpushares")
	options.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod")
	options.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota")
	options.CPUSetCPUs = r.FormValue("cpusetcpus")
	options.CPUSetMems = r.FormValue("cpusetmems")
	options.CgroupParent = r.FormValue("cgroupparent")

	if r.Form.Get("shmsize") != "" {
		shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64)
		if err != nil {
			return nil, err
		}
		options.ShmSize = shmSize
	}

	if i := container.IsolationLevel(r.FormValue("isolation")); i != "" {
		if !container.IsolationLevel.IsValid(i) {
			return nil, fmt.Errorf("Unsupported isolation: %q", i)
		}
		options.IsolationLevel = i
	}

	var buildUlimits = []*units.Ulimit{}
	ulimitsJSON := r.FormValue("ulimits")
	if ulimitsJSON != "" {
		if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil {
			return nil, err
		}
		options.Ulimits = buildUlimits
	}

	var buildArgs = map[string]string{}
	buildArgsJSON := r.FormValue("buildargs")
	if buildArgsJSON != "" {
		if err := json.NewDecoder(strings.NewReader(buildArgsJSON)).Decode(&buildArgs); err != nil {
			return nil, err
		}
		options.BuildArgs = buildArgs
	}
	return options, nil
}

func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	var (
		authConfigs        = map[string]types.AuthConfig{}
		authConfigsEncoded = r.Header.Get("X-Registry-Config")
		notVerboseBuffer   = bytes.NewBuffer(nil)
	)

	if authConfigsEncoded != "" {
		authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
		if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); err != nil {
			// for a pull it is not an error if no auth was given
			// to increase compatibility with the existing api it is defaulting
			// to be empty.
		}
	}

	w.Header().Set("Content-Type", "application/json")

	output := ioutils.NewWriteFlusher(w)
	defer output.Close()
	sf := streamformatter.NewJSONStreamFormatter()
	errf := func(err error) error {
		if httputils.BoolValue(r, "q") && notVerboseBuffer.Len() > 0 {
			output.Write(notVerboseBuffer.Bytes())
		}
		// Do not write the error in the http output if it's still empty.
		// This prevents from writing a 200(OK) when there is an internal error.
		if !output.Flushed() {
			return err
		}
		_, err = w.Write(sf.FormatError(errors.New(utils.GetErrorMessage(err))))
		if err != nil {
			logrus.Warnf("could not write error response: %v", err)
		}
		return nil
	}

	buildOptions, err := newImageBuildOptions(ctx, r)
	if err != nil {
		return errf(err)
	}

	repoAndTags, err := sanitizeRepoAndTags(r.Form["t"])
	if err != nil {
		return errf(err)
	}

	remoteURL := r.FormValue("remote")

	// Currently, only used if context is from a remote url.
	// Look at code in DetectContextFromRemoteURL for more information.
	createProgressReader := func(in io.ReadCloser) io.ReadCloser {
		progressOutput := sf.NewProgressOutput(output, true)
		if buildOptions.SuppressOutput {
			progressOutput = sf.NewProgressOutput(notVerboseBuffer, true)
		}
		return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", remoteURL)
	}

	var (
		context        builder.ModifiableContext
		dockerfileName string
	)
	context, dockerfileName, err = daemonbuilder.DetectContextFromRemoteURL(r.Body, remoteURL, createProgressReader)
	if err != nil {
		return errf(err)
	}
	defer func() {
		if err := context.Close(); err != nil {
			logrus.Debugf("[BUILDER] failed to remove temporary context: %v", err)
		}
	}()
	if len(dockerfileName) > 0 {
		buildOptions.Dockerfile = dockerfileName
	}

	uidMaps, gidMaps := br.backend.GetUIDGIDMaps()
	defaultArchiver := &archive.Archiver{
		Untar:   chrootarchive.Untar,
		UIDMaps: uidMaps,
		GIDMaps: gidMaps,
	}

	docker := &daemonbuilder.Docker{
		Daemon:      br.backend,
		OutOld:      output,
		AuthConfigs: authConfigs,
		Archiver:    defaultArchiver,
	}
	if buildOptions.SuppressOutput {
		docker.OutOld = notVerboseBuffer
	}

	b, err := dockerfile.NewBuilder(
		buildOptions, // result of newBuildConfig
		docker,
		builder.DockerIgnoreContext{ModifiableContext: context},
		nil)
	if err != nil {
		return errf(err)
	}
	b.Stdout = &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf}
	b.Stderr = &streamformatter.StderrFormatter{Writer: output, StreamFormatter: sf}
	if buildOptions.SuppressOutput {
		b.Stdout = &streamformatter.StdoutFormatter{Writer: notVerboseBuffer, StreamFormatter: sf}
		b.Stderr = &streamformatter.StderrFormatter{Writer: notVerboseBuffer, StreamFormatter: sf}
	}

	if closeNotifier, ok := w.(http.CloseNotifier); ok {
		finished := make(chan struct{})
		defer close(finished)
		go func() {
			select {
			case <-finished:
			case <-closeNotifier.CloseNotify():
				logrus.Infof("Client disconnected, cancelling job: build")
				b.Cancel()
			}
		}()
	}

	imgID, err := b.Build()
	if err != nil {
		return errf(err)
	}

	for _, rt := range repoAndTags {
		if err := br.backend.TagImage(rt, imgID); err != nil {
			return errf(err)
		}
	}

	// Everything worked so if -q was provided the output from the daemon
	// should be just the image ID and we'll print that to stdout.
	if buildOptions.SuppressOutput {
		stdout := &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf}
		fmt.Fprintf(stdout, "%s\n", string(imgID))
	}

	return nil
}
<commit_msg>Move closeNotify to fix panic with newer golang<commit_after>package build

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/api/server/httputils"
	"github.com/docker/docker/builder"
	"github.com/docker/docker/builder/dockerfile"
	"github.com/docker/docker/daemon/daemonbuilder"
	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/chrootarchive"
	"github.com/docker/docker/pkg/ioutils"
	"github.com/docker/docker/pkg/progress"
	"github.com/docker/docker/pkg/streamformatter"
	"github.com/docker/docker/reference"
	"github.com/docker/docker/utils"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"github.com/docker/go-units"
	"golang.org/x/net/context"
)

// sanitizeRepoAndTags parses the raw "t" parameter received from the client
// to a slice of repoAndTag.
// It also validates each repoName and tag.
func sanitizeRepoAndTags(names []string) ([]reference.Named, error) {
	var (
		repoAndTags []reference.Named
		// This map is used for deduplicating the "-t" parameter.
		uniqNames = make(map[string]struct{})
	)
	for _, repo := range names {
		if repo == "" {
			continue
		}

		ref, err := reference.ParseNamed(repo)
		if err != nil {
			return nil, err
		}

		ref = reference.WithDefaultTag(ref)

		if _, isCanonical := ref.(reference.Canonical); isCanonical {
			return nil, errors.New("build tag cannot contain a digest")
		}

		if _, isTagged := ref.(reference.NamedTagged); !isTagged {
			ref, err = reference.WithTag(ref, reference.DefaultTag)
		}

		nameWithTag := ref.String()

		if _, exists := uniqNames[nameWithTag]; !exists {
			uniqNames[nameWithTag] = struct{}{}
			repoAndTags = append(repoAndTags, ref)
		}
	}
	return repoAndTags, nil
}

func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) {
	version := httputils.VersionFromContext(ctx)
	options := &types.ImageBuildOptions{}
	if httputils.BoolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") {
		options.Remove = true
	} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
		options.Remove = true
	} else {
		options.Remove = httputils.BoolValue(r, "rm")
	}
	if httputils.BoolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") {
		options.PullParent = true
	}

	options.Dockerfile = r.FormValue("dockerfile")
	options.SuppressOutput = httputils.BoolValue(r, "q")
	options.NoCache = httputils.BoolValue(r, "nocache")
	options.ForceRemove = httputils.BoolValue(r, "forcerm")
	options.MemorySwap = httputils.Int64ValueOrZero(r, "memswap")
	options.Memory = httputils.Int64ValueOrZero(r, "memory")
	options.CPUShares = httputils.Int64ValueOrZero(r, "cpushares")
	options.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod")
	options.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota")
	options.CPUSetCPUs = r.FormValue("cpusetcpus")
	options.CPUSetMems = r.FormValue("cpusetmems")
	options.CgroupParent = r.FormValue("cgroupparent")

	if r.Form.Get("shmsize") != "" {
		shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64)
		if err != nil {
			return nil, err
		}
		options.ShmSize = shmSize
	}

	if i := container.IsolationLevel(r.FormValue("isolation")); i != "" {
		if !container.IsolationLevel.IsValid(i) {
			return nil, fmt.Errorf("Unsupported isolation: %q", i)
		}
		options.IsolationLevel = i
	}

	var buildUlimits = []*units.Ulimit{}
	ulimitsJSON := r.FormValue("ulimits")
	if ulimitsJSON != "" {
		if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil {
			return nil, err
		}
		options.Ulimits = buildUlimits
	}

	var buildArgs = map[string]string{}
	buildArgsJSON := r.FormValue("buildargs")
	if buildArgsJSON != "" {
		if err := json.NewDecoder(strings.NewReader(buildArgsJSON)).Decode(&buildArgs); err != nil {
			return nil, err
		}
		options.BuildArgs = buildArgs
	}
	return options, nil
}

func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	var (
		authConfigs        = map[string]types.AuthConfig{}
		authConfigsEncoded = r.Header.Get("X-Registry-Config")
		notVerboseBuffer   = bytes.NewBuffer(nil)
	)

	if authConfigsEncoded != "" {
		authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
		if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); err != nil {
			// for a pull it is not an error if no auth was given
			// to increase compatibility with the existing api it is defaulting
			// to be empty.
		}
	}

	w.Header().Set("Content-Type", "application/json")

	output := ioutils.NewWriteFlusher(w)
	defer output.Close()
	sf := streamformatter.NewJSONStreamFormatter()
	errf := func(err error) error {
		if httputils.BoolValue(r, "q") && notVerboseBuffer.Len() > 0 {
			output.Write(notVerboseBuffer.Bytes())
		}
		// Do not write the error in the http output if it's still empty.
		// This prevents from writing a 200(OK) when there is an internal error.
		if !output.Flushed() {
			return err
		}
		_, err = w.Write(sf.FormatError(errors.New(utils.GetErrorMessage(err))))
		if err != nil {
			logrus.Warnf("could not write error response: %v", err)
		}
		return nil
	}

	buildOptions, err := newImageBuildOptions(ctx, r)
	if err != nil {
		return errf(err)
	}

	repoAndTags, err := sanitizeRepoAndTags(r.Form["t"])
	if err != nil {
		return errf(err)
	}

	remoteURL := r.FormValue("remote")

	// Currently, only used if context is from a remote url.
	// Look at code in DetectContextFromRemoteURL for more information.
	createProgressReader := func(in io.ReadCloser) io.ReadCloser {
		progressOutput := sf.NewProgressOutput(output, true)
		if buildOptions.SuppressOutput {
			progressOutput = sf.NewProgressOutput(notVerboseBuffer, true)
		}
		return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", remoteURL)
	}

	var (
		context        builder.ModifiableContext
		dockerfileName string
	)
	context, dockerfileName, err = daemonbuilder.DetectContextFromRemoteURL(r.Body, remoteURL, createProgressReader)
	if err != nil {
		return errf(err)
	}
	defer func() {
		if err := context.Close(); err != nil {
			logrus.Debugf("[BUILDER] failed to remove temporary context: %v", err)
		}
	}()
	if len(dockerfileName) > 0 {
		buildOptions.Dockerfile = dockerfileName
	}

	uidMaps, gidMaps := br.backend.GetUIDGIDMaps()
	defaultArchiver := &archive.Archiver{
		Untar:   chrootarchive.Untar,
		UIDMaps: uidMaps,
		GIDMaps: gidMaps,
	}

	docker := &daemonbuilder.Docker{
		Daemon:      br.backend,
		OutOld:      output,
		AuthConfigs: authConfigs,
		Archiver:    defaultArchiver,
	}
	if buildOptions.SuppressOutput {
		docker.OutOld = notVerboseBuffer
	}

	b, err := dockerfile.NewBuilder(
		buildOptions, // result of newBuildConfig
		docker,
		builder.DockerIgnoreContext{ModifiableContext: context},
		nil)
	if err != nil {
		return errf(err)
	}
	b.Stdout = &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf}
	b.Stderr = &streamformatter.StderrFormatter{Writer: output, StreamFormatter: sf}
	if buildOptions.SuppressOutput {
		b.Stdout = &streamformatter.StdoutFormatter{Writer: notVerboseBuffer, StreamFormatter: sf}
		b.Stderr = &streamformatter.StderrFormatter{Writer: notVerboseBuffer, StreamFormatter: sf}
	}

	if closeNotifier, ok := w.(http.CloseNotifier); ok {
		finished := make(chan struct{})
		defer close(finished)
		clientGone := closeNotifier.CloseNotify()
		go func() {
			select {
			case <-finished:
			case <-clientGone:
				logrus.Infof("Client disconnected, cancelling job: build")
				b.Cancel()
			}
		}()
	}

	imgID, err := b.Build()
	if err != nil {
		return errf(err)
	}

	for _, rt := range repoAndTags {
		if err := br.backend.TagImage(rt, imgID); err != nil {
			return errf(err)
		}
	}

	// Everything worked so if -q was provided the output from the daemon
	// should be just the image ID and we'll print that to stdout.
	if buildOptions.SuppressOutput {
		stdout := &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf}
		fmt.Fprintf(stdout, "%s\n", string(imgID))
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package util

import (
	"net"
	"strconv"
	"strings"

	"github.com/bitmark-inc/bitmarkd/fault"
	"github.com/bitmark-inc/logger"
)

// Connection - type to hold an IP and Port
type Connection struct {
	ip   net.IP
	port uint16
}

// NewConnection - create a connection from an Host:Port string
func NewConnection(hostPort string) (*Connection, error) {
	host, port, err := net.SplitHostPort(hostPort)
	if nil != err {
		return nil, fault.ErrInvalidIPAddress
	}

	IP := net.ParseIP(strings.Trim(host, " "))
	if nil == IP {
		ips, err := net.LookupIP(host)
		if nil != err {
			return nil, err
		}
		if len(ips) < 1 {
			return nil, fault.ErrInvalidIPAddress
		}
		IP = ips[0]
	}

	numericPort, err := strconv.Atoi(strings.Trim(port, " "))
	if nil != err {
		return nil, err
	}
	if numericPort < 1 || numericPort > 65535 {
		return nil, fault.ErrInvalidPortNumber
	}
	c := &Connection{
		ip:   IP,
		port: uint16(numericPort),
	}
	return c, nil
}

// NewConnections -  convert an array of connections
func NewConnections(hostPort []string) ([]*Connection, error) {
	if 0 == len(hostPort) {
		return nil, fault.ErrInvalidLength
	}
	c := make([]*Connection, len(hostPort))
	for i, hp := range hostPort {
		err := error(nil)
		c[i], err = NewConnection(hp)
		if nil != err {
			return nil, err
		}
	}
	return c, nil
}

// ConnectionFromIPandPort - convert an IP and port to a connection
func ConnectionFromIPandPort(ip net.IP, port uint16) *Connection {
	return &Connection{
		ip:   ip,
		port: port,
	}
}

// ConnectionFromCanonical - convert a cononical string to a connection
//
// return the connection if string is canonical, otherwise nil
func ConnectionFromCanonical(s string) *Connection {
	if "" == s {
		return nil
	}

	i := strings.LastIndex(s, ":")
	if -1 == i {
		return nil
	}

	ip := net.ParseIP(s[0 : i-1])
	port, err := strconv.ParseInt(s[i+1:], 10, 16)
	if nil == ip || nil != err {
		return nil
	}

	return &Connection{
		ip:   ip,
		port: uint16(port),
	}
}

// CanonicalIPandPort - make the IP:Port into canonical string
//
// examples:
//   IPv4:  127.0.0.1:1234
//   IPv6:  [::1]:1234
//
// prefix is optional and can be empty ("")
// returns prefixed string and IPv6 flag
func (conn *Connection) CanonicalIPandPort(prefix string) (string, bool) {

	port := int(conn.port)
	if nil != conn.ip.To4() {
		return prefix + conn.ip.String() + ":" + strconv.Itoa(port), false
	}
	return prefix + "[" + conn.ip.String() + "]:" + strconv.Itoa(port), true
}

// basic string conversion
func (conn Connection) String() string {
	s, _ := conn.CanonicalIPandPort("")
	return s
}

// MarshalText - convert to text for JSON
func (conn Connection) MarshalText() ([]byte, error) {
	s, _ := conn.CanonicalIPandPort("")
	return []byte(s), nil
}

// PackedConnection - type for packed byte buffer IP and Port
type PackedConnection []byte

// Pack - pack an IP and Port into a byte buffer
func (conn *Connection) Pack() PackedConnection {
	b := []byte(conn.ip)
	length := len(b)
	if 4 != length && 16 != length {
		logger.Panicf("connection.Pack: invalid IP length: %d", length)
	}
	size := length + 3 // count++port.high++port.low++ip
	b2 := make([]byte, size)
	b2[0] = byte(size)           // 7 or 19
	b2[1] = byte(conn.port >> 8) // port high byte
	b2[2] = byte(conn.port)      // port low byte
	copy(b2[3:], b)              // 4 byte IPv4 or 16 byte IPv6
	return b2
}

// Unpack - unpack a byte buffer into an IP and Port
// returns nil if unpack fails
// if successful returns connection and number of bytes used
// so an array can be unpacked more easily
func (packed PackedConnection) Unpack() (*Connection, int) {
	if nil == packed {
		return nil, 0
	}
	count := len(packed)
	if count < 7 {
		return nil, 0
	}
	n := packed[0]
	if 7 != n && 19 != n { // only valid values
		return nil, 0
	}

	ip := make([]byte, n-3) // 4 or 16 bytes
	copy(ip, packed[3:n])
	c := &Connection{
		ip:   ip,
		port: uint16(packed[1])<<8 + uint16(packed[2]),
	}
	return c, int(n)
}

// Unpack46 - unpack first IPv4 and first IPv6 plus Port
func (packed PackedConnection) Unpack46() (*Connection, *Connection) {

	// only expect two
	ipv4Connection := (*Connection)(nil)
	ipv6Connection := (*Connection)(nil)

	for {
		conn, n := packed.Unpack()
		packed = packed[n:]

		if nil == conn {
			return ipv4Connection, ipv6Connection
		}

		if nil != conn.ip.To4() {
			if nil == ipv4Connection {
				ipv4Connection = conn
			}
		} else if nil == ipv6Connection {
			ipv6Connection = conn
		}

		// if both kinds found
		if nil != ipv4Connection && nil != ipv6Connection {
			return ipv4Connection, ipv6Connection
		}
	}
}
<commit_msg>- Fix misspelling<commit_after>// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package util

import (
	"net"
	"strconv"
	"strings"

	"github.com/bitmark-inc/bitmarkd/fault"
	"github.com/bitmark-inc/logger"
)

// Connection - type to hold an IP and Port
type Connection struct {
	ip   net.IP
	port uint16
}

// NewConnection - create a connection from an Host:Port string
func NewConnection(hostPort string) (*Connection, error) {
	host, port, err := net.SplitHostPort(hostPort)
	if nil != err {
		return nil, fault.ErrInvalidIPAddress
	}

	IP := net.ParseIP(strings.Trim(host, " "))
	if nil == IP {
		ips, err := net.LookupIP(host)
		if nil != err {
			return nil, err
		}
		if len(ips) < 1 {
			return nil, fault.ErrInvalidIPAddress
		}
		IP = ips[0]
	}

	numericPort, err := strconv.Atoi(strings.Trim(port, " "))
	if nil != err {
		return nil, err
	}
	if numericPort < 1 || numericPort > 65535 {
		return nil, fault.ErrInvalidPortNumber
	}
	c := &Connection{
		ip:   IP,
		port: uint16(numericPort),
	}
	return c, nil
}

// NewConnections -  convert an array of connections
func NewConnections(hostPort []string) ([]*Connection, error) {
	if 0 == len(hostPort) {
		return nil, fault.ErrInvalidLength
	}
	c := make([]*Connection, len(hostPort))
	for i, hp := range hostPort {
		err := error(nil)
		c[i], err = NewConnection(hp)
		if nil != err {
			return nil, err
		}
	}
	return c, nil
}

// ConnectionFromIPandPort - convert an IP and port to a connection
func ConnectionFromIPandPort(ip net.IP, port uint16) *Connection {
	return &Connection{
		ip:   ip,
		port: port,
	}
}

// ConnectionFromCanonical - convert a canonical string to a connection
//
// return the connection if string is canonical, otherwise nil
func ConnectionFromCanonical(s string) *Connection {
	if "" == s {
		return nil
	}

	i := strings.LastIndex(s, ":")
	if -1 == i {
		return nil
	}

	ip := net.ParseIP(s[0 : i-1])
	port, err := strconv.ParseInt(s[i+1:], 10, 16)
	if nil == ip || nil != err {
		return nil
	}

	return &Connection{
		ip:   ip,
		port: uint16(port),
	}
}

// CanonicalIPandPort - make the IP:Port into canonical string
//
// examples:
//   IPv4:  127.0.0.1:1234
//   IPv6:  [::1]:1234
//
// prefix is optional and can be empty ("")
// returns prefixed string and IPv6 flag
func (conn *Connection) CanonicalIPandPort(prefix string) (string, bool) {

	port := int(conn.port)
	if nil != conn.ip.To4() {
		return prefix + conn.ip.String() + ":" + strconv.Itoa(port), false
	}
	return prefix + "[" + conn.ip.String() + "]:" + strconv.Itoa(port), true
}

// basic string conversion
func (conn Connection) String() string {
	s, _ := conn.CanonicalIPandPort("")
	return s
}

// MarshalText - convert to text for JSON
func (conn Connection) MarshalText() ([]byte, error) {
	s, _ := conn.CanonicalIPandPort("")
	return []byte(s), nil
}

// PackedConnection - type for packed byte buffer IP and Port
type PackedConnection []byte

// Pack - pack an IP and Port into a byte buffer
func (conn *Connection) Pack() PackedConnection {
	b := []byte(conn.ip)
	length := len(b)
	if 4 != length && 16 != length {
		logger.Panicf("connection.Pack: invalid IP length: %d", length)
	}
	size := length + 3 // count++port.high++port.low++ip
	b2 := make([]byte, size)
	b2[0] = byte(size)           // 7 or 19
	b2[1] = byte(conn.port >> 8) // port high byte
	b2[2] = byte(conn.port)      // port low byte
	copy(b2[3:], b)              // 4 byte IPv4 or 16 byte IPv6
	return b2
}

// Unpack - unpack a byte buffer into an IP and Port
// returns nil if unpack fails
// if successful returns connection and number of bytes used
// so an array can be unpacked more easily
func (packed PackedConnection) Unpack() (*Connection, int) {
	if nil == packed {
		return nil, 0
	}
	count := len(packed)
	if count < 7 {
		return nil, 0
	}
	n := packed[0]
	if 7 != n && 19 != n { // only valid values
		return nil, 0
	}

	ip := make([]byte, n-3) // 4 or 16 bytes
	copy(ip, packed[3:n])
	c := &Connection{
		ip:   ip,
		port: uint16(packed[1])<<8 + uint16(packed[2]),
	}
	return c, int(n)
}

// Unpack46 - unpack first IPv4 and first IPv6 plus Port
func (packed PackedConnection) Unpack46() (*Connection, *Connection) {

	// only expect two
	ipv4Connection := (*Connection)(nil)
	ipv6Connection := (*Connection)(nil)

	for {
		conn, n := packed.Unpack()
		packed = packed[n:]

		if nil == conn {
			return ipv4Connection, ipv6Connection
		}

		if nil != conn.ip.To4() {
			if nil == ipv4Connection {
				ipv4Connection = conn
			}
		} else if nil == ipv6Connection {
			ipv6Connection = conn
		}

		// if both kinds found
		if nil != ipv4Connection && nil != ipv6Connection {
			return ipv4Connection, ipv6Connection
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes 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 cert

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	cryptorand "crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"fmt"
	"math"
	"math/big"
	"net"
	"time"
)

const (
	rsaKeySize   = 2048
	duration365d = time.Hour * 24 * 365
)

// Config contains the basic fields required for creating a certificate
type Config struct {
	CommonName   string
	Organization []string
	AltNames     AltNames
	Usages       []x509.ExtKeyUsage
}

// AltNames contains the domain names and IP addresses that will be added
// to the API Server's x509 certificate SubAltNames field. The values will
// be passed directly to the x509.Certificate object.
type AltNames struct {
	DNSNames []string
	IPs      []net.IP
}

// NewPrivateKey creates an RSA private key
func NewPrivateKey() (*rsa.PrivateKey, error) {
	return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
}

// NewSelfSignedCACert creates a CA certificate
func NewSelfSignedCACert(cfg Config, key *rsa.PrivateKey) (*x509.Certificate, error) {
	now := time.Now()
	tmpl := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(0),
		Subject: pkix.Name{
			CommonName:   cfg.CommonName,
			Organization: cfg.Organization,
		},
		NotBefore:             now.UTC(),
		NotAfter:              now.Add(duration365d * 10).UTC(),
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA: true,
	}

	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
	if err != nil {
		return nil, err
	}
	return x509.ParseCertificate(certDERBytes)
}

// NewSignedCert creates a signed certificate using the given CA certificate and key
func NewSignedCert(cfg Config, key *rsa.PrivateKey, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, error) {
	serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64))
	if err != nil {
		return nil, err
	}
	if len(cfg.CommonName) == 0 {
		return nil, errors.New("must specify a CommonName")
	}
	if len(cfg.Usages) == 0 {
		return nil, errors.New("must specify at least one ExtKeyUsage")
	}

	certTmpl := x509.Certificate{
		Subject: pkix.Name{
			CommonName:   cfg.CommonName,
			Organization: cfg.Organization,
		},
		DNSNames:     cfg.AltNames.DNSNames,
		IPAddresses:  cfg.AltNames.IPs,
		SerialNumber: serial,
		NotBefore:    caCert.NotBefore,
		NotAfter:     time.Now().Add(duration365d).UTC(),
		KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:  cfg.Usages,
	}
	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey)
	if err != nil {
		return nil, err
	}
	return x509.ParseCertificate(certDERBytes)
}

// MakeEllipticPrivateKeyPEM creates an ECDSA private key
func MakeEllipticPrivateKeyPEM() ([]byte, error) {
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
	if err != nil {
		return nil, err
	}

	derBytes, err := x509.MarshalECPrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	privateKeyPemBlock := &pem.Block{
		Type:  ECPrivateKeyBlockType,
		Bytes: derBytes,
	}
	return pem.EncodeToMemory(privateKeyPemBlock), nil
}

// GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
// Host may be an IP or a DNS name
// You may also specify additional subject alt names (either ip or dns names) for the certificate
func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
	caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	caTemplate := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()),
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(time.Hour * 24 * 365),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA: true,
	}

	caDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
	if err != nil {
		return nil, nil, err
	}

	caCertificate, err := x509.ParseCertificate(caDERBytes)
	if err != nil {
		return nil, nil, err
	}

	priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(2),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(time.Hour * 24 * 365),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	if ip := net.ParseIP(host); ip != nil {
		template.IPAddresses = append(template.IPAddresses, ip)
	} else {
		template.DNSNames = append(template.DNSNames, host)
	}

	template.IPAddresses = append(template.IPAddresses, alternateIPs...)
	template.DNSNames = append(template.DNSNames, alternateDNS...)

	derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, caCertificate, &priv.PublicKey, caKey)
	if err != nil {
		return nil, nil, err
	}

	// Generate cert, followed by ca
	certBuffer := bytes.Buffer{}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
		return nil, nil, err
	}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: caDERBytes}); err != nil {
		return nil, nil, err
	}

	// Generate key
	keyBuffer := bytes.Buffer{}
	if err := pem.Encode(&keyBuffer, &pem.Block{Type: RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
		return nil, nil, err
	}

	return certBuffer.Bytes(), keyBuffer.Bytes(), nil
}

// FormatBytesCert receives byte array certificate and formats in human-readable format
func FormatBytesCert(cert []byte) (string, error) {
	block, _ := pem.Decode(cert)
	c, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return "", fmt.Errorf("failed to parse certificate [%v]", err)
	}
	return FormatCert(c), nil
}

// FormatCert receives certificate and formats in human-readable format
func FormatCert(c *x509.Certificate) string {
	var ips []string
	for _, ip := range c.IPAddresses {
		ips = append(ips, ip.String())
	}
	altNames := append(ips, c.DNSNames...)
	res := fmt.Sprintf(
		"Issuer: CN=%s | Subject: CN=%s | CA: %t\n",
		c.Issuer.CommonName, c.Subject.CommonName, c.IsCA,
	)
	res += fmt.Sprintf("Not before: %s Not After: %s", c.NotBefore, c.NotAfter)
	if len(altNames) > 0 {
		res += fmt.Sprintf("\nAlternate Names: %v", altNames)
	}
	return res
}
<commit_msg>apiserver: use fixtures for self-signed certs in test server<commit_after>/*
Copyright 2014 The Kubernetes 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 cert

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	cryptorand "crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"math"
	"math/big"
	"net"
	"path"
	"strings"
	"time"
)

const (
	rsaKeySize   = 2048
	duration365d = time.Hour * 24 * 365
)

// Config contains the basic fields required for creating a certificate
type Config struct {
	CommonName   string
	Organization []string
	AltNames     AltNames
	Usages       []x509.ExtKeyUsage
}

// AltNames contains the domain names and IP addresses that will be added
// to the API Server's x509 certificate SubAltNames field. The values will
// be passed directly to the x509.Certificate object.
type AltNames struct {
	DNSNames []string
	IPs      []net.IP
}

// NewPrivateKey creates an RSA private key
func NewPrivateKey() (*rsa.PrivateKey, error) {
	return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
}

// NewSelfSignedCACert creates a CA certificate
func NewSelfSignedCACert(cfg Config, key *rsa.PrivateKey) (*x509.Certificate, error) {
	now := time.Now()
	tmpl := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(0),
		Subject: pkix.Name{
			CommonName:   cfg.CommonName,
			Organization: cfg.Organization,
		},
		NotBefore:             now.UTC(),
		NotAfter:              now.Add(duration365d * 10).UTC(),
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA: true,
	}

	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
	if err != nil {
		return nil, err
	}
	return x509.ParseCertificate(certDERBytes)
}

// NewSignedCert creates a signed certificate using the given CA certificate and key
func NewSignedCert(cfg Config, key *rsa.PrivateKey, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, error) {
	serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64))
	if err != nil {
		return nil, err
	}
	if len(cfg.CommonName) == 0 {
		return nil, errors.New("must specify a CommonName")
	}
	if len(cfg.Usages) == 0 {
		return nil, errors.New("must specify at least one ExtKeyUsage")
	}

	certTmpl := x509.Certificate{
		Subject: pkix.Name{
			CommonName:   cfg.CommonName,
			Organization: cfg.Organization,
		},
		DNSNames:     cfg.AltNames.DNSNames,
		IPAddresses:  cfg.AltNames.IPs,
		SerialNumber: serial,
		NotBefore:    caCert.NotBefore,
		NotAfter:     time.Now().Add(duration365d).UTC(),
		KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:  cfg.Usages,
	}
	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey)
	if err != nil {
		return nil, err
	}
	return x509.ParseCertificate(certDERBytes)
}

// MakeEllipticPrivateKeyPEM creates an ECDSA private key
func MakeEllipticPrivateKeyPEM() ([]byte, error) {
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
	if err != nil {
		return nil, err
	}

	derBytes, err := x509.MarshalECPrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	privateKeyPemBlock := &pem.Block{
		Type:  ECPrivateKeyBlockType,
		Bytes: derBytes,
	}
	return pem.EncodeToMemory(privateKeyPemBlock), nil
}

// GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
// Host may be an IP or a DNS name
// You may also specify additional subject alt names (either ip or dns names) for the certificate.
func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
	return GenerateSelfSignedCertKeyWithFixtures(host, alternateIPs, alternateDNS, "")
}

// GenerateSelfSignedCertKeyWithFixtures creates a self-signed certificate and key for the given host.
// Host may be an IP or a DNS name. You may also specify additional subject alt names (either ip or dns names)
// for the certificate.
//
// If fixtureDirectory is non-empty, it is a directory path which can contain pre-generated certs. The format is:
// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.crt
// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.key
// Certs/keys not existing in that directory are created.
func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, alternateDNS []string, fixtureDirectory string) ([]byte, []byte, error) {
	validFrom := time.Now().Add(-time.Hour) // valid an hour earlier to avoid flakes due to clock skew
	maxAge := time.Hour * 24 * 365          // one year self-signed certs

	baseName := fmt.Sprintf("%s_%s_%s", host, strings.Join(ipsToStrings(alternateIPs), "-"), strings.Join(alternateDNS, "-"))
	certFixturePath := path.Join(fixtureDirectory, baseName+".crt")
	keyFixturePath := path.Join(fixtureDirectory, baseName+".key")
	if len(fixtureDirectory) > 0 {
		cert, err := ioutil.ReadFile(certFixturePath)
		if err == nil {
			key, err := ioutil.ReadFile(keyFixturePath)
			if err == nil {
				return cert, key, nil
			}
			return nil, nil, fmt.Errorf("cert %s can be read, but key %s cannot: %v", certFixturePath, keyFixturePath, err)
		}
		maxAge = 100 * time.Hour * 24 * 365 // 100 years fixtures
	}

	caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	caTemplate := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()),
		},
		NotBefore: validFrom,
		NotAfter:  validFrom.Add(maxAge),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA: true,
	}

	caDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
	if err != nil {
		return nil, nil, err
	}

	caCertificate, err := x509.ParseCertificate(caDERBytes)
	if err != nil {
		return nil, nil, err
	}

	priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(2),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
		},
		NotBefore: validFrom,
		NotAfter:  validFrom.Add(maxAge),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	if ip := net.ParseIP(host); ip != nil {
		template.IPAddresses = append(template.IPAddresses, ip)
	} else {
		template.DNSNames = append(template.DNSNames, host)
	}

	template.IPAddresses = append(template.IPAddresses, alternateIPs...)
	template.DNSNames = append(template.DNSNames, alternateDNS...)

	derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, caCertificate, &priv.PublicKey, caKey)
	if err != nil {
		return nil, nil, err
	}

	// Generate cert, followed by ca
	certBuffer := bytes.Buffer{}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
		return nil, nil, err
	}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: caDERBytes}); err != nil {
		return nil, nil, err
	}

	// Generate key
	keyBuffer := bytes.Buffer{}
	if err := pem.Encode(&keyBuffer, &pem.Block{Type: RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
		return nil, nil, err
	}

	if len(fixtureDirectory) > 0 {
		if err := ioutil.WriteFile(certFixturePath, certBuffer.Bytes(), 0644); err != nil {
			return nil, nil, fmt.Errorf("failed to write cert fixture to %s: %v", certFixturePath, err)
		}
		if err := ioutil.WriteFile(keyFixturePath, keyBuffer.Bytes(), 0644); err != nil {
			return nil, nil, fmt.Errorf("failed to write key fixture to %s: %v", certFixturePath, err)
		}
	}

	return certBuffer.Bytes(), keyBuffer.Bytes(), nil
}

// FormatBytesCert receives byte array certificate and formats in human-readable format
func FormatBytesCert(cert []byte) (string, error) {
	block, _ := pem.Decode(cert)
	c, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return "", fmt.Errorf("failed to parse certificate [%v]", err)
	}
	return FormatCert(c), nil
}

// FormatCert receives certificate and formats in human-readable format
func FormatCert(c *x509.Certificate) string {
	var ips []string
	for _, ip := range c.IPAddresses {
		ips = append(ips, ip.String())
	}
	altNames := append(ips, c.DNSNames...)
	res := fmt.Sprintf(
		"Issuer: CN=%s | Subject: CN=%s | CA: %t\n",
		c.Issuer.CommonName, c.Subject.CommonName, c.IsCA,
	)
	res += fmt.Sprintf("Not before: %s Not After: %s", c.NotBefore, c.NotAfter)
	if len(altNames) > 0 {
		res += fmt.Sprintf("\nAlternate Names: %v", altNames)
	}
	return res
}

func ipsToStrings(ips []net.IP) []string {
	ss := make([]string, 0, len(ips))
	for _, ip := range ips {
		ss = append(ss, ip.String())
	}
	return ss
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
//
// 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 render

import (
	"bytes"
	"fmt"
	htmlTemplate "html/template"
	"io"
	"sort"
	"strconv"
	"strings"

	"github.com/googlecodelabs/tools/claat/types"
)

// TODO: render HTML using golang/x/net/html or template.

// HTML renders nodes as the markup for the target env.
func HTML(ctx Context, nodes ...types.Node) (htmlTemplate.HTML, error) {
	var buf bytes.Buffer
	if err := WriteHTML(&buf, ctx.Env, ctx.Format, nodes...); err != nil {
		return "", err
	}
	return htmlTemplate.HTML(buf.String()), nil
}

// WriteHTML does the same as HTML but outputs rendered markup to w.
func WriteHTML(w io.Writer, env string, fmt string, nodes ...types.Node) error {
	hw := htmlWriter{w: w, env: env, format: fmt}
	return hw.write(nodes...)
}

// ReplaceDoubleCurlyBracketsWithEntity replaces Double Curly Brackets with their charater entity.
func ReplaceDoubleCurlyBracketsWithEntity(s string) string {
	return strings.Replace(s, "{{", "{{", -1)
}

type htmlWriter struct {
	w      io.Writer // output writer
	env    string    // target environment
	format string    // target template
	err    error     // error during any writeXxx methods
}

func (hw *htmlWriter) matchEnv(v []string) bool {
	if len(v) == 0 || hw.env == "" {
		return true
	}
	i := sort.SearchStrings(v, hw.env)
	return i < len(v) && v[i] == hw.env
}

func (hw *htmlWriter) write(nodes ...types.Node) error {
	for _, n := range nodes {
		if !hw.matchEnv(n.Env()) {
			continue
		}
		switch n := n.(type) {
		case *types.TextNode:
			hw.text(n)
		case *types.ImageNode:
			hw.image(n)
		case *types.URLNode:
			hw.url(n)
		case *types.ButtonNode:
			hw.button(n)
		case *types.CodeNode:
			hw.code(n)
			hw.writeString("\n")
		case *types.ListNode:
			hw.list(n)
			hw.writeString("\n")
		case *types.ImportNode:
			if len(n.Content.Nodes) == 0 {
				break
			}
			hw.list(n.Content)
			hw.writeString("\n")
		case *types.ItemsListNode:
			hw.itemsList(n)
			hw.writeString("\n")
		case *types.GridNode:
			hw.grid(n)
			hw.writeString("\n")
		case *types.InfoboxNode:
			hw.infobox(n)
			hw.writeString("\n")
		case *types.SurveyNode:
			hw.survey(n)
			hw.writeString("\n")
		case *types.HeaderNode:
			hw.header(n)
			hw.writeString("\n")
		case *types.YouTubeNode:
			hw.youtube(n)
			hw.writeString("\n")
		case *types.IframeNode:
			hw.iframe(n)
			hw.writeString("\n")
		}
		if hw.err != nil {
			return hw.err
		}
	}
	return nil
}

// Writes a string to the htmlWriter unless a write error has occurred on this htmlWriter in the past.
// Will set a write error on this htmlWriter if the write fails.
func (hw *htmlWriter) writeString(s string) {
	if hw.err != nil {
		return
	}
	_, hw.err = hw.w.Write([]byte(s))
}

// Same as writeString, but with fmt.Sprintf arguments/semantics.
func (hw *htmlWriter) writeFmt(f string, a ...interface{}) {
	hw.writeString(fmt.Sprintf(f, a...))
}

// Same as writeString, but performs HTML escaping and double curly bracket escaping.
func (hw *htmlWriter) writeEscape(s string) {
	s = htmlTemplate.HTMLEscapeString(s)
	hw.writeString(ReplaceDoubleCurlyBracketsWithEntity(s))
}

func (hw *htmlWriter) text(n *types.TextNode) {
	if n.Bold {
		hw.writeString("")
	}
	if n.Italic {
		hw.writeString("")
	}
	if n.Code {
		hw.writeString("")
	}
	s := htmlTemplate.HTMLEscapeString(n.Value)
	s = ReplaceDoubleCurlyBracketsWithEntity(s)
	hw.writeString(strings.Replace(s, "\n", "
", -1))
	if n.Code {
		hw.writeString("")
	}
	if n.Italic {
		hw.writeString("")
	}
	if n.Bold {
		hw.writeString("")
	}
}

func (hw *htmlWriter) image(n *types.ImageNode) {
	hw.writeString(" 0 {
		hw.writeFmt(` style="width: %.2fpx"`, n.Width)
	}
	hw.writeFmt(" src=%q>", n.Src)
}

func (hw *htmlWriter) url(n *types.URLNode) {
	hw.writeString("<a")
	if n.URL != "" {
		hw.writeFmt(" href=%q", n.URL)
	}
	if n.Name != "" {
		hw.writeString(` name="`)
		hw.writeEscape(n.Name)
		hw.writeString(`"`)
	}
	if n.Target != "" {
		hw.writeString(` target="`)
		hw.writeEscape(n.Target)
		hw.writeString(`"`)
	}
	hw.writeString(">")
	hw.write(n.Content.Nodes...)
	hw.writeString("")
}

func (hw *htmlWriter) button(n *types.ButtonNode) {
	hw.writeString("<paper-button")
	if n.Colored {
		hw.writeString(` class="colored"`)
	}
	if n.Raised {
		hw.writeString(" raised")
	}
	hw.writeString(">")
	if n.Download {
		hw.writeString(`<iron-icon icon="file-download"></iron-icon>`)
	}
	hw.write(n.Content.Nodes...)
	hw.writeString("</paper-button>")
}

func (hw *htmlWriter) code(n *types.CodeNode) {
	hw.writeString("
")
	if !n.Term {
		hw.writeString("")
	}
	if hw.format == "devsite" {
		hw.writeString("{% verbatim %}")
	}
	hw.writeEscape(n.Value)
	if hw.format == "devsite" {
		hw.writeString("{% endverbatim %}")
	}
	if !n.Term {
		hw.writeString("")
	}
	hw.writeString("
")
}

func (hw *htmlWriter) list(n *types.ListNode) {
	wrap := n.Block() == true
	if wrap {
		if onlyImages(n.Nodes...) {
			hw.writeString(`<p class="image-container">`)
		} else {
			hw.writeString("
")
		}
	}
	hw.write(n.Nodes...)
	if wrap {
		hw.writeString("
")
	}
}

// Returns true if the list of Nodes contains only images or white spaces.
func onlyImages(nodes ...types.Node) bool {
	for _, n := range nodes {
		switch n := n.(type) {
		case *types.TextNode:
			if len(strings.TrimSpace(n.Value)) == 0 {
				continue
			}
			return false
		case *types.ImageNode:
			continue
		default:
			return false
		}
	}
	return true
}

func (hw *htmlWriter) itemsList(n *types.ItemsListNode) {
	tag := "ul"
	if n.Type() == types.NodeItemsList && (n.Start > 0 || n.ListType != "") {
		tag = "ol"
	}
	hw.writeString("<")
	hw.writeString(tag)
	switch n.Type() {
	case types.NodeItemsCheck:
		hw.writeString(` class="checklist"`)
	case types.NodeItemsFAQ:
		hw.writeString(` class="faq"`)
	default:
		if n.ListType != "" {
			hw.writeFmt(" type=%q", n.ListType)
		}
		if n.Start > 0 {
			hw.writeFmt(` start=%q`, strconv.Itoa(n.Start))
		}
	}
	hw.writeString(">\n")

	for _, i := range n.Items {
		hw.writeString("
")
		hw.write(i.Nodes...)
		hw.writeString("
\n")
	}

	hw.writeFmt("</%s>", tag)
}

func (hw *htmlWriter) grid(n *types.GridNode) {
	hw.writeString("
\n")
	for _, r := range n.Rows {
		hw.writeString("
")
		for _, c := range r {
			hw.writeFmt(`<td colspan="%d" rowspan="%d">`, c.Colspan, c.Rowspan)
			hw.write(c.Content.Nodes...)
			hw.writeString("	")
		}
		hw.writeString("
\n")
	}
	hw.writeString("
")
}

func (hw *htmlWriter) infobox(n *types.InfoboxNode) {
	hw.writeString(`<aside class="`)
	hw.writeEscape(string(n.Kind))
	hw.writeString(`">`)
	hw.write(n.Content.Nodes...)
	hw.writeString("")
}

func (hw *htmlWriter) survey(n *types.SurveyNode) {
	hw.writeFmt("<google-codelab-survey survey-id=%q>\n", n.ID)
	for _, g := range n.Groups {
		hw.writeString("
")
		hw.writeEscape(g.Name)
		hw.writeString("
\n<paper-radio-group>\n")
		for _, o := range g.Options {
			hw.writeString("<paper-radio-button>")
			hw.writeEscape(o)
			hw.writeString("</paper-radio-button>\n")
		}
		hw.writeString("</paper-radio-group>\n")
	}
	hw.writeString("</google-codelab-survey>")
}

func (hw *htmlWriter) header(n *types.HeaderNode) {
	tag := "h" + strconv.Itoa(n.Level)
	hw.writeString("<")
	hw.writeString(tag)
	switch n.Type() {
	case types.NodeHeaderCheck:
		hw.writeString(` class="checklist"`)
	case types.NodeHeaderFAQ:
		hw.writeString(` class="faq"`)

	}
	hw.writeString(` is-upgraded>`)
	hw.write(n.Content.Nodes...)
	hw.writeFmt("</%s>", tag)
}

func (hw *htmlWriter) youtube(n *types.YouTubeNode) {
	hw.writeFmt(`<iframe class="youtube-video" `+
		`src="https://www.youtube.com/embed/%s?rel=0" allow="accelerometer; `+
		`autoplay; encrypted-media; gyroscope; picture-in-picture" `+
		`allowfullscreen></iframe>`, n.VideoID)
}

func (hw *htmlWriter) iframe(n *types.IframeNode) {
	hw.writeFmt(`<iframe class="embedded-iframe" src=%q></iframe>`, n.URL)
}
<commit_msg>Further refactor code by inlining some escaping.<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// 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 render

import (
	"bytes"
	"fmt"
	htmlTemplate "html/template"
	"io"
	"sort"
	"strconv"
	"strings"

	"github.com/googlecodelabs/tools/claat/types"
)

// TODO: render HTML using golang/x/net/html or template.

// HTML renders nodes as the markup for the target env.
func HTML(ctx Context, nodes ...types.Node) (htmlTemplate.HTML, error) {
	var buf bytes.Buffer
	if err := WriteHTML(&buf, ctx.Env, ctx.Format, nodes...); err != nil {
		return "", err
	}
	return htmlTemplate.HTML(buf.String()), nil
}

// WriteHTML does the same as HTML but outputs rendered markup to w.
func WriteHTML(w io.Writer, env string, fmt string, nodes ...types.Node) error {
	hw := htmlWriter{w: w, env: env, format: fmt}
	return hw.write(nodes...)
}

// ReplaceDoubleCurlyBracketsWithEntity replaces Double Curly Brackets with their charater entity.
func ReplaceDoubleCurlyBracketsWithEntity(s string) string {
	return strings.Replace(s, "{{", "{{", -1)
}

type htmlWriter struct {
	w      io.Writer // output writer
	env    string    // target environment
	format string    // target template
	err    error     // error during any writeXxx methods
}

func (hw *htmlWriter) matchEnv(v []string) bool {
	if len(v) == 0 || hw.env == "" {
		return true
	}
	i := sort.SearchStrings(v, hw.env)
	return i < len(v) && v[i] == hw.env
}

func (hw *htmlWriter) write(nodes ...types.Node) error {
	for _, n := range nodes {
		if !hw.matchEnv(n.Env()) {
			continue
		}
		switch n := n.(type) {
		case *types.TextNode:
			hw.text(n)
		case *types.ImageNode:
			hw.image(n)
		case *types.URLNode:
			hw.url(n)
		case *types.ButtonNode:
			hw.button(n)
		case *types.CodeNode:
			hw.code(n)
			hw.writeString("\n")
		case *types.ListNode:
			hw.list(n)
			hw.writeString("\n")
		case *types.ImportNode:
			if len(n.Content.Nodes) == 0 {
				break
			}
			hw.list(n.Content)
			hw.writeString("\n")
		case *types.ItemsListNode:
			hw.itemsList(n)
			hw.writeString("\n")
		case *types.GridNode:
			hw.grid(n)
			hw.writeString("\n")
		case *types.InfoboxNode:
			hw.infobox(n)
			hw.writeString("\n")
		case *types.SurveyNode:
			hw.survey(n)
			hw.writeString("\n")
		case *types.HeaderNode:
			hw.header(n)
			hw.writeString("\n")
		case *types.YouTubeNode:
			hw.youtube(n)
			hw.writeString("\n")
		case *types.IframeNode:
			hw.iframe(n)
			hw.writeString("\n")
		}
		if hw.err != nil {
			return hw.err
		}
	}
	return nil
}

// Writes a string to the htmlWriter unless a write error has occurred on this htmlWriter in the past.
// Will set a write error on this htmlWriter if the write fails.
func (hw *htmlWriter) writeString(s string) {
	if hw.err != nil {
		return
	}
	_, hw.err = hw.w.Write([]byte(s))
}

// Same as writeString, but with fmt.Sprintf arguments/semantics.
func (hw *htmlWriter) writeFmt(f string, a ...interface{}) {
	hw.writeString(fmt.Sprintf(f, a...))
}

func escape(s string) string {
	s = htmlTemplate.HTMLEscapeString(s)
	s = ReplaceDoubleCurlyBracketsWithEntity(s)
	return s
}

// Same as writeString, but performs HTML escaping and double curly bracket escaping.
func (hw *htmlWriter) writeEscape(s string) {
	hw.writeString(escape(s))
}

func (hw *htmlWriter) text(n *types.TextNode) {
	if n.Bold {
		hw.writeString("")
	}
	if n.Italic {
		hw.writeString("")
	}
	if n.Code {
		hw.writeString("")
	}
	s := htmlTemplate.HTMLEscapeString(n.Value)
	s = ReplaceDoubleCurlyBracketsWithEntity(s)
	hw.writeString(strings.Replace(s, "\n", "
", -1))
	if n.Code {
		hw.writeString("")
	}
	if n.Italic {
		hw.writeString("")
	}
	if n.Bold {
		hw.writeString("")
	}
}

func (hw *htmlWriter) image(n *types.ImageNode) {
	hw.writeString(" 0 {
		hw.writeFmt(` style="width: %.2fpx"`, n.Width)
	}
	hw.writeFmt(" src=%q>", n.Src)
}

func (hw *htmlWriter) url(n *types.URLNode) {
	hw.writeString("")
	hw.write(n.Content.Nodes...)
	hw.writeString("")
}

func (hw *htmlWriter) button(n *types.ButtonNode) {
	hw.writeString("<paper-button")
	if n.Colored {
		hw.writeString(` class="colored"`)
	}
	if n.Raised {
		hw.writeString(" raised")
	}
	hw.writeString(">")
	if n.Download {
		hw.writeString(`<iron-icon icon="file-download"></iron-icon>`)
	}
	hw.write(n.Content.Nodes...)
	hw.writeString("</paper-button>")
}

func (hw *htmlWriter) code(n *types.CodeNode) {
	hw.writeString("
")
	if !n.Term {
		hw.writeString("")
	}
	if hw.format == "devsite" {
		hw.writeString("{% verbatim %}")
	}
	hw.writeEscape(n.Value)
	if hw.format == "devsite" {
		hw.writeString("{% endverbatim %}")
	}
	if !n.Term {
		hw.writeString("")
	}
	hw.writeString("
")
}

func (hw *htmlWriter) list(n *types.ListNode) {
	wrap := n.Block() == true
	if wrap {
		if onlyImages(n.Nodes...) {
			hw.writeString(`<p class="image-container">`)
		} else {
			hw.writeString("
")
		}
	}
	hw.write(n.Nodes...)
	if wrap {
		hw.writeString("
")
	}
}

// Returns true if the list of Nodes contains only images or white spaces.
func onlyImages(nodes ...types.Node) bool {
	for _, n := range nodes {
		switch n := n.(type) {
		case *types.TextNode:
			if len(strings.TrimSpace(n.Value)) == 0 {
				continue
			}
			return false
		case *types.ImageNode:
			continue
		default:
			return false
		}
	}
	return true
}

func (hw *htmlWriter) itemsList(n *types.ItemsListNode) {
	tag := "ul"
	if n.Type() == types.NodeItemsList && (n.Start > 0 || n.ListType != "") {
		tag = "ol"
	}
	hw.writeString("<")
	hw.writeString(tag)
	switch n.Type() {
	case types.NodeItemsCheck:
		hw.writeString(` class="checklist"`)
	case types.NodeItemsFAQ:
		hw.writeString(` class="faq"`)
	default:
		if n.ListType != "" {
			hw.writeFmt(" type=%q", n.ListType)
		}
		if n.Start > 0 {
			hw.writeFmt(` start=%q`, strconv.Itoa(n.Start))
		}
	}
	hw.writeString(">\n")

	for _, i := range n.Items {
		hw.writeString("
")
		hw.write(i.Nodes...)
		hw.writeString("
\n")
	}

	hw.writeFmt("</%s>", tag)
}

func (hw *htmlWriter) grid(n *types.GridNode) {
	hw.writeString("
\n")
	for _, r := range n.Rows {
		hw.writeString("
")
		for _, c := range r {
			hw.writeFmt(`<td colspan="%d" rowspan="%d">`, c.Colspan, c.Rowspan)
			hw.write(c.Content.Nodes...)
			hw.writeString("	")
		}
		hw.writeString("
\n")
	}
	hw.writeString("
")
}

func (hw *htmlWriter) infobox(n *types.InfoboxNode) {
	hw.writeFmt("", escape(n.Kind))
	hw.write(n.Content.Nodes...)
	hw.writeString("")
}

func (hw *htmlWriter) survey(n *types.SurveyNode) {
	hw.writeFmt("<google-codelab-survey survey-id=%q>\n", n.ID)
	for _, g := range n.Groups {
		hw.writeFmt("
%s
\n<paper-radio-group>\n", g.Name)
		for _, o := range g.Options {
			hw.writeFmt("<paper-radio-button>%s</paper-radio-button>\n", escape(o))
		}
		hw.writeString("</paper-radio-group>\n")
	}
	hw.writeString("</google-codelab-survey>")
}

func (hw *htmlWriter) header(n *types.HeaderNode) {
	tag := "h" + strconv.Itoa(n.Level)
	hw.writeString("<")
	hw.writeString(tag)
	switch n.Type() {
	case types.NodeHeaderCheck:
		hw.writeString(` class="checklist"`)
	case types.NodeHeaderFAQ:
		hw.writeString(` class="faq"`)

	}
	hw.writeString(` is-upgraded>`)
	hw.write(n.Content.Nodes...)
	hw.writeFmt("</%s>", tag)
}

func (hw *htmlWriter) youtube(n *types.YouTubeNode) {
	hw.writeFmt(`<iframe class="youtube-video" `+
		`src="https://www.youtube.com/embed/%s?rel=0" allow="accelerometer; `+
		`autoplay; encrypted-media; gyroscope; picture-in-picture" `+
		`allowfullscreen></iframe>`, n.VideoID)
}

func (hw *htmlWriter) iframe(n *types.IframeNode) {
	hw.writeFmt(`<iframe class="embedded-iframe" src=%q></iframe>`, n.URL)
}
<|endoftext|>


<commit_before>package nyaafeeds

// rss support
// validation done according to spec here:
//    http://cyber.law.harvard.edu/rss/rss.html

import (
	"encoding/xml"
	"fmt"
	"strconv"
	"time"

	"github.com/gorilla/feeds"
)

// private wrapper around the RssFeed which gives us the <rss>..</rss> xml
type rssFeedXML struct {
	XMLName  xml.Name `xml:"rss"`
	Xmlns    string   `xml:"xmlns:torznab,attr,omitempty"`
	Version  string   `xml:"version,attr"`
	Encoding string   `xml:"encoding,attr"`
	Channel  *RssFeed `xml:"channel,omitempty"`
	Caps     *RssCaps `xml:"caps,omitempty"`
}

type RssImage struct {
	XMLName xml.Name `xml:"image"`
	URL     string   `xml:"url"`
	Title   string   `xml:"title"`
	Link    string   `xml:"link"`
	Width   int      `xml:"width,omitempty"`
	Height  int      `xml:"height,omitempty"`
}

type RssTextInput struct {
	XMLName     xml.Name `xml:"textInput"`
	Title       string   `xml:"title"`
	Description string   `xml:"description"`
	Name        string   `xml:"name"`
	Link        string   `xml:"link"`
}

type RssFeed struct {
	XMLName        xml.Name `xml:"channel"`
	Xmlns          string   `xml:"-"`
	Title          string   `xml:"title"`       // required
	Link           string   `xml:"link"`        // required
	Description    string   `xml:"description"` // required
	Language       string   `xml:"language,omitempty"`
	Copyright      string   `xml:"copyright,omitempty"`
	ManagingEditor string   `xml:"managingEditor,omitempty"` // Author used
	WebMaster      string   `xml:"webMaster,omitempty"`
	PubDate        string   `xml:"pubDate,omitempty"`       // created or updated
	LastBuildDate  string   `xml:"lastBuildDate,omitempty"` // updated used
	Category       string   `xml:"category,omitempty"`
	Generator      string   `xml:"generator,omitempty"`
	Docs           string   `xml:"docs,omitempty"`
	Cloud          string   `xml:"cloud,omitempty"`
	TTL            int      `xml:"ttl,omitempty"`
	Rating         string   `xml:"rating,omitempty"`
	SkipHours      string   `xml:"skipHours,omitempty"`
	SkipDays       string   `xml:"skipDays,omitempty"`
	Image          *RssImage
	TextInput      *RssTextInput
	Items          []*RssItem
}

type RssItem struct {
	XMLName     xml.Name     `xml:"item"`
	Title       string       `xml:"title"`       // required
	Link        string       `xml:"link"`        // required
	Description string       `xml:"description"` // required
	Author      string       `xml:"author,omitempty"`
	Category    *RssCategory `xml:"category,omitempty"`
	Comments    string       `xml:"comments,omitempty"`
	Enclosure   *RssEnclosure
	GUID        string      `xml:"guid,omitempty"`    // Id used
	PubDate     string      `xml:"pubDate,omitempty"` // created or updated
	Source      string      `xml:"source,omitempty"`
	Torrent     *RssTorrent `xml:"torrent,omitempty"`
	Torznab     []*RssTorznab
}

type RssCaps struct {
	XMLName      xml.Name         `xml:"caps"`
	Server       *RssServer       `xml:"server,omitempty"`
	Limits       *RssLimits       `xml:"limits,omitempty"`
	Registration *RssRegistration `xml:"registration,omitempty"`
	Searching    *RssSearching    `xml:"searching,omitempty"`
	Categories   *RssCategories   `xml:"categories,omitempty"`
}

type RssServer struct {
	XMLName   xml.Name `xml:"server"`
	Xmlns     string   `xml:"xmlns,attr"`
	Version   string   `xml:"version,attr"`
	Title     string   `xml:"title,attr"`
	Strapline string   `xml:"strapline,attr"`
	Email     string   `xml:"email,attr"`
	URL       string   `xml:"url,attr"`
	Image     string   `xml:"image,attr"`
}

type RssLimits struct {
	XMLName xml.Name `xml:"limits"`
	Max     string   `xml:"max,attr"`
	Default string   `xml:"default,attr"`
}

type RssRegistration struct {
	XMLName   xml.Name `xml:"registration"`
	Available string   `xml:"available,attr"`
	Open      string   `xml:"open,attr"`
}

type RssSearching struct {
	XMLName     xml.Name   `xml:"searching"`
	Search      *RssSearch `xml:"search,omitempty"`
	TvSearch    *RssSearch `xml:"tv-search,omitempty"`
	MovieSearch *RssSearch `xml:"movie-search,omitempty"`
}

type RssSearch struct {
	Available       string `xml:"available,attr"`
	SupportedParams string `xml:"supportedParams,attr,omitempty"`
}

type RssCategories struct {
	XMLName  xml.Name `xml:"categories"`
	Category []*RssCategoryTorznab
}

type RssCategoryTorznab struct {
	XMLName     xml.Name `xml:"category"`
	ID          string   `xml:"id,attr"`
	Name        string   `xml:"name,attr"`
	Subcat      []*RssSubCat
	Description string `xml:"description,attr,omitempty"`
}

type RssSubCat struct {
	XMLName     xml.Name `xml:"subcat"`
	ID          string   `xml:"id,attr"`
	Name        string   `xml:"name,attr"`
	Description string   `xml:"description,attr,omitempty"`
}

type RssTorrent struct {
	XMLName       xml.Name `xml:"torrent"`
	Xmlns         string   `xml:"xmlns,attr"`
	FileName      string   `xml:"fileName,omitempty"`
	ContentLength string   `xml:"contentLength,omitempty"`
	InfoHash      string   `xml:"infoHash,omitempty"`
	MagnetURI     string   `xml:"magnetUri,omitempty"`
}

type RssTorznab struct {
	XMLName xml.Name `xml:"torznab:attr"`
	Name    string   `xml:"name,attr"`
	Value   string   `xml:"value,attr,omitempty"`
}

// RssCategory is a category for rss item
type RssCategory struct {
	XMLName xml.Name `xml:"category"`
	Domain  string   `xml:"domain"`
}

type RssEnclosure struct {
	//RSS 2.0 <enclosure url="http://example.com/file.mp3" length="123456789" type="audio/mpeg" />
	XMLName xml.Name `xml:"enclosure"`
	URL     string   `xml:"url,attr"`
	Length  string   `xml:"length,attr"`
	Type    string   `xml:"type,attr"`
}

type Rss struct {
	*feeds.Feed
}

// create a new RssItem with a generic Item struct's data
func newRssItem(i *feeds.Item) *RssItem {
	item := &RssItem{
		Title:       i.Title,
		Link:        i.Link.Href,
		Description: i.Description,
		GUID:        i.Id,
		PubDate:     anyTimeFormat(time.RFC1123Z, i.Created, i.Updated),
	}

	intLength, err := strconv.ParseInt(i.Link.Length, 10, 64)

	if err == nil && (intLength > 0 || i.Link.Type != "") {
		item.Enclosure = &RssEnclosure{URL: i.Link.Href, Type: i.Link.Type, Length: i.Link.Length}
	}
	if i.Author != nil {
		item.Author = i.Author.Name
	}
	return item
}

// returns the first non-zero time formatted as a string or ""
func anyTimeFormat(format string, times ...time.Time) string {
	for _, t := range times {
		if !t.IsZero() {
			return t.Format(format)
		}
	}
	return ""
}

// RssFeed : create a new RssFeed with a generic Feed struct's data
func (r *Rss) RssFeed() *RssFeed {
	pub := anyTimeFormat(time.RFC1123Z, r.Created, r.Updated)
	build := anyTimeFormat(time.RFC1123Z, r.Updated)
	author := ""
	if r.Author != nil {
		author = r.Author.Email
		if len(r.Author.Name) > 0 {
			author = fmt.Sprintf("%s (%s)", r.Author.Email, r.Author.Name)
		}
	}

	channel := &RssFeed{
		Title:          r.Title,
		Link:           r.Link.Href,
		Description:    r.Description,
		ManagingEditor: author,
		PubDate:        pub,
		LastBuildDate:  build,
		Copyright:      r.Copyright,
	}
	for _, i := range r.Items {
		channel.Items = append(channel.Items, newRssItem(i))
	}
	return channel
}

// FeedXml : return an XML-Ready object for an Rss object
func (r *Rss) FeedXml() interface{} {
	// only generate version 2.0 feeds for now
	return r.RssFeed().FeedXml()

}

// FeedXml : return an XML-ready object for an RssFeed object
func (r *RssFeed) FeedXml() interface{} {
	if r.Xmlns != "" {
		return &rssFeedXML{Version: "2.0", Encoding: "UTF-8", Channel: r, Xmlns: r.Xmlns}
	}
	return &rssFeedXML{Version: "2.0", Encoding: "UTF-8", Channel: r}
}

// FeedXml : return an XML-ready object for an RssFeed object
func (r *RssCaps) FeedXml() interface{} {
	return r
}
<commit_msg>Hopefully fix torznab api<commit_after>package nyaafeeds

// rss support
// validation done according to spec here:
//    http://cyber.law.harvard.edu/rss/rss.html

import (
	"encoding/xml"
	"fmt"
	"strconv"
	"time"

	"github.com/gorilla/feeds"
)

// private wrapper around the RssFeed which gives us the <rss>..</rss> xml
type rssFeedXML struct {
	XMLName  xml.Name `xml:"rss"`
	Xmlns    string   `xml:"xmlns:torznab,attr,omitempty"`
	Version  string   `xml:"version,attr"`
	Encoding string   `xml:"encoding,attr"`
	Channel  *RssFeed `xml:"channel,omitempty"`
	Caps     *RssCaps `xml:"caps,omitempty"`
}

type RssImage struct {
	XMLName xml.Name `xml:"image"`
	URL     string   `xml:"url"`
	Title   string   `xml:"title"`
	Link    string   `xml:"link"`
	Width   int      `xml:"width,omitempty"`
	Height  int      `xml:"height,omitempty"`
}

type RssTextInput struct {
	XMLName     xml.Name `xml:"textInput"`
	Title       string   `xml:"title"`
	Description string   `xml:"description"`
	Name        string   `xml:"name"`
	Link        string   `xml:"link"`
}

type RssFeed struct {
	XMLName        xml.Name `xml:"channel"`
	Xmlns          string   `xml:"-"`
	Title          string   `xml:"title"`       // required
	Link           string   `xml:"link"`        // required
	Description    string   `xml:"description"` // required
	Language       string   `xml:"language,omitempty"`
	Copyright      string   `xml:"copyright,omitempty"`
	ManagingEditor string   `xml:"managingEditor,omitempty"` // Author used
	WebMaster      string   `xml:"webMaster,omitempty"`
	PubDate        string   `xml:"pubDate,omitempty"`       // created or updated
	LastBuildDate  string   `xml:"lastBuildDate,omitempty"` // updated used
	Category       string   `xml:"category,omitempty"`
	Generator      string   `xml:"generator,omitempty"`
	Docs           string   `xml:"docs,omitempty"`
	Cloud          string   `xml:"cloud,omitempty"`
	TTL            int      `xml:"ttl,omitempty"`
	Rating         string   `xml:"rating,omitempty"`
	SkipHours      string   `xml:"skipHours,omitempty"`
	SkipDays       string   `xml:"skipDays,omitempty"`
	Image          *RssImage
	TextInput      *RssTextInput
	Items          []*RssItem
}

type RssItem struct {
	XMLName     xml.Name     `xml:"item"`
	Title       string       `xml:"title"`       // required
	Link        string       `xml:"link"`        // required
	Description string       `xml:"description"` // required
	Author      string       `xml:"author,omitempty"`
	Category    *RssCategory `xml:"category,omitempty"`
	Comments    string       `xml:"comments,omitempty"`
	Enclosure   *RssEnclosure
	GUID        string      `xml:"guid,omitempty"`    // Id used
	PubDate     string      `xml:"pubDate,omitempty"` // created or updated
	Source      string      `xml:"source,omitempty"`
	Torrent     *RssTorrent `xml:"torrent,omitempty"`
	Torznab     []*RssTorznab
}

type RssCaps struct {
	XMLName      xml.Name         `xml:"caps"`
	Server       *RssServer       `xml:"server,omitempty"`
	Limits       *RssLimits       `xml:"limits,omitempty"`
	Registration *RssRegistration `xml:"registration,omitempty"`
	Searching    *RssSearching    `xml:"searching,omitempty"`
	Categories   *RssCategories   `xml:"categories,omitempty"`
}

type RssServer struct {
	XMLName   xml.Name `xml:"server"`
	Xmlns     string   `xml:"xmlns,attr"`
	Version   string   `xml:"version,attr"`
	Title     string   `xml:"title,attr"`
	Strapline string   `xml:"strapline,attr"`
	Email     string   `xml:"email,attr"`
	URL       string   `xml:"url,attr"`
	Image     string   `xml:"image,attr"`
}

type RssLimits struct {
	XMLName xml.Name `xml:"limits"`
	Max     string   `xml:"max,attr"`
	Default string   `xml:"default,attr"`
}

type RssRegistration struct {
	XMLName   xml.Name `xml:"registration"`
	Available string   `xml:"available,attr"`
	Open      string   `xml:"open,attr"`
}

type RssSearching struct {
	XMLName     xml.Name   `xml:"searching"`
	Search      *RssSearch `xml:"search,omitempty"`
	TvSearch    *RssSearch `xml:"tv-search,omitempty"`
	MovieSearch *RssSearch `xml:"movie-search,omitempty"`
}

type RssSearch struct {
	Available       string `xml:"available,attr"`
	SupportedParams string `xml:"supportedParams,attr,omitempty"`
}

type RssCategories struct {
	XMLName  xml.Name `xml:"categories"`
	Category []*RssCategoryTorznab
}

type RssCategoryTorznab struct {
	XMLName     xml.Name `xml:"category"`
	ID          string   `xml:"id,attr"`
	Name        string   `xml:"name,attr"`
	Subcat      []*RssSubCat
	Description string `xml:"description,attr,omitempty"`
}

type RssSubCat struct {
	XMLName     xml.Name `xml:"subcat"`
	ID          string   `xml:"id,attr"`
	Name        string   `xml:"name,attr"`
	Description string   `xml:"description,attr,omitempty"`
}

type RssTorrent struct {
	XMLName       xml.Name `xml:"torrent"`
	Xmlns         string   `xml:"xmlns,attr"`
	FileName      string   `xml:"fileName,omitempty"`
	ContentLength string   `xml:"contentLength,omitempty"`
	InfoHash      string   `xml:"infoHash,omitempty"`
	MagnetURI     string   `xml:"magnetUri,omitempty"`
}

type RssTorznab struct {
	XMLName xml.Name `xml:"torznab:attr,omitempty"`
	Name    string   `xml:"name,attr,omitempty"`
	Value   string   `xml:"value,attr,omitempty"`
}

// RssCategory is a category for rss item
type RssCategory struct {
	XMLName xml.Name `xml:"category"`
	Domain  string   `xml:"domain"`
}

type RssEnclosure struct {
	//RSS 2.0 <enclosure url="http://example.com/file.mp3" length="123456789" type="audio/mpeg" />
	XMLName xml.Name `xml:"enclosure"`
	URL     string   `xml:"url,attr"`
	Length  string   `xml:"length,attr"`
	Type    string   `xml:"type,attr"`
}

type Rss struct {
	*feeds.Feed
}

// create a new RssItem with a generic Item struct's data
func newRssItem(i *feeds.Item) *RssItem {
	item := &RssItem{
		Title:       i.Title,
		Link:        i.Link.Href,
		Description: i.Description,
		GUID:        i.Id,
		PubDate:     anyTimeFormat(time.RFC1123Z, i.Created, i.Updated),
	}

	intLength, err := strconv.ParseInt(i.Link.Length, 10, 64)

	if err == nil && (intLength > 0 || i.Link.Type != "") {
		item.Enclosure = &RssEnclosure{URL: i.Link.Href, Type: i.Link.Type, Length: i.Link.Length}
	}
	if i.Author != nil {
		item.Author = i.Author.Name
	}
	return item
}

// returns the first non-zero time formatted as a string or ""
func anyTimeFormat(format string, times ...time.Time) string {
	for _, t := range times {
		if !t.IsZero() {
			return t.Format(format)
		}
	}
	return ""
}

// RssFeed : create a new RssFeed with a generic Feed struct's data
func (r *Rss) RssFeed() *RssFeed {
	pub := anyTimeFormat(time.RFC1123Z, r.Created, r.Updated)
	build := anyTimeFormat(time.RFC1123Z, r.Updated)
	author := ""
	if r.Author != nil {
		author = r.Author.Email
		if len(r.Author.Name) > 0 {
			author = fmt.Sprintf("%s (%s)", r.Author.Email, r.Author.Name)
		}
	}

	channel := &RssFeed{
		Title:          r.Title,
		Link:           r.Link.Href,
		Description:    r.Description,
		ManagingEditor: author,
		PubDate:        pub,
		LastBuildDate:  build,
		Copyright:      r.Copyright,
	}
	for _, i := range r.Items {
		channel.Items = append(channel.Items, newRssItem(i))
	}
	return channel
}

// FeedXml : return an XML-Ready object for an Rss object
func (r *Rss) FeedXml() interface{} {
	// only generate version 2.0 feeds for now
	return r.RssFeed().FeedXml()

}

// FeedXml : return an XML-ready object for an RssFeed object
func (r *RssFeed) FeedXml() interface{} {
	if r.Xmlns != "" {
		return &rssFeedXML{Version: "2.0", Encoding: "UTF-8", Channel: r, Xmlns: r.Xmlns}
	}
	return &rssFeedXML{Version: "2.0", Encoding: "UTF-8", Channel: r}
}

// FeedXml : return an XML-ready object for an RssFeed object
func (r *RssCaps) FeedXml() interface{} {
	return r
}
<|endoftext|>


<commit_before>package helm

import (
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"

	"github.com/ghodss/yaml"
	log "github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

	argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
	"github.com/argoproj/argo-cd/util"
	"github.com/argoproj/argo-cd/util/config"
	"github.com/argoproj/argo-cd/util/kube"
)

// Helm provides wrapper functionality around the `helm` command.
type Helm interface {
	// Template returns a list of unstructured objects from a `helm template` command
	Template(appName string, namespace string, opts *argoappv1.ApplicationSourceHelm) ([]*unstructured.Unstructured, error)
	// GetParameters returns a list of chart parameters taking into account values in provided YAML files.
	GetParameters(valuesFiles []string) ([]*argoappv1.HelmParameter, error)
	// DependencyBuild runs `helm dependency build` to download a chart's dependencies
	DependencyBuild() error
	// SetHome sets the helm home location (default "~/.helm")
	SetHome(path string)
	// Init runs `helm init --client-only`
	Init() error
}

// NewHelmApp create a new wrapper to run commands on the `helm` command-line tool.
func NewHelmApp(path string, helmRepos []*argoappv1.HelmRepository) Helm {
	return &helm{path: path, helmRepos: helmRepos}
}

type helm struct {
	path             string
	home             string
	helmRepos        []*argoappv1.HelmRepository
	reposInitialized bool
}

// IsMissingDependencyErr tests if the error is related to a missing chart dependency
func IsMissingDependencyErr(err error) bool {
	return strings.Contains(err.Error(), "found in requirements.yaml, but missing in charts")
}

func (h *helm) Template(appName string, namespace string, opts *argoappv1.ApplicationSourceHelm) ([]*unstructured.Unstructured, error) {
	args := []string{
		"template", ".", "--name", appName,
	}
	if namespace != "" {
		args = append(args, "--namespace", namespace)
	}
	if opts != nil {
		for _, valuesFile := range opts.ValueFiles {
			args = append(args, "-f", valuesFile)
		}
		for _, p := range opts.Parameters {
			args = append(args, "--set", fmt.Sprintf("%s=%s", p.Name, p.Value))
		}
	}
	out, err := h.helmCmd(args...)
	if err != nil {
		return nil, err
	}
	return kube.SplitYAML(out)
}

func (h *helm) DependencyBuild() error {
	if !h.reposInitialized {
		var files []*os.File
		defer func() {
			for i := range files {
				util.DeleteFile(files[i].Name())
			}
		}()

		for _, repo := range h.helmRepos {
			args := []string{"repo", "add"}

			for flag, data := range map[string][]byte{"--ca-file": repo.CAData, "--cert-file": repo.CertData, "--key-file": repo.KeyData} {
				if repo.KeyData != nil {
					f, err := ioutil.TempFile(util.TempDir, "")
					if err != nil {
						return fmt.Errorf("failed to generate temp file for %s: %v", flag, err)
					}
					files = append(files, f)
					_, err = f.Write(data)
					if err != nil {
						return fmt.Errorf("failed to write temp file for %s: %v", flag, err)
					}
					_ = f.Close()
					args = append(args, flag, f.Name())
				}
			}
			if repo.Username != "" {
				args = append(args, "--username", repo.Username)
			}
			if repo.Password != "" {
				args = append(args, "--password", repo.Password)
			}

			_, err := h.helmCmdExt(append(args, repo.Name, repo.URL), func(log string) string {
				if repo.Username != "" {
					log = strings.Replace(log, fmt.Sprintf("--username %s", repo.Username), "--username ***", 1)
				}
				if repo.Password != "" {
					log = strings.Replace(log, fmt.Sprintf("--password %s", repo.Password), "--password ***", 1)
				}
				return log
			})
			if err != nil {
				return err
			}
		}
	}
	_, err := h.helmCmd("dependency", "build")
	return err
}

func (h *helm) SetHome(home string) {
	h.home = home
}

func (h *helm) Init() error {
	if h.home == "" {
		home, err := ioutil.TempDir("", "helm")
		if err != nil {
			return err
		}
		h.home = home
	}
	_, err := h.helmCmd("init", "--client-only", "--skip-refresh")
	return err
}

func (h *helm) GetParameters(valuesFiles []string) ([]*argoappv1.HelmParameter, error) {
	out, err := h.helmCmd("inspect", "values", ".")
	if err != nil {
		return nil, err
	}
	values := append([]string{out})
	for _, file := range valuesFiles {
		var fileValues []byte
		parsedURL, err := url.ParseRequestURI(file)
		if err == nil && (parsedURL.Scheme == "http" || parsedURL.Scheme == "https") {
			fileValues, err = config.ReadRemoteFile(file)
		} else {
			fileValues, err = ioutil.ReadFile(path.Join(h.path, file))
		}
		if err != nil {
			return nil, fmt.Errorf("failed to read value file %s: %s", file, err)
		}
		values = append(values, string(fileValues))
	}

	output := map[string]string{}
	for _, file := range values {
		values := map[string]interface{}{}
		if err = yaml.Unmarshal([]byte(file), &values); err != nil {
			return nil, fmt.Errorf("failed to parse values: %s", err)
		}
		flatVals(values, output)
	}

	params := make([]*argoappv1.HelmParameter, 0)
	for key, val := range output {
		params = append(params, &argoappv1.HelmParameter{
			Name:  key,
			Value: val,
		})
	}
	sort.Slice(params, func(i, j int) bool {
		return params[i].Name < params[j].Name
	})
	return params, nil
}

func (h *helm) helmCmd(args ...string) (string, error) {
	return h.helmCmdExt(args, func(s string) string {
		return s
	})
}

func (h *helm) helmCmdExt(args []string, logFormat func(string) string) (string, error) {
	cmd := exec.Command("helm", args...)
	cmd.Dir = h.path
	if h.home != "" {
		cmd.Env = append(cmd.Env, fmt.Sprintf("HELM_HOME=%s", h.home))
	}
	cmdStr := logFormat(strings.Join(cmd.Args, " "))
	log.Info(cmdStr)
	outBytes, err := cmd.Output()
	if err != nil {
		exErr, ok := err.(*exec.ExitError)
		if !ok {
			return "", err
		}
		errOutput := string(exErr.Stderr)
		log.Errorf("`%s` failed: %s", cmdStr, errOutput)
		return "", fmt.Errorf(strings.TrimSpace(errOutput))
	}
	out := string(outBytes)
	log.Debug(out)
	return out, nil
}

func flatVals(input map[string]interface{}, output map[string]string, prefixes ...string) {
	for key, val := range input {
		if subMap, ok := val.(map[string]interface{}); ok {
			flatVals(subMap, output, append(prefixes, fmt.Sprintf("%v", key))...)
		} else {
			output[strings.Join(append(prefixes, fmt.Sprintf("%v", key)), ".")] = fmt.Sprintf("%v", val)
		}
	}
}
<commit_msg>Honor os environment variables for helm commands (#1306)<commit_after>package helm

import (
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"

	"github.com/ghodss/yaml"
	log "github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

	argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
	"github.com/argoproj/argo-cd/util"
	"github.com/argoproj/argo-cd/util/config"
	"github.com/argoproj/argo-cd/util/kube"
)

// Helm provides wrapper functionality around the `helm` command.
type Helm interface {
	// Template returns a list of unstructured objects from a `helm template` command
	Template(appName string, namespace string, opts *argoappv1.ApplicationSourceHelm) ([]*unstructured.Unstructured, error)
	// GetParameters returns a list of chart parameters taking into account values in provided YAML files.
	GetParameters(valuesFiles []string) ([]*argoappv1.HelmParameter, error)
	// DependencyBuild runs `helm dependency build` to download a chart's dependencies
	DependencyBuild() error
	// SetHome sets the helm home location (default "~/.helm")
	SetHome(path string)
	// Init runs `helm init --client-only`
	Init() error
}

// NewHelmApp create a new wrapper to run commands on the `helm` command-line tool.
func NewHelmApp(path string, helmRepos []*argoappv1.HelmRepository) Helm {
	return &helm{path: path, helmRepos: helmRepos}
}

type helm struct {
	path             string
	home             string
	helmRepos        []*argoappv1.HelmRepository
	reposInitialized bool
}

// IsMissingDependencyErr tests if the error is related to a missing chart dependency
func IsMissingDependencyErr(err error) bool {
	return strings.Contains(err.Error(), "found in requirements.yaml, but missing in charts")
}

func (h *helm) Template(appName string, namespace string, opts *argoappv1.ApplicationSourceHelm) ([]*unstructured.Unstructured, error) {
	args := []string{
		"template", ".", "--name", appName,
	}
	if namespace != "" {
		args = append(args, "--namespace", namespace)
	}
	if opts != nil {
		for _, valuesFile := range opts.ValueFiles {
			args = append(args, "-f", valuesFile)
		}
		for _, p := range opts.Parameters {
			args = append(args, "--set", fmt.Sprintf("%s=%s", p.Name, p.Value))
		}
	}
	out, err := h.helmCmd(args...)
	if err != nil {
		return nil, err
	}
	return kube.SplitYAML(out)
}

func (h *helm) DependencyBuild() error {
	if !h.reposInitialized {
		var files []*os.File
		defer func() {
			for i := range files {
				util.DeleteFile(files[i].Name())
			}
		}()

		for _, repo := range h.helmRepos {
			args := []string{"repo", "add"}

			for flag, data := range map[string][]byte{"--ca-file": repo.CAData, "--cert-file": repo.CertData, "--key-file": repo.KeyData} {
				if repo.KeyData != nil {
					f, err := ioutil.TempFile(util.TempDir, "")
					if err != nil {
						return fmt.Errorf("failed to generate temp file for %s: %v", flag, err)
					}
					files = append(files, f)
					_, err = f.Write(data)
					if err != nil {
						return fmt.Errorf("failed to write temp file for %s: %v", flag, err)
					}
					_ = f.Close()
					args = append(args, flag, f.Name())
				}
			}
			if repo.Username != "" {
				args = append(args, "--username", repo.Username)
			}
			if repo.Password != "" {
				args = append(args, "--password", repo.Password)
			}

			_, err := h.helmCmdExt(append(args, repo.Name, repo.URL), func(log string) string {
				if repo.Username != "" {
					log = strings.Replace(log, fmt.Sprintf("--username %s", repo.Username), "--username ***", 1)
				}
				if repo.Password != "" {
					log = strings.Replace(log, fmt.Sprintf("--password %s", repo.Password), "--password ***", 1)
				}
				return log
			})
			if err != nil {
				return err
			}
		}
	}
	_, err := h.helmCmd("dependency", "build")
	return err
}

func (h *helm) SetHome(home string) {
	h.home = home
}

func (h *helm) Init() error {
	if h.home == "" {
		home, err := ioutil.TempDir("", "helm")
		if err != nil {
			return err
		}
		h.home = home
	}
	_, err := h.helmCmd("init", "--client-only", "--skip-refresh")
	return err
}

func (h *helm) GetParameters(valuesFiles []string) ([]*argoappv1.HelmParameter, error) {
	out, err := h.helmCmd("inspect", "values", ".")
	if err != nil {
		return nil, err
	}
	values := append([]string{out})
	for _, file := range valuesFiles {
		var fileValues []byte
		parsedURL, err := url.ParseRequestURI(file)
		if err == nil && (parsedURL.Scheme == "http" || parsedURL.Scheme == "https") {
			fileValues, err = config.ReadRemoteFile(file)
		} else {
			fileValues, err = ioutil.ReadFile(path.Join(h.path, file))
		}
		if err != nil {
			return nil, fmt.Errorf("failed to read value file %s: %s", file, err)
		}
		values = append(values, string(fileValues))
	}

	output := map[string]string{}
	for _, file := range values {
		values := map[string]interface{}{}
		if err = yaml.Unmarshal([]byte(file), &values); err != nil {
			return nil, fmt.Errorf("failed to parse values: %s", err)
		}
		flatVals(values, output)
	}

	params := make([]*argoappv1.HelmParameter, 0)
	for key, val := range output {
		params = append(params, &argoappv1.HelmParameter{
			Name:  key,
			Value: val,
		})
	}
	sort.Slice(params, func(i, j int) bool {
		return params[i].Name < params[j].Name
	})
	return params, nil
}

func (h *helm) helmCmd(args ...string) (string, error) {
	return h.helmCmdExt(args, func(s string) string {
		return s
	})
}

func (h *helm) helmCmdExt(args []string, logFormat func(string) string) (string, error) {
	cmd := exec.Command("helm", args...)
	cmd.Env = os.Environ()
	cmd.Dir = h.path
	if h.home != "" {
		cmd.Env = append(cmd.Env, fmt.Sprintf("HELM_HOME=%s", h.home))
	}
	cmdStr := logFormat(strings.Join(cmd.Args, " "))
	log.Info(cmdStr)
	outBytes, err := cmd.Output()
	if err != nil {
		exErr, ok := err.(*exec.ExitError)
		if !ok {
			return "", err
		}
		errOutput := string(exErr.Stderr)
		log.Errorf("`%s` failed: %s", cmdStr, errOutput)
		return "", fmt.Errorf(strings.TrimSpace(errOutput))
	}
	out := string(outBytes)
	log.Debug(out)
	return out, nil
}

func flatVals(input map[string]interface{}, output map[string]string, prefixes ...string) {
	for key, val := range input {
		if subMap, ok := val.(map[string]interface{}); ok {
			flatVals(subMap, output, append(prefixes, fmt.Sprintf("%v", key))...)
		} else {
			output[strings.Join(append(prefixes, fmt.Sprintf("%v", key)), ".")] = fmt.Sprintf("%v", val)
		}
	}
}
<|endoftext|>