<commit_before>package builder

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	docker "github.com/fsouza/go-dockerclient"

	s2igit "github.com/openshift/source-to-image/pkg/scm/git"

	"github.com/openshift/origin/pkg/build/api"
	"github.com/openshift/origin/pkg/build/builder/cmd/dockercfg"
	"github.com/openshift/origin/pkg/generate/git"
	"github.com/openshift/source-to-image/pkg/tar"
)

const (
	// initialURLCheckTimeout is the initial timeout used to check the
	// source URL.  If fetching the URL exceeds the timeout, then a longer
	// timeout will be tried until the fetch either succeeds or the build
	// itself times out.
	initialURLCheckTimeout = 16 * time.Second

	// timeoutIncrementFactor is the factor to use when increasing
	// the timeout after each unsuccessful try
	timeoutIncrementFactor = 4
)

type gitAuthError string
type gitNotFoundError string

func (e gitAuthError) Error() string {
	return fmt.Sprintf("failed to fetch requested repository %q with provided credentials", string(e))
}

func (e gitNotFoundError) Error() string {
	return fmt.Sprintf("requested repository %q not found", string(e))
}

// fetchSource retrieves the inputs defined by the build source into the
// provided directory, or returns an error if retrieval is not possible.
func fetchSource(dockerClient DockerClient, dir string, build *api.Build, urlTimeout time.Duration, in io.Reader, gitClient GitClient) (*git.SourceInfo, error) {
	hasGitSource := false

	// expect to receive input from STDIN
	if err := extractInputBinary(in, build.Spec.Source.Binary, dir); err != nil {
		return nil, err
	}

	// may retrieve source from Git
	hasGitSource, err := extractGitSource(gitClient, build.Spec.Source.Git, build.Spec.Revision, dir, urlTimeout)
	if err != nil {
		return nil, err
	}

	var sourceInfo *git.SourceInfo
	if hasGitSource {
		var errs []error
		sourceInfo, errs = gitClient.GetInfo(dir)
		if len(errs) > 0 {
			for _, e := range errs {
				glog.V(0).Infof("error: Unable to retrieve Git info: %v", e)
			}
		}
	}

	forcePull := false
	switch {
	case build.Spec.Strategy.SourceStrategy != nil:
		forcePull = build.Spec.Strategy.SourceStrategy.ForcePull
	case build.Spec.Strategy.DockerStrategy != nil:
		forcePull = build.Spec.Strategy.DockerStrategy.ForcePull
	case build.Spec.Strategy.CustomStrategy != nil:
		forcePull = build.Spec.Strategy.CustomStrategy.ForcePull
	}
	// extract source from an Image if specified
	for i, image := range build.Spec.Source.Images {
		imageSecretIndex := i
		if image.PullSecret == nil {
			imageSecretIndex = -1
		}
		err := extractSourceFromImage(dockerClient, image.From.Name, dir, imageSecretIndex, image.Paths, forcePull)
		if err != nil {
			return nil, err
		}
	}

	// a Dockerfile has been specified, create or overwrite into the destination
	if dockerfileSource := build.Spec.Source.Dockerfile; dockerfileSource != nil {
		baseDir := dir
		// if a context dir has been defined and we cloned source, overwrite the destination
		if hasGitSource && len(build.Spec.Source.ContextDir) != 0 {
			baseDir = filepath.Join(baseDir, build.Spec.Source.ContextDir)
		}
		return sourceInfo, ioutil.WriteFile(filepath.Join(baseDir, "Dockerfile"), []byte(*dockerfileSource), 0660)
	}

	return sourceInfo, nil
}

// checkRemoteGit validates the specified Git URL. It returns GitNotFoundError
// when the remote repository not found and GitAuthenticationError when the
// remote repository failed to authenticate.
// Since this is calling the 'git' binary, the proxy settings should be
// available for this command.
func checkRemoteGit(gitClient GitClient, url string, initialTimeout time.Duration) error {

	var (
		out    string
		errOut string
		err    error
	)

	timeout := initialTimeout
	for {
		glog.V(4).Infof("git ls-remote --heads %s", url)
		out, errOut, err = gitClient.TimedListRemote(timeout, url, "--heads")
		if len(out) != 0 {
			glog.V(4).Infof(out)
		}
		if len(errOut) != 0 {
			glog.V(4).Infof(errOut)
		}
		if err != nil {
			if _, ok := err.(*git.TimeoutError); ok {
				timeout = timeout * timeoutIncrementFactor
				glog.Infof("WARNING: timed out waiting for git server, will wait %s", timeout)
				continue
			}
		}
		break
	}
	if err != nil {
		combinedOut := out + errOut
		switch {
		case strings.Contains(combinedOut, "Authentication failed"):
			return gitAuthError(url)
		case strings.Contains(combinedOut, "not found"):
			return gitNotFoundError(url)
		}
	}
	return err
}

// checkSourceURI performs a check on the URI associated with the build
// to make sure that it is valid.
func checkSourceURI(gitClient GitClient, rawurl string, timeout time.Duration) error {
	ok, err := s2igit.New().ValidCloneSpec(rawurl)
	if err != nil {
		return fmt.Errorf("Invalid git source url %q: %v", rawurl, err)
	}
	if !ok {
		return fmt.Errorf("Invalid git source url: %s", rawurl)
	}
	return checkRemoteGit(gitClient, rawurl, timeout)
}

// extractInputBinary processes the provided input stream as directed by BinaryBuildSource
// into dir.
func extractInputBinary(in io.Reader, source *api.BinaryBuildSource, dir string) error {
	if source == nil {
		return nil
	}

	var path string
	if len(source.AsFile) > 0 {
		glog.V(0).Infof("Receiving source from STDIN as file %s", source.AsFile)
		path = filepath.Join(dir, source.AsFile)

		f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0664)
		if err != nil {
			return err
		}
		defer f.Close()
		n, err := io.Copy(f, os.Stdin)
		if err != nil {
			return err
		}
		glog.V(4).Infof("Received %d bytes into %s", n, path)
		return nil
	}

	glog.V(0).Infof("Receiving source from STDIN as archive ...")

	// use bsdtar to process the incoming archive and convert it to a tar stream (since bsdtar autodetects and handles various archive formats)
	// use gnu tar to extract that tar stream to work around the bsdtar (libarchive) bug https://github.com/libarchive/libarchive/issues/746
	cmd := exec.Command("sh", "-o", "pipefail", "-c", `bsdtar -cf - @- | tar xf - -m -C "$0"`, dir)
	cmd.Stdin = in
	out, err := cmd.CombinedOutput()
	if err != nil {
		glog.V(0).Infof("Extracting...\n%s", string(out))
		return fmt.Errorf("unable to extract binary build input, must be a zip, tar, or gzipped tar, or specified as a file: %v", err)
	}
	return nil
}

func extractGitSource(gitClient GitClient, gitSource *api.GitBuildSource, revision *api.SourceRevision, dir string, timeout time.Duration) (bool, error) {
	if gitSource == nil {
		return false, nil
	}

	glog.V(0).Infof("Cloning %q ...", gitSource.URI)

	// Check source URI, trying to connect to the server only if not using a proxy.
	if err := checkSourceURI(gitClient, gitSource.URI, timeout); err != nil {
		return true, err
	}

	cloneOptions := []string{}
	usingRef := len(gitSource.Ref) != 0 || (revision != nil && revision.Git != nil && len(revision.Git.Commit) != 0)

	// check if we specify a commit, ref, or branch to check out
	// Recursive clone if we're not going to checkout a ref and submodule update later
	if !usingRef {
		cloneOptions = append(cloneOptions, "--recursive")
		cloneOptions = append(cloneOptions, git.Shallow)
	}

	glog.V(3).Infof("Cloning source from %s", gitSource.URI)

	// Only use the quiet flag if Verbosity is not 5 or greater
	if !glog.Is(5) {
		cloneOptions = append(cloneOptions, "--quiet")
	}
	if err := gitClient.CloneWithOptions(dir, gitSource.URI, cloneOptions...); err != nil {
		return true, err
	}

	// if we specify a commit, ref, or branch to checkout, do so, and update submodules
	if usingRef {
		commit := gitSource.Ref

		if revision != nil && revision.Git != nil && revision.Git.Commit != "" {
			commit = revision.Git.Commit
		}

		if err := gitClient.Checkout(dir, commit); err != nil {
			return true, err
		}

		// Recursively update --init
		if err := gitClient.SubmoduleUpdate(dir, true, true); err != nil {
			return true, err
		}
	}

	if glog.Is(0) {
		if information, gitErr := gitClient.GetInfo(dir); len(gitErr) == 0 {
			glog.Infof("\tCommit:\t%s (%s)\n", information.CommitID, information.Message)
			glog.Infof("\tAuthor:\t%s <%s>\n", information.AuthorName, information.AuthorEmail)
			glog.Infof("\tDate:\t%s\n", information.Date)
		}
	}

	return true, nil
}

func copyImageSource(dockerClient DockerClient, containerID, sourceDir, destDir string, tarHelper tar.Tar) error {
	// Setup destination directory
	fi, err := os.Stat(destDir)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		glog.V(4).Infof("Creating image destination directory: %s", destDir)
		err := os.MkdirAll(destDir, 0644)
		if err != nil {
			return err
		}
	} else {
		if !fi.IsDir() {
			return fmt.Errorf("destination %s must be a directory", destDir)
		}
	}

	tempFile, err := ioutil.TempFile("", "imgsrc")
	if err != nil {
		return err
	}
	glog.V(4).Infof("Downloading source from path %s in container %s to temporary archive %s", sourceDir, containerID, tempFile.Name())
	err = dockerClient.DownloadFromContainer(containerID, docker.DownloadFromContainerOptions{
		OutputStream: tempFile,
		Path:         sourceDir,
	})
	if err != nil {
		tempFile.Close()
		return err
	}
	if err := tempFile.Close(); err != nil {
		return err
	}

	// Extract the created tar file to the destination directory
	file, err := os.Open(tempFile.Name())
	if err != nil {
		return err
	}
	defer file.Close()

	glog.V(4).Infof("Extracting temporary tar %s to directory %s", tempFile.Name(), destDir)
	var tarOutput io.Writer
	if glog.Is(4) {
		tarOutput = os.Stdout
	}
	return tarHelper.ExtractTarStreamWithLogging(destDir, file, tarOutput)
}

func extractSourceFromImage(dockerClient DockerClient, image, buildDir string, imageSecretIndex int, paths []api.ImageSourcePath, forcePull bool) error {
	glog.V(4).Infof("Extracting image source from %s", image)

	dockerAuth := docker.AuthConfiguration{}
	if imageSecretIndex != -1 {
		pullSecret := os.Getenv(fmt.Sprintf("%s%d", dockercfg.PullSourceAuthType, imageSecretIndex))
		if len(pullSecret) > 0 {
			authPresent := false
			dockerAuth, authPresent = dockercfg.NewHelper().GetDockerAuth(image, fmt.Sprintf("%s%d", dockercfg.PullSourceAuthType, imageSecretIndex))
			if authPresent {
				glog.V(5).Infof("Registry server Address: %s", dockerAuth.ServerAddress)
				glog.V(5).Infof("Registry server User Name: %s", dockerAuth.Username)
				glog.V(5).Infof("Registry server Email: %s", dockerAuth.Email)
				passwordPresent := "<<empty>>"
				if len(dockerAuth.Password) > 0 {
					passwordPresent = "<<non-empty>>"
				}
				glog.V(5).Infof("Registry server Password: %s", passwordPresent)
			}
		}
	}

	exists := true
	if !forcePull {
		_, err := dockerClient.InspectImage(image)
		if err != nil && err == docker.ErrNoSuchImage {
			exists = false
		} else if err != nil {
			return err
		}
	}

	if !exists || forcePull {
		glog.V(0).Infof("Pulling image %q ...", image)
		if err := dockerClient.PullImage(docker.PullImageOptions{Repository: image}, dockerAuth); err != nil {
			return fmt.Errorf("error pulling image %v: %v", image, err)
		}
	}

	containerConfig := &docker.Config{Image: image}
	if inspect, err := dockerClient.InspectImage(image); err != nil {
		return err
	} else {
		// In case the Docker image does not specify the entrypoint
		if len(inspect.Config.Entrypoint) == 0 && len(inspect.Config.Cmd) == 0 {
			containerConfig.Entrypoint = []string{"/fake-entrypoint"}
		}
	}

	// Create container to copy from
	container, err := dockerClient.CreateContainer(docker.CreateContainerOptions{Config: containerConfig})
	if err != nil {
		return fmt.Errorf("error creating source image container: %v", err)
	}
	defer dockerClient.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID})

	tarHelper := tar.New()
	tarHelper.SetExclusionPattern(nil)

	for _, path := range paths {
		glog.V(4).Infof("Extracting path %s from container %s to %s", path.SourcePath, container.ID, path.DestinationDir)
		err := copyImageSource(dockerClient, container.ID, path.SourcePath, filepath.Join(buildDir, path.DestinationDir), tarHelper)
		if err != nil {
			return fmt.Errorf("error copying source path %s to %s: %v", path.SourcePath, path.DestinationDir, err)
		}
	}

	return nil
}
<commit_msg>extract git revision condition<commit_after>package builder

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	docker "github.com/fsouza/go-dockerclient"

	s2igit "github.com/openshift/source-to-image/pkg/scm/git"

	"github.com/openshift/origin/pkg/build/api"
	"github.com/openshift/origin/pkg/build/builder/cmd/dockercfg"
	"github.com/openshift/origin/pkg/generate/git"
	"github.com/openshift/source-to-image/pkg/tar"
)

const (
	// initialURLCheckTimeout is the initial timeout used to check the
	// source URL.  If fetching the URL exceeds the timeout, then a longer
	// timeout will be tried until the fetch either succeeds or the build
	// itself times out.
	initialURLCheckTimeout = 16 * time.Second

	// timeoutIncrementFactor is the factor to use when increasing
	// the timeout after each unsuccessful try
	timeoutIncrementFactor = 4
)

type gitAuthError string
type gitNotFoundError string

func (e gitAuthError) Error() string {
	return fmt.Sprintf("failed to fetch requested repository %q with provided credentials", string(e))
}

func (e gitNotFoundError) Error() string {
	return fmt.Sprintf("requested repository %q not found", string(e))
}

// fetchSource retrieves the inputs defined by the build source into the
// provided directory, or returns an error if retrieval is not possible.
func fetchSource(dockerClient DockerClient, dir string, build *api.Build, urlTimeout time.Duration, in io.Reader, gitClient GitClient) (*git.SourceInfo, error) {
	hasGitSource := false

	// expect to receive input from STDIN
	if err := extractInputBinary(in, build.Spec.Source.Binary, dir); err != nil {
		return nil, err
	}

	// may retrieve source from Git
	hasGitSource, err := extractGitSource(gitClient, build.Spec.Source.Git, build.Spec.Revision, dir, urlTimeout)
	if err != nil {
		return nil, err
	}

	var sourceInfo *git.SourceInfo
	if hasGitSource {
		var errs []error
		sourceInfo, errs = gitClient.GetInfo(dir)
		if len(errs) > 0 {
			for _, e := range errs {
				glog.V(0).Infof("error: Unable to retrieve Git info: %v", e)
			}
		}
	}

	forcePull := false
	switch {
	case build.Spec.Strategy.SourceStrategy != nil:
		forcePull = build.Spec.Strategy.SourceStrategy.ForcePull
	case build.Spec.Strategy.DockerStrategy != nil:
		forcePull = build.Spec.Strategy.DockerStrategy.ForcePull
	case build.Spec.Strategy.CustomStrategy != nil:
		forcePull = build.Spec.Strategy.CustomStrategy.ForcePull
	}
	// extract source from an Image if specified
	for i, image := range build.Spec.Source.Images {
		imageSecretIndex := i
		if image.PullSecret == nil {
			imageSecretIndex = -1
		}
		err := extractSourceFromImage(dockerClient, image.From.Name, dir, imageSecretIndex, image.Paths, forcePull)
		if err != nil {
			return nil, err
		}
	}

	// a Dockerfile has been specified, create or overwrite into the destination
	if dockerfileSource := build.Spec.Source.Dockerfile; dockerfileSource != nil {
		baseDir := dir
		// if a context dir has been defined and we cloned source, overwrite the destination
		if hasGitSource && len(build.Spec.Source.ContextDir) != 0 {
			baseDir = filepath.Join(baseDir, build.Spec.Source.ContextDir)
		}
		return sourceInfo, ioutil.WriteFile(filepath.Join(baseDir, "Dockerfile"), []byte(*dockerfileSource), 0660)
	}

	return sourceInfo, nil
}

// checkRemoteGit validates the specified Git URL. It returns GitNotFoundError
// when the remote repository not found and GitAuthenticationError when the
// remote repository failed to authenticate.
// Since this is calling the 'git' binary, the proxy settings should be
// available for this command.
func checkRemoteGit(gitClient GitClient, url string, initialTimeout time.Duration) error {

	var (
		out    string
		errOut string
		err    error
	)

	timeout := initialTimeout
	for {
		glog.V(4).Infof("git ls-remote --heads %s", url)
		out, errOut, err = gitClient.TimedListRemote(timeout, url, "--heads")
		if len(out) != 0 {
			glog.V(4).Infof(out)
		}
		if len(errOut) != 0 {
			glog.V(4).Infof(errOut)
		}
		if err != nil {
			if _, ok := err.(*git.TimeoutError); ok {
				timeout = timeout * timeoutIncrementFactor
				glog.Infof("WARNING: timed out waiting for git server, will wait %s", timeout)
				continue
			}
		}
		break
	}
	if err != nil {
		combinedOut := out + errOut
		switch {
		case strings.Contains(combinedOut, "Authentication failed"):
			return gitAuthError(url)
		case strings.Contains(combinedOut, "not found"):
			return gitNotFoundError(url)
		}
	}
	return err
}

// checkSourceURI performs a check on the URI associated with the build
// to make sure that it is valid.
func checkSourceURI(gitClient GitClient, rawurl string, timeout time.Duration) error {
	ok, err := s2igit.New().ValidCloneSpec(rawurl)
	if err != nil {
		return fmt.Errorf("Invalid git source url %q: %v", rawurl, err)
	}
	if !ok {
		return fmt.Errorf("Invalid git source url: %s", rawurl)
	}
	return checkRemoteGit(gitClient, rawurl, timeout)
}

// extractInputBinary processes the provided input stream as directed by BinaryBuildSource
// into dir.
func extractInputBinary(in io.Reader, source *api.BinaryBuildSource, dir string) error {
	if source == nil {
		return nil
	}

	var path string
	if len(source.AsFile) > 0 {
		glog.V(0).Infof("Receiving source from STDIN as file %s", source.AsFile)
		path = filepath.Join(dir, source.AsFile)

		f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0664)
		if err != nil {
			return err
		}
		defer f.Close()
		n, err := io.Copy(f, os.Stdin)
		if err != nil {
			return err
		}
		glog.V(4).Infof("Received %d bytes into %s", n, path)
		return nil
	}

	glog.V(0).Infof("Receiving source from STDIN as archive ...")

	// use bsdtar to process the incoming archive and convert it to a tar stream (since bsdtar autodetects and handles various archive formats)
	// use gnu tar to extract that tar stream to work around the bsdtar (libarchive) bug https://github.com/libarchive/libarchive/issues/746
	cmd := exec.Command("sh", "-o", "pipefail", "-c", `bsdtar -cf - @- | tar xf - -m -C "$0"`, dir)
	cmd.Stdin = in
	out, err := cmd.CombinedOutput()
	if err != nil {
		glog.V(0).Infof("Extracting...\n%s", string(out))
		return fmt.Errorf("unable to extract binary build input, must be a zip, tar, or gzipped tar, or specified as a file: %v", err)
	}
	return nil
}

func extractGitSource(gitClient GitClient, gitSource *api.GitBuildSource, revision *api.SourceRevision, dir string, timeout time.Duration) (bool, error) {
	if gitSource == nil {
		return false, nil
	}

	glog.V(0).Infof("Cloning %q ...", gitSource.URI)

	// Check source URI, trying to connect to the server only if not using a proxy.
	if err := checkSourceURI(gitClient, gitSource.URI, timeout); err != nil {
		return true, err
	}

	cloneOptions := []string{}
	usingRevision := revision != nil && revision.Git != nil && len(revision.Git.Commit) != 0
	usingRef := len(gitSource.Ref) != 0 || usingRevision

	// check if we specify a commit, ref, or branch to check out
	// Recursive clone if we're not going to checkout a ref and submodule update later
	if !usingRef {
		cloneOptions = append(cloneOptions, "--recursive")
		cloneOptions = append(cloneOptions, git.Shallow)
	}

	glog.V(3).Infof("Cloning source from %s", gitSource.URI)

	// Only use the quiet flag if Verbosity is not 5 or greater
	if !glog.Is(5) {
		cloneOptions = append(cloneOptions, "--quiet")
	}
	if err := gitClient.CloneWithOptions(dir, gitSource.URI, cloneOptions...); err != nil {
		return true, err
	}

	// if we specify a commit, ref, or branch to checkout, do so, and update submodules
	if usingRef {
		commit := gitSource.Ref

		if usingRevision {
			commit = revision.Git.Commit
		}

		if err := gitClient.Checkout(dir, commit); err != nil {
			return true, err
		}

		// Recursively update --init
		if err := gitClient.SubmoduleUpdate(dir, true, true); err != nil {
			return true, err
		}
	}

	if glog.Is(0) {
		if information, gitErr := gitClient.GetInfo(dir); len(gitErr) == 0 {
			glog.Infof("\tCommit:\t%s (%s)\n", information.CommitID, information.Message)
			glog.Infof("\tAuthor:\t%s <%s>\n", information.AuthorName, information.AuthorEmail)
			glog.Infof("\tDate:\t%s\n", information.Date)
		}
	}

	return true, nil
}

func copyImageSource(dockerClient DockerClient, containerID, sourceDir, destDir string, tarHelper tar.Tar) error {
	// Setup destination directory
	fi, err := os.Stat(destDir)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		glog.V(4).Infof("Creating image destination directory: %s", destDir)
		err := os.MkdirAll(destDir, 0644)
		if err != nil {
			return err
		}
	} else {
		if !fi.IsDir() {
			return fmt.Errorf("destination %s must be a directory", destDir)
		}
	}

	tempFile, err := ioutil.TempFile("", "imgsrc")
	if err != nil {
		return err
	}
	glog.V(4).Infof("Downloading source from path %s in container %s to temporary archive %s", sourceDir, containerID, tempFile.Name())
	err = dockerClient.DownloadFromContainer(containerID, docker.DownloadFromContainerOptions{
		OutputStream: tempFile,
		Path:         sourceDir,
	})
	if err != nil {
		tempFile.Close()
		return err
	}
	if err := tempFile.Close(); err != nil {
		return err
	}

	// Extract the created tar file to the destination directory
	file, err := os.Open(tempFile.Name())
	if err != nil {
		return err
	}
	defer file.Close()

	glog.V(4).Infof("Extracting temporary tar %s to directory %s", tempFile.Name(), destDir)
	var tarOutput io.Writer
	if glog.Is(4) {
		tarOutput = os.Stdout
	}
	return tarHelper.ExtractTarStreamWithLogging(destDir, file, tarOutput)
}

func extractSourceFromImage(dockerClient DockerClient, image, buildDir string, imageSecretIndex int, paths []api.ImageSourcePath, forcePull bool) error {
	glog.V(4).Infof("Extracting image source from %s", image)

	dockerAuth := docker.AuthConfiguration{}
	if imageSecretIndex != -1 {
		pullSecret := os.Getenv(fmt.Sprintf("%s%d", dockercfg.PullSourceAuthType, imageSecretIndex))
		if len(pullSecret) > 0 {
			authPresent := false
			dockerAuth, authPresent = dockercfg.NewHelper().GetDockerAuth(image, fmt.Sprintf("%s%d", dockercfg.PullSourceAuthType, imageSecretIndex))
			if authPresent {
				glog.V(5).Infof("Registry server Address: %s", dockerAuth.ServerAddress)
				glog.V(5).Infof("Registry server User Name: %s", dockerAuth.Username)
				glog.V(5).Infof("Registry server Email: %s", dockerAuth.Email)
				passwordPresent := "<<empty>>"
				if len(dockerAuth.Password) > 0 {
					passwordPresent = "<<non-empty>>"
				}
				glog.V(5).Infof("Registry server Password: %s", passwordPresent)
			}
		}
	}

	exists := true
	if !forcePull {
		_, err := dockerClient.InspectImage(image)
		if err != nil && err == docker.ErrNoSuchImage {
			exists = false
		} else if err != nil {
			return err
		}
	}

	if !exists || forcePull {
		glog.V(0).Infof("Pulling image %q ...", image)
		if err := dockerClient.PullImage(docker.PullImageOptions{Repository: image}, dockerAuth); err != nil {
			return fmt.Errorf("error pulling image %v: %v", image, err)
		}
	}

	containerConfig := &docker.Config{Image: image}
	if inspect, err := dockerClient.InspectImage(image); err != nil {
		return err
	} else {
		// In case the Docker image does not specify the entrypoint
		if len(inspect.Config.Entrypoint) == 0 && len(inspect.Config.Cmd) == 0 {
			containerConfig.Entrypoint = []string{"/fake-entrypoint"}
		}
	}

	// Create container to copy from
	container, err := dockerClient.CreateContainer(docker.CreateContainerOptions{Config: containerConfig})
	if err != nil {
		return fmt.Errorf("error creating source image container: %v", err)
	}
	defer dockerClient.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID})

	tarHelper := tar.New()
	tarHelper.SetExclusionPattern(nil)

	for _, path := range paths {
		glog.V(4).Infof("Extracting path %s from container %s to %s", path.SourcePath, container.ID, path.DestinationDir)
		err := copyImageSource(dockerClient, container.ID, path.SourcePath, filepath.Join(buildDir, path.DestinationDir), tarHelper)
		if err != nil {
			return fmt.Errorf("error copying source path %s to %s: %v", path.SourcePath, path.DestinationDir, err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package linux_backend

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"sync"
	"time"

	"github.com/cloudfoundry-incubator/garden/warden"
	"github.com/cloudfoundry-incubator/warden-linux/system_info"
)

type Container interface {
	ID() string
	Properties() warden.Properties
	GraceTime() time.Duration

	Start() error

	Snapshot(io.Writer) error
	Cleanup()

	warden.Container
}

type ContainerPool interface {
	Setup() error
	Create(warden.ContainerSpec) (Container, error)
	Restore(io.Reader) (Container, error)
	Destroy(Container) error
	Prune(keep map[string]bool) error
	MaxContainers() int
}

type LinuxBackend struct {
	containerPool ContainerPool
	systemInfo    system_info.Provider
	snapshotsPath string

	containers      map[string]Container
	containersMutex *sync.RWMutex
}

type UnknownHandleError struct {
	Handle string
}

func (e UnknownHandleError) Error() string {
	return "unknown handle: " + e.Handle
}

type FailedToSnapshotError struct {
	OriginalError error
}

func (e FailedToSnapshotError) Error() string {
	return fmt.Sprintf("failed to save snapshot: %s", e.OriginalError)
}

func New(containerPool ContainerPool, systemInfo system_info.Provider, snapshotsPath string) *LinuxBackend {
	return &LinuxBackend{
		containerPool: containerPool,
		systemInfo:    systemInfo,
		snapshotsPath: snapshotsPath,

		containers:      make(map[string]Container),
		containersMutex: new(sync.RWMutex),
	}
}

func (b *LinuxBackend) Setup() error {
	return b.containerPool.Setup()
}

func (b *LinuxBackend) Start() error {
	if b.snapshotsPath != "" {
		_, err := os.Stat(b.snapshotsPath)
		if err == nil {
			err = b.restoreSnapshots()
			if err != nil {
				return err
			}

			os.RemoveAll(b.snapshotsPath)
		}

		err = os.MkdirAll(b.snapshotsPath, 0755)
		if err != nil {
			return err
		}
	}

	keep := map[string]bool{}

	b.containersMutex.RLock()
	containers := b.containers
	b.containersMutex.RUnlock()

	for _, container := range containers {
		keep[container.ID()] = true
	}

	return b.containerPool.Prune(keep)
}

func (b *LinuxBackend) Capacity() (warden.Capacity, error) {
	totalMemory, err := b.systemInfo.TotalMemory()
	if err != nil {
		return warden.Capacity{}, err
	}

	totalDisk, err := b.systemInfo.TotalDisk()
	if err != nil {
		return warden.Capacity{}, err
	}

	return warden.Capacity{
		MemoryInBytes: totalMemory,
		DiskInBytes:   totalDisk,
		MaxContainers: uint64(b.containerPool.MaxContainers()),
	}, nil
}

func (b *LinuxBackend) Create(spec warden.ContainerSpec) (warden.Container, error) {
	container, err := b.containerPool.Create(spec)
	if err != nil {
		return nil, err
	}

	err = container.Start()
	if err != nil {
		return nil, err
	}

	b.containersMutex.Lock()
	b.containers[container.Handle()] = container
	b.containersMutex.Unlock()

	return container, nil
}

func (b *LinuxBackend) Destroy(handle string) error {
	b.containersMutex.RLock()
	container, found := b.containers[handle]
	b.containersMutex.RUnlock()

	if !found {
		return UnknownHandleError{handle}
	}

	err := b.containerPool.Destroy(container)
	if err != nil {
		return err
	}

	b.containersMutex.Lock()
	delete(b.containers, container.Handle())
	b.containersMutex.Unlock()

	return nil
}

func (b *LinuxBackend) Containers(filter warden.Properties) (containers []warden.Container, err error) {
	b.containersMutex.RLock()
	defer b.containersMutex.RUnlock()

	for _, container := range b.containers {
		if containerHasProperties(container, filter) {
			containers = append(containers, container)
		}
	}

	return containers, nil
}

func (b *LinuxBackend) Lookup(handle string) (warden.Container, error) {
	b.containersMutex.RLock()
	defer b.containersMutex.RUnlock()

	container, found := b.containers[handle]
	if !found {
		return nil, UnknownHandleError{handle}
	}

	return container, nil
}

func (b *LinuxBackend) GraceTime(container warden.Container) time.Duration {
	return container.(Container).GraceTime()
}

func (b *LinuxBackend) Stop() {
	b.containersMutex.RLock()
	defer b.containersMutex.RUnlock()

	for _, container := range b.containers {
		container.Cleanup()
		b.saveSnapshot(container)
	}
}

func (b *LinuxBackend) restoreSnapshots() error {
	entries, err := ioutil.ReadDir(b.snapshotsPath)
	if err != nil {
		return err
	}

	for _, entry := range entries {
		snapshot := path.Join(b.snapshotsPath, entry.Name())

		log.Println("loading snapshot for", entry.Name())

		file, err := os.Open(snapshot)
		if err != nil {
			return err
		}

		_, err = b.restore(file)
		if err != nil {
			return err
		}
	}

	return nil
}

func (b *LinuxBackend) saveSnapshot(container Container) error {
	if b.snapshotsPath == "" {
		return nil
	}

	log.Println("saving snapshot for", container.ID())

	tmpfile, err := ioutil.TempFile(os.TempDir(), "snapshot-"+container.ID())
	if err != nil {
		return &FailedToSnapshotError{err}
	}

	err = container.Snapshot(tmpfile)
	if err != nil {
		return &FailedToSnapshotError{err}
	}

	snapshotPath := path.Join(b.snapshotsPath, container.ID())

	err = os.Rename(tmpfile.Name(), snapshotPath)
	if err != nil {
		return &FailedToSnapshotError{err}
	}

	return nil
}

func (b *LinuxBackend) restore(snapshot io.Reader) (warden.Container, error) {
	container, err := b.containerPool.Restore(snapshot)
	if err != nil {
		return nil, err
	}

	b.containersMutex.Lock()
	b.containers[container.Handle()] = container
	b.containersMutex.Unlock()

	return container, nil
}

func containerHasProperties(container Container, properties warden.Properties) bool {
	containerProps := container.Properties()

	for key, val := range properties {
		cval, ok := containerProps[key]
		if !ok {
			return false
		}

		if cval != val {
			return false
		}
	}

	return true
}
<commit_msg>log when failing to snapshot<commit_after>package linux_backend

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"sync"
	"time"

	"github.com/cloudfoundry-incubator/garden/warden"
	"github.com/cloudfoundry-incubator/warden-linux/system_info"
)

type Container interface {
	ID() string
	Properties() warden.Properties
	GraceTime() time.Duration

	Start() error

	Snapshot(io.Writer) error
	Cleanup()

	warden.Container
}

type ContainerPool interface {
	Setup() error
	Create(warden.ContainerSpec) (Container, error)
	Restore(io.Reader) (Container, error)
	Destroy(Container) error
	Prune(keep map[string]bool) error
	MaxContainers() int
}

type LinuxBackend struct {
	containerPool ContainerPool
	systemInfo    system_info.Provider
	snapshotsPath string

	containers      map[string]Container
	containersMutex *sync.RWMutex
}

type UnknownHandleError struct {
	Handle string
}

func (e UnknownHandleError) Error() string {
	return "unknown handle: " + e.Handle
}

type FailedToSnapshotError struct {
	OriginalError error
}

func (e FailedToSnapshotError) Error() string {
	return fmt.Sprintf("failed to save snapshot: %s", e.OriginalError)
}

func New(containerPool ContainerPool, systemInfo system_info.Provider, snapshotsPath string) *LinuxBackend {
	return &LinuxBackend{
		containerPool: containerPool,
		systemInfo:    systemInfo,
		snapshotsPath: snapshotsPath,

		containers:      make(map[string]Container),
		containersMutex: new(sync.RWMutex),
	}
}

func (b *LinuxBackend) Setup() error {
	return b.containerPool.Setup()
}

func (b *LinuxBackend) Start() error {
	if b.snapshotsPath != "" {
		_, err := os.Stat(b.snapshotsPath)
		if err == nil {
			err = b.restoreSnapshots()
			if err != nil {
				return err
			}

			os.RemoveAll(b.snapshotsPath)
		}

		err = os.MkdirAll(b.snapshotsPath, 0755)
		if err != nil {
			return err
		}
	}

	keep := map[string]bool{}

	b.containersMutex.RLock()
	containers := b.containers
	b.containersMutex.RUnlock()

	for _, container := range containers {
		keep[container.ID()] = true
	}

	return b.containerPool.Prune(keep)
}

func (b *LinuxBackend) Capacity() (warden.Capacity, error) {
	totalMemory, err := b.systemInfo.TotalMemory()
	if err != nil {
		return warden.Capacity{}, err
	}

	totalDisk, err := b.systemInfo.TotalDisk()
	if err != nil {
		return warden.Capacity{}, err
	}

	return warden.Capacity{
		MemoryInBytes: totalMemory,
		DiskInBytes:   totalDisk,
		MaxContainers: uint64(b.containerPool.MaxContainers()),
	}, nil
}

func (b *LinuxBackend) Create(spec warden.ContainerSpec) (warden.Container, error) {
	container, err := b.containerPool.Create(spec)
	if err != nil {
		return nil, err
	}

	err = container.Start()
	if err != nil {
		return nil, err
	}

	b.containersMutex.Lock()
	b.containers[container.Handle()] = container
	b.containersMutex.Unlock()

	return container, nil
}

func (b *LinuxBackend) Destroy(handle string) error {
	b.containersMutex.RLock()
	container, found := b.containers[handle]
	b.containersMutex.RUnlock()

	if !found {
		return UnknownHandleError{handle}
	}

	err := b.containerPool.Destroy(container)
	if err != nil {
		return err
	}

	b.containersMutex.Lock()
	delete(b.containers, container.Handle())
	b.containersMutex.Unlock()

	return nil
}

func (b *LinuxBackend) Containers(filter warden.Properties) (containers []warden.Container, err error) {
	b.containersMutex.RLock()
	defer b.containersMutex.RUnlock()

	for _, container := range b.containers {
		if containerHasProperties(container, filter) {
			containers = append(containers, container)
		}
	}

	return containers, nil
}

func (b *LinuxBackend) Lookup(handle string) (warden.Container, error) {
	b.containersMutex.RLock()
	defer b.containersMutex.RUnlock()

	container, found := b.containers[handle]
	if !found {
		return nil, UnknownHandleError{handle}
	}

	return container, nil
}

func (b *LinuxBackend) GraceTime(container warden.Container) time.Duration {
	return container.(Container).GraceTime()
}

func (b *LinuxBackend) Stop() {
	b.containersMutex.RLock()
	defer b.containersMutex.RUnlock()

	for _, container := range b.containers {
		container.Cleanup()
		err := b.saveSnapshot(container)
		if err != nil {
			log.Println(err)
		}
	}
}

func (b *LinuxBackend) restoreSnapshots() error {
	entries, err := ioutil.ReadDir(b.snapshotsPath)
	if err != nil {
		return err
	}

	for _, entry := range entries {
		snapshot := path.Join(b.snapshotsPath, entry.Name())

		log.Println("loading snapshot for", entry.Name())

		file, err := os.Open(snapshot)
		if err != nil {
			return err
		}

		_, err = b.restore(file)
		if err != nil {
			return err
		}
	}

	return nil
}

func (b *LinuxBackend) saveSnapshot(container Container) error {
	if b.snapshotsPath == "" {
		return nil
	}

	log.Println("saving snapshot for", container.ID())

	tmpfile, err := ioutil.TempFile(os.TempDir(), "snapshot-"+container.ID())
	if err != nil {
		return &FailedToSnapshotError{err}
	}

	err = container.Snapshot(tmpfile)
	if err != nil {
		return &FailedToSnapshotError{err}
	}

	snapshotPath := path.Join(b.snapshotsPath, container.ID())

	err = os.Rename(tmpfile.Name(), snapshotPath)
	if err != nil {
		return &FailedToSnapshotError{err}
	}

	return nil
}

func (b *LinuxBackend) restore(snapshot io.Reader) (warden.Container, error) {
	container, err := b.containerPool.Restore(snapshot)
	if err != nil {
		return nil, err
	}

	b.containersMutex.Lock()
	b.containers[container.Handle()] = container
	b.containersMutex.Unlock()

	return container, nil
}

func containerHasProperties(container Container, properties warden.Properties) bool {
	containerProps := container.Properties()

	for key, val := range properties {
		cval, ok := containerProps[key]
		if !ok {
			return false
		}

		if cval != val {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>package discovery

import (
	"fmt"
	"sync"

	"nimona.io/internal/rand"
	"nimona.io/pkg/context"
	"nimona.io/pkg/log"
	"nimona.io/pkg/peer"
	"nimona.io/pkg/sqlobjectstore"
)

//go:generate $GOBIN/mockery -name PeerStorer -case underscore
//go:generate $GOBIN/mockery -name Discoverer -case underscore

type (
	// Discoverer defines the interface for a addressBook provider, eg our DHT
	Discoverer interface {
		Lookup(
			ctx context.Context,
			opts ...peer.LookupOption,
		) (<-chan *peer.Peer, error)
	}
	// PeerStorer interface
	PeerStorer interface {
		Add(*peer.Peer, bool)
		AddDiscoverer(Discoverer) error
		Lookup(
			context.Context,
			...peer.LookupOption,
		) (<-chan *peer.Peer, error)
	}
)

// NewPeerStorer creates a new empty addressBook with no providers
func NewPeerStorer(store *sqlobjectstore.Store) PeerStorer {
	return &addressBook{
		providers: sync.Map{},
		store:     store,
	}
}

// addressBook is
type addressBook struct {
	providers sync.Map
	store     *sqlobjectstore.Store
}

// Lookup goes through the given providers until one returns something
func (r *addressBook) Lookup(
	ctx context.Context,
	opts ...peer.LookupOption,
) (<-chan *peer.Peer, error) {
	opt := peer.ParseLookupOptions(opts...)

	logger := log.FromContext(ctx).With(
		log.String("method", "discovery/addressBook.Lookup"),
		log.String("opts", fmt.Sprintf("%#v", opt)),
	)

	logger.Debug("trying to lookup peers")

	// find all peer objects
	// TODO replace with sqlpeerstore
	os, err := r.store.Filter(
		sqlobjectstore.FilterByObjectType("nimona.io/peer.Peer"),
	)
	if err != nil {
		return nil, err
	}

	lps := []*peer.Peer{}

	// go through the peers objects, and if they match the lookup options
	// add them to the results
	for _, o := range os {
		p := &peer.Peer{}
		if err := p.FromObject(o); err != nil {
			panic(err)
		}
		if opt.Match(p) {
			lps = append(lps, p)
		}
	}

	// something to hold our results
	// TODO should probably remove the buffer
	ps := make(chan *peer.Peer, 100)

	// push local peers to channel
	for _, p := range peer.Unique(lps) {
		ps <- p
	}

	// if we have found some results, let's just return
	// TODO I don't really like this
	if len(ps) > 0 {
		close(ps)
		return ps, nil
	}

	// if no results have been found but only local results were requests return
	if opt.Local {
		close(ps)
		return ps, nil
	}
	go func() {
		// else, go through the discoveres and try to find some results
		// TODO once we have more than one discoverer we should run them in parallel
		r.providers.Range(func(_, v interface{}) bool {
			p, ok := v.(Discoverer)
			if !ok {
				return true
			}
			eps, err := p.Lookup(ctx, opts...)
			if err != nil {
				logger.With(
					log.Error(err),
				).Debug("provider failed")
				return true
			}
			for ep := range eps {
				ps <- ep
			}
			logger.With(
				log.Int("n", len(eps)),
				log.Int("total.n", len(ps)),
				log.Any("peers", ps),
			).Debug("found n peers")
			return true
		})
		close(ps)
	}()

	return ps, nil
}

// AddDiscoverer to the addressBook
func (r *addressBook) AddDiscoverer(provider Discoverer) error {
	r.providers.Store(rand.String(5), provider)
	return nil
}

// Add allows manually adding peer infos to be resolved.
// These peers will eventually be gc-ed unless pinned.
// WARNING: Only bootstrap peers should be pinned. Probably.
func (r *addressBook) Add(peer *peer.Peer, pin bool) {
	opts := []sqlobjectstore.Option{}
	if pin {
		opts = append(opts, sqlobjectstore.WithTTL(0))
	} else {
		opts = append(opts, sqlobjectstore.WithTTL(60))
	}
	o := peer.ToObject()
	r.store.Put(o, opts...) // nolint: errcheck
}
<commit_msg>fix(discovery): only returning known peers<commit_after>package discovery

import (
	"fmt"
	"sync"

	"nimona.io/internal/rand"
	"nimona.io/pkg/context"
	"nimona.io/pkg/log"
	"nimona.io/pkg/peer"
	"nimona.io/pkg/sqlobjectstore"
)

//go:generate $GOBIN/mockery -name PeerStorer -case underscore
//go:generate $GOBIN/mockery -name Discoverer -case underscore

type (
	// Discoverer defines the interface for a addressBook provider, eg our DHT
	Discoverer interface {
		Lookup(
			ctx context.Context,
			opts ...peer.LookupOption,
		) (<-chan *peer.Peer, error)
	}
	// PeerStorer interface
	PeerStorer interface {
		Add(*peer.Peer, bool)
		AddDiscoverer(Discoverer) error
		Lookup(
			context.Context,
			...peer.LookupOption,
		) (<-chan *peer.Peer, error)
	}
)

// NewPeerStorer creates a new empty addressBook with no providers
func NewPeerStorer(store *sqlobjectstore.Store) PeerStorer {
	return &addressBook{
		providers: sync.Map{},
		store:     store,
	}
}

// addressBook is
type addressBook struct {
	providers sync.Map
	store     *sqlobjectstore.Store
}

// Lookup goes through the given providers until one returns something
func (r *addressBook) Lookup(
	ctx context.Context,
	opts ...peer.LookupOption,
) (<-chan *peer.Peer, error) {
	opt := peer.ParseLookupOptions(opts...)

	logger := log.FromContext(ctx).With(
		log.String("method", "discovery/addressBook.Lookup"),
		log.String("opts", fmt.Sprintf("%#v", opt)),
	)

	logger.Debug("trying to lookup peers")

	// find all peer objects
	// TODO replace with sqlpeerstore
	os, err := r.store.Filter(
		sqlobjectstore.FilterByObjectType("nimona.io/peer.Peer"),
	)
	if err != nil {
		return nil, err
	}

	lps := []*peer.Peer{}

	// go through the peers objects, and if they match the lookup options
	// add them to the results
	for _, o := range os {
		p := &peer.Peer{}
		if err := p.FromObject(o); err != nil {
			panic(err)
		}
		if opt.Match(p) {
			lps = append(lps, p)
		}
	}

	// something to hold our results
	// TODO should probably remove the buffer
	ps := make(chan *peer.Peer, 100)

	// push local peers to channel
	for _, p := range peer.Unique(lps) {
		ps <- p
	}

	// if no results have been found but only local results were requests return
	if opt.Local {
		close(ps)
		return ps, nil
	}
	go func() {
		// else, go through the discoveres and try to find some results
		// TODO once we have more than one discoverer we should run them in parallel
		r.providers.Range(func(_, v interface{}) bool {
			p, ok := v.(Discoverer)
			if !ok {
				return true
			}
			eps, err := p.Lookup(ctx, opts...)
			if err != nil {
				logger.With(
					log.Error(err),
				).Debug("provider failed")
				return true
			}
			for ep := range eps {
				ps <- ep
			}
			logger.With(
				log.Int("n", len(eps)),
				log.Int("total.n", len(ps)),
				log.Any("peers", ps),
			).Debug("found n peers")
			return true
		})
		close(ps)
	}()

	return ps, nil
}

// AddDiscoverer to the addressBook
func (r *addressBook) AddDiscoverer(provider Discoverer) error {
	r.providers.Store(rand.String(5), provider)
	return nil
}

// Add allows manually adding peer infos to be resolved.
// These peers will eventually be gc-ed unless pinned.
// WARNING: Only bootstrap peers should be pinned. Probably.
func (r *addressBook) Add(peer *peer.Peer, pin bool) {
	opts := []sqlobjectstore.Option{}
	if pin {
		opts = append(opts, sqlobjectstore.WithTTL(0))
	} else {
		opts = append(opts, sqlobjectstore.WithTTL(60))
	}
	o := peer.ToObject()
	r.store.Put(o, opts...) // nolint: errcheck
}
<|endoftext|>


<commit_before>// Copyright 2017 The OpenPitrix Authors. All rights reserved.
// Use of this source code is governed by a Apache license
// that can be found in the LICENSE file.

package apps

import (
	"time"

	"github.com/go-openapi/strfmt"
	"github.com/go-openapi/swag"
	"openpitrix.io/openpitrix/pkg/swagger/models"
)

type AppsItem struct {
	Id          string    `db:"id, size:50, primarykey"`
	Name        string    `db:"name", size:50`
	Description string    `db:"description, size:1000"`
	Url         string    `db:"url, size:255"`
	Created     time.Time `db:"created"`
}

type AppsItems []AppsItem

func (p *AppsItem) From_models_App(app *models.App) *AppsItem {
	*p = AppsItem{
		Id:          swag.StringValue(app.AppID),
		Name:        app.Name,
		Description: app.Description,
		Url:         app.URL,
		Created:     time.Time(app.CreateTime),
	}
	return p
}

func (p *AppsItem) To_models_App() *models.App {
	return &models.App{
		AppID:       swag.String(p.Id),
		CreateTime:  strfmt.DateTime(p.Created),
		Description: p.Description,
		Name:        p.Name,
		URL:         p.Url,
	}
}

func (p *AppsItems) From_models_AppsItems(items models.AppsItems) AppsItems {
	q := make(AppsItems, len(items))
	for i, v := range items {
		q[i].From_models_App(v)
	}
	*p = q
	return *p
}

func (p AppsItems) To_models_AppsItems(pageNumber, pageSize int) models.AppsItems {
	if pageNumber > 0 {
		pageNumber = pageNumber - 1 // start with 1
	}

	start := pageNumber * pageSize
	end := start + pageSize

	if start >= len(p) {
		return nil
	}
	if end > len(p) {
		end = len(p)
	}

	q := make(models.AppsItems, end-start)
	for i := start; i < end; i++ {
		q[i-start] = p[i].To_models_App()
	}
	return q
}
<commit_msg>pkg/handlers/apps: the api changes causes the swagger generated code changed, fix build error<commit_after>// Copyright 2017 The OpenPitrix Authors. All rights reserved.
// Use of this source code is governed by a Apache license
// that can be found in the LICENSE file.

package apps

import (
	"time"

	"github.com/go-openapi/strfmt"
	"github.com/go-openapi/swag"
	"openpitrix.io/openpitrix/pkg/swagger/models"
)

type AppsItem struct {
	Id           string    `db:"id, size:50, primarykey"`
	Name         string    `db:"name", size:50`
	Description  string    `db:"description, size:1000"`
	RepoID       string    `db:"repo_id, size:50"`
	Url          string    `db:"url, size:255"`
	Created      time.Time `db:"created"`
	LastModified time.Time `db:"last_modified"`
}

type AppsItems []AppsItem

func (p *AppsItem) From_models_App(app *models.App) *AppsItem {
	*p = AppsItem{
		Id:           swag.StringValue(app.ID),
		Name:         app.Name,
		Description:  app.Description,
		RepoID:       app.RepoID,
		Url:          app.URL,
		Created:      time.Time(app.Created),
		LastModified: time.Time(app.LastModified),
	}
	return p
}

func (p *AppsItem) To_models_App() *models.App {
	return &models.App{
		ID:           swag.String(p.Id),
		Name:         p.Name,
		Description:  p.Description,
		RepoID:       p.RepoID,
		URL:          p.Url,
		Created:      strfmt.DateTime(p.Created),
		LastModified: strfmt.DateTime(p.LastModified),
	}
}

func (p *AppsItems) From_models_AppsItems(items models.AppsItems) AppsItems {
	q := make(AppsItems, len(items))
	for i, v := range items {
		q[i].From_models_App(v)
	}
	*p = q
	return *p
}

func (p AppsItems) To_models_AppsItems(pageNumber, pageSize int) models.AppsItems {
	if pageNumber > 0 {
		pageNumber = pageNumber - 1 // start with 1
	}

	start := pageNumber * pageSize
	end := start + pageSize

	if start >= len(p) {
		return nil
	}
	if end > len(p) {
		end = len(p)
	}

	q := make(models.AppsItems, end-start)
	for i := start; i < end; i++ {
		q[i-start] = p[i].To_models_App()
	}
	return q
}
<|endoftext|>


<commit_before>/*
Copyright 2018 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 image

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"io"
	"io/ioutil"
	"strings"
	"time"

	"github.com/containers/image/manifest"
	"github.com/containers/image/types"
	digest "github.com/opencontainers/go-digest"
)

type MutableSource struct {
	ProxySource
	mfst        *manifest.Schema2
	cfg         *manifest.Schema2Image
	extraBlobs  map[string][]byte
	extraLayers []digest.Digest
}

func NewMutableSource(r types.ImageReference) (*MutableSource, error) {
	src, err := r.NewImageSource(nil)
	if err != nil {
		return nil, err
	}
	img, err := r.NewImage(nil)
	if err != nil {
		return nil, err
	}

	ms := &MutableSource{
		ProxySource: ProxySource{
			Ref:         r,
			ImageSource: src,
			img:         img,
		},
		extraBlobs: make(map[string][]byte),
	}
	if err := ms.populateManifestAndConfig(); err != nil {
		return nil, err
	}
	return ms, nil
}

// Manifest marshals the stored manifest to the byte format.
func (m *MutableSource) GetManifest(_ *digest.Digest) ([]byte, string, error) {
	if err := m.saveConfig(); err != nil {
		return nil, "", err
	}
	s, err := json.Marshal(m.mfst)
	if err != nil {
		return nil, "", err
	}
	return s, manifest.DockerV2Schema2MediaType, err
}

// populateManifestAndConfig parses the raw manifest and configs, storing them on the struct.
func (m *MutableSource) populateManifestAndConfig() error {
	mfstBytes, _, err := m.ProxySource.GetManifest(nil)
	if err != nil {
		return err
	}

	m.mfst, err = manifest.Schema2FromManifest(mfstBytes)
	if err != nil {
		return err
	}

	bi := types.BlobInfo{Digest: m.mfst.ConfigDescriptor.Digest}
	r, _, err := m.GetBlob(bi)
	if err != nil {
		return err
	}

	cfgBytes, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	return json.Unmarshal(cfgBytes, &m.cfg)
}

// GetBlob first checks the stored "extra" blobs, then proxies the call to the original source.
func (m *MutableSource) GetBlob(bi types.BlobInfo) (io.ReadCloser, int64, error) {
	if b, ok := m.extraBlobs[bi.Digest.String()]; ok {
		return ioutil.NopCloser(bytes.NewReader(b)), int64(len(b)), nil
	}
	return m.ImageSource.GetBlob(bi)
}

func gzipBytes(b []byte) ([]byte, error) {
	buf := bytes.NewBuffer([]byte{})
	w := gzip.NewWriter(buf)
	_, err := w.Write(b)
	w.Close()
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// appendLayer appends an uncompressed blob to the image, preserving the invariants required across the config and manifest.
func (m *MutableSource) AppendLayer(content []byte, author string) error {
	compressedBlob, err := gzipBytes(content)
	if err != nil {
		return err
	}

	dgst := digest.FromBytes(compressedBlob)

	// Add the layer to the manifest.
	descriptor := manifest.Schema2Descriptor{
		MediaType: manifest.DockerV2Schema2LayerMediaType,
		Size:      int64(len(content)),
		Digest:    dgst,
	}
	m.mfst.LayersDescriptors = append(m.mfst.LayersDescriptors, descriptor)

	m.extraBlobs[dgst.String()] = compressedBlob
	m.extraLayers = append(m.extraLayers, dgst)

	// Also add it to the config.
	diffID := digest.FromBytes(content)
	m.cfg.RootFS.DiffIDs = append(m.cfg.RootFS.DiffIDs, diffID)
	m.AppendConfigHistory(author, false)
	return nil
}

// saveConfig marshals the stored image config, and updates the references to it in the manifest.
func (m *MutableSource) saveConfig() error {
	cfgBlob, err := json.Marshal(m.cfg)
	if err != nil {
		return err
	}

	cfgDigest := digest.FromBytes(cfgBlob)
	m.extraBlobs[cfgDigest.String()] = cfgBlob
	m.mfst.ConfigDescriptor = manifest.Schema2Descriptor{
		MediaType: manifest.DockerV2Schema2ConfigMediaType,
		Size:      int64(len(cfgBlob)),
		Digest:    cfgDigest,
	}
	return nil
}

// Env returns a map of environment variables stored in the image config
// Converts each variable from a string of the form KEY=VALUE to a map of KEY:VALUE
func (m *MutableSource) Env() map[string]string {
	envArray := m.cfg.Schema2V1Image.Config.Env
	envMap := make(map[string]string)
	for _, env := range envArray {
		entry := strings.Split(env, "=")
		envMap[entry[0]] = entry[1]
	}
	return envMap
}

// SetEnv takes a map of environment variables, and converts them to an array of strings
// in the form KEY=VALUE, and then sets the image config
func (m *MutableSource) SetEnv(envMap map[string]string, author string) {
	envArray := []string{}
	for key, value := range envMap {
		entry := key + "=" + value
		envArray = append(envArray, entry)
	}
	m.cfg.Schema2V1Image.Config.Env = envArray
	m.AppendConfigHistory(author, true)
}

func (m *MutableSource) Config() *manifest.Schema2Config {
	return m.cfg.Schema2V1Image.Config
}

func (m *MutableSource) SetConfig(config *manifest.Schema2Config, author string, emptyLayer bool) {
	m.cfg.Schema2V1Image.Config = config
	m.AppendConfigHistory(author, emptyLayer)
}

func (m *MutableSource) AppendConfigHistory(author string, emptyLayer bool) {
	history := manifest.Schema2History{
		Created:    time.Now(),
		Author:     author,
		EmptyLayer: emptyLayer,
	}
	m.cfg.History = append(m.cfg.History, history)
}
<commit_msg>Get image config from reference<commit_after>/*
Copyright 2018 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 image

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"io"
	"io/ioutil"
	"strings"
	"time"

	"github.com/containers/image/manifest"
	"github.com/containers/image/types"
	digest "github.com/opencontainers/go-digest"
)

type MutableSource struct {
	ProxySource
	mfst        *manifest.Schema2
	cfg         *manifest.Schema2Image
	extraBlobs  map[string][]byte
	extraLayers []digest.Digest
}

func NewMutableSource(r types.ImageReference) (*MutableSource, error) {
	src, err := r.NewImageSource(nil)
	if err != nil {
		return nil, err
	}
	img, err := r.NewImage(nil)
	if err != nil {
		return nil, err
	}

	ms := &MutableSource{
		ProxySource: ProxySource{
			Ref:         r,
			ImageSource: src,
			img:         img,
		},
		extraBlobs: make(map[string][]byte),
	}
	if err := ms.populateManifestAndConfig(); err != nil {
		return nil, err
	}
	return ms, nil
}

// Manifest marshals the stored manifest to the byte format.
func (m *MutableSource) GetManifest(_ *digest.Digest) ([]byte, string, error) {
	if err := m.saveConfig(); err != nil {
		return nil, "", err
	}
	s, err := json.Marshal(m.mfst)
	if err != nil {
		return nil, "", err
	}
	return s, manifest.DockerV2Schema2MediaType, err
}

// populateManifestAndConfig parses the raw manifest and configs, storing them on the struct.
func (m *MutableSource) populateManifestAndConfig() error {
	// First get manifest
	mfstBytes, _, err := m.ProxySource.GetManifest(nil)
	if err != nil {
		return err
	}

	m.mfst, err = manifest.Schema2FromManifest(mfstBytes)
	if err != nil {
		return err
	}

	// Now, get config
	img, err := m.ProxySource.Ref.NewImage(nil)
	if err != nil {
		return err
	}
	defer img.Close()
	configBlob, err := img.ConfigBlob()
	if err != nil {
		return err
	}
	return json.Unmarshal(configBlob, &m.cfg)
}

// GetBlob first checks the stored "extra" blobs, then proxies the call to the original source.
func (m *MutableSource) GetBlob(bi types.BlobInfo) (io.ReadCloser, int64, error) {
	if b, ok := m.extraBlobs[bi.Digest.String()]; ok {
		return ioutil.NopCloser(bytes.NewReader(b)), int64(len(b)), nil
	}
	return m.ImageSource.GetBlob(bi)
}

func gzipBytes(b []byte) ([]byte, error) {
	buf := bytes.NewBuffer([]byte{})
	w := gzip.NewWriter(buf)
	_, err := w.Write(b)
	w.Close()
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// appendLayer appends an uncompressed blob to the image, preserving the invariants required across the config and manifest.
func (m *MutableSource) AppendLayer(content []byte, author string) error {
	compressedBlob, err := gzipBytes(content)
	if err != nil {
		return err
	}

	dgst := digest.FromBytes(compressedBlob)

	// Add the layer to the manifest.
	descriptor := manifest.Schema2Descriptor{
		MediaType: manifest.DockerV2Schema2LayerMediaType,
		Size:      int64(len(content)),
		Digest:    dgst,
	}
	m.mfst.LayersDescriptors = append(m.mfst.LayersDescriptors, descriptor)

	m.extraBlobs[dgst.String()] = compressedBlob
	m.extraLayers = append(m.extraLayers, dgst)

	// Also add it to the config.
	diffID := digest.FromBytes(content)
	m.cfg.RootFS.DiffIDs = append(m.cfg.RootFS.DiffIDs, diffID)
	m.AppendConfigHistory(author, false)
	return nil
}

// saveConfig marshals the stored image config, and updates the references to it in the manifest.
func (m *MutableSource) saveConfig() error {
	cfgBlob, err := json.Marshal(m.cfg)
	if err != nil {
		return err
	}

	cfgDigest := digest.FromBytes(cfgBlob)
	m.extraBlobs[cfgDigest.String()] = cfgBlob
	m.mfst.ConfigDescriptor = manifest.Schema2Descriptor{
		MediaType: manifest.DockerV2Schema2ConfigMediaType,
		Size:      int64(len(cfgBlob)),
		Digest:    cfgDigest,
	}
	return nil
}

// Env returns a map of environment variables stored in the image config
// Converts each variable from a string of the form KEY=VALUE to a map of KEY:VALUE
func (m *MutableSource) Env() map[string]string {
	envArray := m.cfg.Schema2V1Image.Config.Env
	envMap := make(map[string]string)
	for _, env := range envArray {
		entry := strings.Split(env, "=")
		envMap[entry[0]] = entry[1]
	}
	return envMap
}

// SetEnv takes a map of environment variables, and converts them to an array of strings
// in the form KEY=VALUE, and then sets the image config
func (m *MutableSource) SetEnv(envMap map[string]string, author string) {
	envArray := []string{}
	for key, value := range envMap {
		entry := key + "=" + value
		envArray = append(envArray, entry)
	}
	m.cfg.Schema2V1Image.Config.Env = envArray
	m.AppendConfigHistory(author, true)
}

func (m *MutableSource) Config() *manifest.Schema2Config {
	return m.cfg.Schema2V1Image.Config
}

func (m *MutableSource) SetConfig(config *manifest.Schema2Config, author string, emptyLayer bool) {
	m.cfg.Schema2V1Image.Config = config
	m.AppendConfigHistory(author, emptyLayer)
}

func (m *MutableSource) AppendConfigHistory(author string, emptyLayer bool) {
	history := manifest.Schema2History{
		Created:    time.Now(),
		Author:     author,
		EmptyLayer: emptyLayer,
	}
	m.cfg.History = append(m.cfg.History, history)
}
<|endoftext|>


<commit_before>package modules

import (
	"bufio"
	"os"
	"os/exec"
	"strings"

	"github.com/rancher/os/config"
	"github.com/rancher/os/pkg/log"
	"github.com/rancher/os/pkg/util"
)

func LoadModules(cfg *config.CloudConfig) (*config.CloudConfig, error) {
	mounted := map[string]bool{}

	f, err := os.Open("/proc/modules")
	if err != nil {
		return cfg, err
	}
	defer f.Close()

	reader := bufio.NewScanner(f)
	for reader.Scan() {
		mounted[strings.SplitN(reader.Text(), " ", 2)[0]] = true
	}

	if util.GetHypervisor() == "hyperv" {
		cfg.Rancher.Modules = append(cfg.Rancher.Modules, "hv_utils", "hv_storvsc")
	}

	for _, module := range cfg.Rancher.Modules {
		if mounted[module] {
			continue
		}

		log.Debugf("Loading module %s", module)
		// split module and module parameters
		cmdParam := strings.SplitN(module, " ", -1)
		cmd := exec.Command("modprobe", cmdParam...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			log.Errorf("Could not load module %s, err %v", module, err)
		}
	}

	return cfg, reader.Err()
}
<commit_msg>Add azure hyperv module<commit_after>package modules

import (
	"bufio"
	"os"
	"os/exec"
	"strings"

	"github.com/rancher/os/config"
	"github.com/rancher/os/pkg/log"
	"github.com/rancher/os/pkg/util"
)

func LoadModules(cfg *config.CloudConfig) (*config.CloudConfig, error) {
	mounted := map[string]bool{}

	f, err := os.Open("/proc/modules")
	if err != nil {
		return cfg, err
	}
	defer f.Close()

	reader := bufio.NewScanner(f)
	for reader.Scan() {
		mounted[strings.SplitN(reader.Text(), " ", 2)[0]] = true
	}

	if util.GetHypervisor() == "hyperv" {
		cfg.Rancher.Modules = append(cfg.Rancher.Modules, "hv_utils", "hv_storvsc", "hv_vmbus")
	}

	for _, module := range cfg.Rancher.Modules {
		if mounted[module] {
			continue
		}

		log.Debugf("Loading module %s", module)
		// split module and module parameters
		cmdParam := strings.SplitN(module, " ", -1)
		cmd := exec.Command("modprobe", cmdParam...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			log.Errorf("Could not load module %s, err %v", module, err)
		}
	}

	return cfg, reader.Err()
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Jetstack cert-manager contributors.

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 venafi

import (
	"crypto/x509"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/Venafi/vcert/pkg/certificate"

	internalvanafiapi "github.com/jetstack/cert-manager/pkg/internal/venafi/api"
	"github.com/jetstack/cert-manager/pkg/util/pki"
)

// ErrCustomFieldsType provides a common error structure for an invalid Venafi custom field type
type ErrCustomFieldsType struct {
	Type internalvanafiapi.CustomFieldType
}

func (err ErrCustomFieldsType) Error() string {
	return fmt.Sprintf("certificate request contains an invalid Venafi custom fields type: %q", err.Type)
}

var ErrorMissingSubject = errors.New("certificate needs a comon name or at least one field in the subject set to be processed by Venafi")

// This function sends a request to Venafi to for a signed certificate.
// The CSR will be decoded to be validated against the zone configuration policy.
// Upon the template being successfully defaulted and validated, the CSR will be sent, as is.
func (v *Venafi) Sign(csrPEM []byte, duration time.Duration, customFields []internalvanafiapi.CustomField) (cert []byte, err error) {
	// Retrieve a copy of the Venafi zone.
	// This contains default values and policy control info that we can apply
	// and check against locally.
	zoneCfg, err := v.client.ReadZoneConfiguration()
	if err != nil {
		return nil, err
	}

	tmpl, err := pki.GenerateTemplateFromCSRPEM(csrPEM, duration, false)
	if err != nil {
		return nil, err
	}

	if tmpl.Subject.String() == "" {
		return nil, ErrorMissingSubject
	}

	// Create a vcert Request structure
	vreq := newVRequest(tmpl)

	// Add cert-manager origin tag
	vreq.CustomFields = []certificate.CustomField{
		{
			Type:  certificate.CustomFieldOrigin,
			Value: "Jetstack cert-manager",
		},
	}

	// Convert over custom fields from our struct type to venafi's
	if len(customFields) > 0 {
		for _, field := range customFields {
			var fieldType certificate.CustomFieldType
			switch field.Type {
			case internalvanafiapi.CustomFieldTypePlain, "":
				fieldType = certificate.CustomFieldPlain
			default:
				return nil, ErrCustomFieldsType{Type: field.Type}
			}

			vreq.CustomFields = append(vreq.CustomFields, certificate.CustomField{
				Type:  fieldType,
				Name:  field.Name,
				Value: field.Value,
			})
		}
	}

	// Apply default values from the Venafi zone
	zoneCfg.UpdateCertificateRequest(vreq)

	// Here we are validating the request using the current policy with
	// defaulting applied to the CSR. The CSR we send will not be defaulted
	// however, as this will be done again server side.
	err = zoneCfg.ValidateCertificateRequest(vreq)
	if err != nil {
		return nil, err
	}

	vreq.SetCSR(csrPEM)
	// Set options on the request
	vreq.CsrOrigin = certificate.UserProvidedCSR
	//// TODO: better set the timeout here. Right now, we'll block for this amount of time.
	vreq.Timeout = time.Minute * 5

	// Set the 'ObjectName' through the request friendly name. This is set in
	// order of precedence CN->DNS->URI.
	switch {
	case len(tmpl.Subject.CommonName) > 0:
		vreq.FriendlyName = tmpl.Subject.CommonName
		break
	case len(tmpl.DNSNames) > 0:
		vreq.FriendlyName = tmpl.DNSNames[0]
		break
	case len(tmpl.URIs) > 0:
		vreq.FriendlyName = tmpl.URIs[0].String()
		break
	default:
		return nil, errors.New(
			"certificate request contains no Common Name, DNS Name, nor URI SAN, at least one must be supplied to be used as the Venafi certificate objects name")
	}

	// Set the request CSR with the passed value
	if err := vreq.SetCSR(csrPEM); err != nil {
		return nil, err
	}

	// Send the certificate signing request to Venafi
	requestID, err := v.client.RequestCertificate(vreq)
	if err != nil {
		return nil, err
	}

	// Set the PickupID so vcert does not have to look it up by the fingerprint
	vreq.PickupID = requestID

	// Retrieve the certificate from request
	pemCollection, err := v.client.RetrieveCertificate(vreq)
	if err != nil {
		return nil, err
	}

	// Construct the certificate chain and return the new keypair
	cs := append([]string{pemCollection.Certificate}, pemCollection.Chain...)
	chain := strings.Join(cs, "\n")

	return []byte(chain), nil
}

func newVRequest(cert *x509.Certificate) *certificate.Request {
	req := certificate.NewRequest(cert)

	// overwrite entire Subject block
	req.Subject = cert.Subject
	return req
}
<commit_msg>Implement feedback on error message<commit_after>/*
Copyright 2018 The Jetstack cert-manager contributors.

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 venafi

import (
	"crypto/x509"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/Venafi/vcert/pkg/certificate"

	internalvanafiapi "github.com/jetstack/cert-manager/pkg/internal/venafi/api"
	"github.com/jetstack/cert-manager/pkg/util/pki"
)

// ErrCustomFieldsType provides a common error structure for an invalid Venafi custom field type
type ErrCustomFieldsType struct {
	Type internalvanafiapi.CustomFieldType
}

func (err ErrCustomFieldsType) Error() string {
	return fmt.Sprintf("certificate request contains an invalid Venafi custom fields type: %q", err.Type)
}

var ErrorMissingSubject = errors.New("Certificate requests submitted to Venafi issuers must have the 'commonName' field or at least one other subject field set.")

// This function sends a request to Venafi to for a signed certificate.
// The CSR will be decoded to be validated against the zone configuration policy.
// Upon the template being successfully defaulted and validated, the CSR will be sent, as is.
func (v *Venafi) Sign(csrPEM []byte, duration time.Duration, customFields []internalvanafiapi.CustomField) (cert []byte, err error) {
	// Retrieve a copy of the Venafi zone.
	// This contains default values and policy control info that we can apply
	// and check against locally.
	zoneCfg, err := v.client.ReadZoneConfiguration()
	if err != nil {
		return nil, err
	}

	tmpl, err := pki.GenerateTemplateFromCSRPEM(csrPEM, duration, false)
	if err != nil {
		return nil, err
	}

	if tmpl.Subject.String() == "" {
		return nil, ErrorMissingSubject
	}

	// Create a vcert Request structure
	vreq := newVRequest(tmpl)

	// Add cert-manager origin tag
	vreq.CustomFields = []certificate.CustomField{
		{
			Type:  certificate.CustomFieldOrigin,
			Value: "Jetstack cert-manager",
		},
	}

	// Convert over custom fields from our struct type to venafi's
	if len(customFields) > 0 {
		for _, field := range customFields {
			var fieldType certificate.CustomFieldType
			switch field.Type {
			case internalvanafiapi.CustomFieldTypePlain, "":
				fieldType = certificate.CustomFieldPlain
			default:
				return nil, ErrCustomFieldsType{Type: field.Type}
			}

			vreq.CustomFields = append(vreq.CustomFields, certificate.CustomField{
				Type:  fieldType,
				Name:  field.Name,
				Value: field.Value,
			})
		}
	}

	// Apply default values from the Venafi zone
	zoneCfg.UpdateCertificateRequest(vreq)

	// Here we are validating the request using the current policy with
	// defaulting applied to the CSR. The CSR we send will not be defaulted
	// however, as this will be done again server side.
	err = zoneCfg.ValidateCertificateRequest(vreq)
	if err != nil {
		return nil, err
	}

	vreq.SetCSR(csrPEM)
	// Set options on the request
	vreq.CsrOrigin = certificate.UserProvidedCSR
	//// TODO: better set the timeout here. Right now, we'll block for this amount of time.
	vreq.Timeout = time.Minute * 5

	// Set the 'ObjectName' through the request friendly name. This is set in
	// order of precedence CN->DNS->URI.
	switch {
	case len(tmpl.Subject.CommonName) > 0:
		vreq.FriendlyName = tmpl.Subject.CommonName
		break
	case len(tmpl.DNSNames) > 0:
		vreq.FriendlyName = tmpl.DNSNames[0]
		break
	case len(tmpl.URIs) > 0:
		vreq.FriendlyName = tmpl.URIs[0].String()
		break
	default:
		return nil, errors.New(
			"certificate request contains no Common Name, DNS Name, nor URI SAN, at least one must be supplied to be used as the Venafi certificate objects name")
	}

	// Set the request CSR with the passed value
	if err := vreq.SetCSR(csrPEM); err != nil {
		return nil, err
	}

	// Send the certificate signing request to Venafi
	requestID, err := v.client.RequestCertificate(vreq)
	if err != nil {
		return nil, err
	}

	// Set the PickupID so vcert does not have to look it up by the fingerprint
	vreq.PickupID = requestID

	// Retrieve the certificate from request
	pemCollection, err := v.client.RetrieveCertificate(vreq)
	if err != nil {
		return nil, err
	}

	// Construct the certificate chain and return the new keypair
	cs := append([]string{pemCollection.Certificate}, pemCollection.Chain...)
	chain := strings.Join(cs, "\n")

	return []byte(chain), nil
}

func newVRequest(cert *x509.Certificate) *certificate.Request {
	req := certificate.NewRequest(cert)

	// overwrite entire Subject block
	req.Subject = cert.Subject
	return req
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc.
//
// 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 gofer

import (
	"fmt"
	"syscall"

	"gvisor.googlesource.com/gvisor/pkg/p9"
	"gvisor.googlesource.com/gvisor/pkg/sentry/context"
	"gvisor.googlesource.com/gvisor/pkg/sentry/device"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
	"gvisor.googlesource.com/gvisor/pkg/syserror"
	"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/unix"
)

// Lookup loads an Inode at name into a Dirent based on the session's cache
// policy.
func (i *inodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name string) (*fs.Dirent, error) {
	if i.session().cachePolicy.cacheReaddir() {
		// Check to see if we have readdirCache that indicates the
		// child does not exist.  Avoid holding readdirMu longer than
		// we need to.
		i.readdirMu.Lock()
		if i.readdirCache != nil && !i.readdirCache.Contains(name) {
			// No such child.  Return a negative dirent.
			i.readdirMu.Unlock()
			return fs.NewNegativeDirent(name), nil
		}
		i.readdirMu.Unlock()
	}

	// Get a p9.File for name.
	qids, newFile, mask, p9attr, err := i.fileState.file.walkGetAttr(ctx, []string{name})
	if err != nil {
		if err == syscall.ENOENT {
			if i.session().cachePolicy.cacheNegativeDirents() {
				// Return a negative Dirent. It will stay cached until something
				// is created over it.
				return fs.NewNegativeDirent(name), nil
			}
			return nil, syserror.ENOENT
		}
		return nil, err
	}

	// Construct the Inode operations.
	sattr, node := newInodeOperations(ctx, i.fileState.s, newFile, qids[0], mask, p9attr, false)

	// Construct a positive Dirent.
	return fs.NewDirent(fs.NewInode(node, dir.MountSource, sattr), name), nil
}

// Creates a new Inode at name and returns its File based on the session's cache policy.
//
// Ownership is currently ignored.
func (i *inodeOperations) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.FileFlags, perm fs.FilePermissions) (*fs.File, error) {
	// Create replaces the directory fid with the newly created/opened
	// file, so clone this directory so it doesn't change out from under
	// this node.
	_, newFile, err := i.fileState.file.walk(ctx, nil)
	if err != nil {
		return nil, err
	}

	// Map the FileFlags to p9 OpenFlags.
	var openFlags p9.OpenFlags
	switch {
	case flags.Read && flags.Write:
		openFlags = p9.ReadWrite
	case flags.Read:
		openFlags = p9.ReadOnly
	case flags.Write:
		openFlags = p9.WriteOnly
	default:
		panic(fmt.Sprintf("Create called with unknown or unset open flags: %v", flags))
	}

	owner := fs.FileOwnerFromContext(ctx)
	hostFile, err := newFile.create(ctx, name, openFlags, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID))
	if err != nil {
		// Could not create the file.
		return nil, err
	}

	i.touchModificationTime(ctx, dir)

	// Get the attributes of the file.
	qid, mask, p9attr, err := getattr(ctx, newFile)
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	// Get an unopened p9.File for the file we created so that it can be
	// cloned and re-opened multiple times after creation.
	_, unopened, err := i.fileState.file.walk(ctx, []string{name})
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	// Construct the InodeOperations.
	sattr, iops := newInodeOperations(ctx, i.fileState.s, unopened, qid, mask, p9attr, false)

	// Construct the positive Dirent.
	d := fs.NewDirent(fs.NewInode(iops, dir.MountSource, sattr), name)
	defer d.DecRef()

	// Construct the new file, caching the handles if allowed.
	h := &handles{
		File: newFile,
		Host: hostFile,
	}
	if iops.session().cachePolicy.usePageCache(d.Inode) {
		iops.fileState.setHandlesForCachedIO(flags, h)
	}
	return NewFile(ctx, d, name, flags, iops, h), nil
}

// CreateLink uses Create to create a symlink between oldname and newname.
func (i *inodeOperations) CreateLink(ctx context.Context, dir *fs.Inode, oldname string, newname string) error {
	owner := fs.FileOwnerFromContext(ctx)
	if _, err := i.fileState.file.symlink(ctx, oldname, newname, p9.UID(owner.UID), p9.GID(owner.GID)); err != nil {
		return err
	}
	i.touchModificationTime(ctx, dir)
	return nil
}

// CreateHardLink implements InodeOperations.CreateHardLink.
func (i *inodeOperations) CreateHardLink(ctx context.Context, inode *fs.Inode, target *fs.Inode, newName string) error {
	targetOpts, ok := target.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	if err := i.fileState.file.link(ctx, &targetOpts.fileState.file, newName); err != nil {
		return err
	}
	if i.session().cachePolicy.cacheUAttrs(inode) {
		// Increase link count.
		targetOpts.cachingInodeOps.IncLinks(ctx)
	}
	i.touchModificationTime(ctx, inode)
	return nil
}

// CreateDirectory uses Create to create a directory named s under inodeOperations.
func (i *inodeOperations) CreateDirectory(ctx context.Context, dir *fs.Inode, s string, perm fs.FilePermissions) error {
	owner := fs.FileOwnerFromContext(ctx)
	if _, err := i.fileState.file.mkdir(ctx, s, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID)); err != nil {
		return err
	}
	if i.session().cachePolicy.cacheUAttrs(dir) {
		// Increase link count.
		i.cachingInodeOps.IncLinks(ctx)
	}
	if i.session().cachePolicy.cacheReaddir() {
		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
	return nil
}

// Bind implements InodeOperations.Bind.
func (i *inodeOperations) Bind(ctx context.Context, dir *fs.Inode, name string, ep unix.BoundEndpoint, perm fs.FilePermissions) (*fs.Dirent, error) {
	if i.session().endpoints == nil {
		return nil, syscall.EOPNOTSUPP
	}

	// Create replaces the directory fid with the newly created/opened
	// file, so clone this directory so it doesn't change out from under
	// this node.
	_, newFile, err := i.fileState.file.walk(ctx, nil)
	if err != nil {
		return nil, err
	}

	// Stabilize the endpoint map while creation is in progress.
	unlock := i.session().endpoints.lock()
	defer unlock()

	// Create a regular file in the gofer and then mark it as a socket by
	// adding this inode key in the 'endpoints' map.
	owner := fs.FileOwnerFromContext(ctx)
	hostFile, err := newFile.create(ctx, name, p9.ReadWrite, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID))
	if err != nil {
		return nil, err
	}
	// We're not going to use this file.
	hostFile.Close()

	i.touchModificationTime(ctx, dir)

	// Get the attributes of the file to create inode key.
	qid, mask, attr, err := getattr(ctx, newFile)
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	key := device.MultiDeviceKey{
		Device:          attr.RDev,
		SecondaryDevice: i.session().connID,
		Inode:           qid.Path,
	}

	// Create child dirent.

	// Get an unopened p9.File for the file we created so that it can be
	// cloned and re-opened multiple times after creation.
	_, unopened, err := i.fileState.file.walk(ctx, []string{name})
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	// Construct the InodeOperations.
	sattr, iops := newInodeOperations(ctx, i.fileState.s, unopened, qid, mask, attr, true)

	// Construct the positive Dirent.
	childDir := fs.NewDirent(fs.NewInode(iops, dir.MountSource, sattr), name)
	i.session().endpoints.add(key, childDir, ep)
	return childDir, nil
}

// CreateFifo implements fs.InodeOperations.CreateFifo. Gofer nodes do not support the
// creation of fifos and always returns EOPNOTSUPP.
func (*inodeOperations) CreateFifo(context.Context, *fs.Inode, string, fs.FilePermissions) error {
	return syscall.EOPNOTSUPP
}

// Remove implements InodeOperations.Remove.
func (i *inodeOperations) Remove(ctx context.Context, dir *fs.Inode, name string) error {
	var key device.MultiDeviceKey
	removeSocket := false
	if i.session().endpoints != nil {
		// Find out if file being deleted is a socket that needs to be
		// removed from endpoint map.
		if d, err := i.Lookup(ctx, dir, name); err == nil {
			defer d.DecRef()
			if fs.IsSocket(d.Inode.StableAttr) {
				child := d.Inode.InodeOperations.(*inodeOperations)
				key = child.fileState.key
				removeSocket = true

				// Stabilize the endpoint map while deletion is in progress.
				unlock := i.session().endpoints.lock()
				defer unlock()
			}
		}
	}

	if err := i.fileState.file.unlinkAt(ctx, name, 0); err != nil {
		return err
	}
	if removeSocket {
		i.session().endpoints.remove(key)
	}
	i.touchModificationTime(ctx, dir)

	return nil
}

// Remove implements InodeOperations.RemoveDirectory.
func (i *inodeOperations) RemoveDirectory(ctx context.Context, dir *fs.Inode, name string) error {
	// 0x200 = AT_REMOVEDIR.
	if err := i.fileState.file.unlinkAt(ctx, name, 0x200); err != nil {
		return err
	}
	if i.session().cachePolicy.cacheUAttrs(dir) {
		// Decrease link count and updates atime.
		i.cachingInodeOps.DecLinks(ctx)
	}
	if i.session().cachePolicy.cacheReaddir() {
		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
	return nil
}

// Rename renames this node.
func (i *inodeOperations) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
	// Unwrap the new parent to a *inodeOperations.
	newParentInodeOperations, ok := newParent.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	// Unwrap the old parent to a *inodeOperations.
	oldParentInodeOperations, ok := oldParent.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	// Do the rename.
	if err := i.fileState.file.rename(ctx, newParentInodeOperations.fileState.file, newName); err != nil {
		return err
	}

	// Is the renamed entity a directory? Fix link counts.
	if fs.IsDir(i.fileState.sattr) {
		// Update cached state.
		if i.session().cachePolicy.cacheUAttrs(oldParent) {
			oldParentInodeOperations.cachingInodeOps.DecLinks(ctx)
		}
		if i.session().cachePolicy.cacheUAttrs(newParent) {
			newParentInodeOperations.cachingInodeOps.IncLinks(ctx)
		}
	}
	if i.session().cachePolicy.cacheReaddir() {
		// Mark old directory dirty.
		oldParentInodeOperations.markDirectoryDirty()
		if oldParent != newParent {
			// Mark new directory dirty.
			newParentInodeOperations.markDirectoryDirty()
		}
	}
	return nil
}

func (i *inodeOperations) touchModificationTime(ctx context.Context, inode *fs.Inode) {
	if i.session().cachePolicy.cacheUAttrs(inode) {
		i.cachingInodeOps.TouchModificationTime(ctx)
	}
	if i.session().cachePolicy.cacheReaddir() {
		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
}

// markDirectoryDirty marks any cached data dirty for this directory. This is necessary in order
// to ensure that this node does not retain stale state throughout its lifetime across multiple
// open directory handles.
//
// Currently this means invalidating any readdir caches.
func (i *inodeOperations) markDirectoryDirty() {
	i.readdirMu.Lock()
	defer i.readdirMu.Unlock()
	i.readdirCache = nil
}
<commit_msg>cache policy: Check policy before returning a negative dirent.<commit_after>// Copyright 2018 Google Inc.
//
// 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 gofer

import (
	"fmt"
	"syscall"

	"gvisor.googlesource.com/gvisor/pkg/p9"
	"gvisor.googlesource.com/gvisor/pkg/sentry/context"
	"gvisor.googlesource.com/gvisor/pkg/sentry/device"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
	"gvisor.googlesource.com/gvisor/pkg/syserror"
	"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/unix"
)

// Lookup loads an Inode at name into a Dirent based on the session's cache
// policy.
func (i *inodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name string) (*fs.Dirent, error) {
	cp := i.session().cachePolicy
	if cp.cacheReaddir() {
		// Check to see if we have readdirCache that indicates the
		// child does not exist.  Avoid holding readdirMu longer than
		// we need to.
		i.readdirMu.Lock()
		if i.readdirCache != nil && !i.readdirCache.Contains(name) {
			// No such child.
			i.readdirMu.Unlock()
			if cp.cacheNegativeDirents() {
				return fs.NewNegativeDirent(name), nil
			}
			return nil, syserror.ENOENT
		}
		i.readdirMu.Unlock()
	}

	// Get a p9.File for name.
	qids, newFile, mask, p9attr, err := i.fileState.file.walkGetAttr(ctx, []string{name})
	if err != nil {
		if err == syscall.ENOENT {
			if cp.cacheNegativeDirents() {
				// Return a negative Dirent. It will stay cached until something
				// is created over it.
				return fs.NewNegativeDirent(name), nil
			}
			return nil, syserror.ENOENT
		}
		return nil, err
	}

	// Construct the Inode operations.
	sattr, node := newInodeOperations(ctx, i.fileState.s, newFile, qids[0], mask, p9attr, false)

	// Construct a positive Dirent.
	return fs.NewDirent(fs.NewInode(node, dir.MountSource, sattr), name), nil
}

// Creates a new Inode at name and returns its File based on the session's cache policy.
//
// Ownership is currently ignored.
func (i *inodeOperations) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.FileFlags, perm fs.FilePermissions) (*fs.File, error) {
	// Create replaces the directory fid with the newly created/opened
	// file, so clone this directory so it doesn't change out from under
	// this node.
	_, newFile, err := i.fileState.file.walk(ctx, nil)
	if err != nil {
		return nil, err
	}

	// Map the FileFlags to p9 OpenFlags.
	var openFlags p9.OpenFlags
	switch {
	case flags.Read && flags.Write:
		openFlags = p9.ReadWrite
	case flags.Read:
		openFlags = p9.ReadOnly
	case flags.Write:
		openFlags = p9.WriteOnly
	default:
		panic(fmt.Sprintf("Create called with unknown or unset open flags: %v", flags))
	}

	owner := fs.FileOwnerFromContext(ctx)
	hostFile, err := newFile.create(ctx, name, openFlags, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID))
	if err != nil {
		// Could not create the file.
		return nil, err
	}

	i.touchModificationTime(ctx, dir)

	// Get the attributes of the file.
	qid, mask, p9attr, err := getattr(ctx, newFile)
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	// Get an unopened p9.File for the file we created so that it can be
	// cloned and re-opened multiple times after creation.
	_, unopened, err := i.fileState.file.walk(ctx, []string{name})
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	// Construct the InodeOperations.
	sattr, iops := newInodeOperations(ctx, i.fileState.s, unopened, qid, mask, p9attr, false)

	// Construct the positive Dirent.
	d := fs.NewDirent(fs.NewInode(iops, dir.MountSource, sattr), name)
	defer d.DecRef()

	// Construct the new file, caching the handles if allowed.
	h := &handles{
		File: newFile,
		Host: hostFile,
	}
	if iops.session().cachePolicy.usePageCache(d.Inode) {
		iops.fileState.setHandlesForCachedIO(flags, h)
	}
	return NewFile(ctx, d, name, flags, iops, h), nil
}

// CreateLink uses Create to create a symlink between oldname and newname.
func (i *inodeOperations) CreateLink(ctx context.Context, dir *fs.Inode, oldname string, newname string) error {
	owner := fs.FileOwnerFromContext(ctx)
	if _, err := i.fileState.file.symlink(ctx, oldname, newname, p9.UID(owner.UID), p9.GID(owner.GID)); err != nil {
		return err
	}
	i.touchModificationTime(ctx, dir)
	return nil
}

// CreateHardLink implements InodeOperations.CreateHardLink.
func (i *inodeOperations) CreateHardLink(ctx context.Context, inode *fs.Inode, target *fs.Inode, newName string) error {
	targetOpts, ok := target.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	if err := i.fileState.file.link(ctx, &targetOpts.fileState.file, newName); err != nil {
		return err
	}
	if i.session().cachePolicy.cacheUAttrs(inode) {
		// Increase link count.
		targetOpts.cachingInodeOps.IncLinks(ctx)
	}
	i.touchModificationTime(ctx, inode)
	return nil
}

// CreateDirectory uses Create to create a directory named s under inodeOperations.
func (i *inodeOperations) CreateDirectory(ctx context.Context, dir *fs.Inode, s string, perm fs.FilePermissions) error {
	owner := fs.FileOwnerFromContext(ctx)
	if _, err := i.fileState.file.mkdir(ctx, s, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID)); err != nil {
		return err
	}
	if i.session().cachePolicy.cacheUAttrs(dir) {
		// Increase link count.
		i.cachingInodeOps.IncLinks(ctx)
	}
	if i.session().cachePolicy.cacheReaddir() {
		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
	return nil
}

// Bind implements InodeOperations.Bind.
func (i *inodeOperations) Bind(ctx context.Context, dir *fs.Inode, name string, ep unix.BoundEndpoint, perm fs.FilePermissions) (*fs.Dirent, error) {
	if i.session().endpoints == nil {
		return nil, syscall.EOPNOTSUPP
	}

	// Create replaces the directory fid with the newly created/opened
	// file, so clone this directory so it doesn't change out from under
	// this node.
	_, newFile, err := i.fileState.file.walk(ctx, nil)
	if err != nil {
		return nil, err
	}

	// Stabilize the endpoint map while creation is in progress.
	unlock := i.session().endpoints.lock()
	defer unlock()

	// Create a regular file in the gofer and then mark it as a socket by
	// adding this inode key in the 'endpoints' map.
	owner := fs.FileOwnerFromContext(ctx)
	hostFile, err := newFile.create(ctx, name, p9.ReadWrite, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID))
	if err != nil {
		return nil, err
	}
	// We're not going to use this file.
	hostFile.Close()

	i.touchModificationTime(ctx, dir)

	// Get the attributes of the file to create inode key.
	qid, mask, attr, err := getattr(ctx, newFile)
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	key := device.MultiDeviceKey{
		Device:          attr.RDev,
		SecondaryDevice: i.session().connID,
		Inode:           qid.Path,
	}

	// Create child dirent.

	// Get an unopened p9.File for the file we created so that it can be
	// cloned and re-opened multiple times after creation.
	_, unopened, err := i.fileState.file.walk(ctx, []string{name})
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	// Construct the InodeOperations.
	sattr, iops := newInodeOperations(ctx, i.fileState.s, unopened, qid, mask, attr, true)

	// Construct the positive Dirent.
	childDir := fs.NewDirent(fs.NewInode(iops, dir.MountSource, sattr), name)
	i.session().endpoints.add(key, childDir, ep)
	return childDir, nil
}

// CreateFifo implements fs.InodeOperations.CreateFifo. Gofer nodes do not support the
// creation of fifos and always returns EOPNOTSUPP.
func (*inodeOperations) CreateFifo(context.Context, *fs.Inode, string, fs.FilePermissions) error {
	return syscall.EOPNOTSUPP
}

// Remove implements InodeOperations.Remove.
func (i *inodeOperations) Remove(ctx context.Context, dir *fs.Inode, name string) error {
	var key device.MultiDeviceKey
	removeSocket := false
	if i.session().endpoints != nil {
		// Find out if file being deleted is a socket that needs to be
		// removed from endpoint map.
		if d, err := i.Lookup(ctx, dir, name); err == nil {
			defer d.DecRef()
			if fs.IsSocket(d.Inode.StableAttr) {
				child := d.Inode.InodeOperations.(*inodeOperations)
				key = child.fileState.key
				removeSocket = true

				// Stabilize the endpoint map while deletion is in progress.
				unlock := i.session().endpoints.lock()
				defer unlock()
			}
		}
	}

	if err := i.fileState.file.unlinkAt(ctx, name, 0); err != nil {
		return err
	}
	if removeSocket {
		i.session().endpoints.remove(key)
	}
	i.touchModificationTime(ctx, dir)

	return nil
}

// Remove implements InodeOperations.RemoveDirectory.
func (i *inodeOperations) RemoveDirectory(ctx context.Context, dir *fs.Inode, name string) error {
	// 0x200 = AT_REMOVEDIR.
	if err := i.fileState.file.unlinkAt(ctx, name, 0x200); err != nil {
		return err
	}
	if i.session().cachePolicy.cacheUAttrs(dir) {
		// Decrease link count and updates atime.
		i.cachingInodeOps.DecLinks(ctx)
	}
	if i.session().cachePolicy.cacheReaddir() {
		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
	return nil
}

// Rename renames this node.
func (i *inodeOperations) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
	// Unwrap the new parent to a *inodeOperations.
	newParentInodeOperations, ok := newParent.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	// Unwrap the old parent to a *inodeOperations.
	oldParentInodeOperations, ok := oldParent.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	// Do the rename.
	if err := i.fileState.file.rename(ctx, newParentInodeOperations.fileState.file, newName); err != nil {
		return err
	}

	// Is the renamed entity a directory? Fix link counts.
	if fs.IsDir(i.fileState.sattr) {
		// Update cached state.
		if i.session().cachePolicy.cacheUAttrs(oldParent) {
			oldParentInodeOperations.cachingInodeOps.DecLinks(ctx)
		}
		if i.session().cachePolicy.cacheUAttrs(newParent) {
			newParentInodeOperations.cachingInodeOps.IncLinks(ctx)
		}
	}
	if i.session().cachePolicy.cacheReaddir() {
		// Mark old directory dirty.
		oldParentInodeOperations.markDirectoryDirty()
		if oldParent != newParent {
			// Mark new directory dirty.
			newParentInodeOperations.markDirectoryDirty()
		}
	}
	return nil
}

func (i *inodeOperations) touchModificationTime(ctx context.Context, inode *fs.Inode) {
	if i.session().cachePolicy.cacheUAttrs(inode) {
		i.cachingInodeOps.TouchModificationTime(ctx)
	}
	if i.session().cachePolicy.cacheReaddir() {
		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
}

// markDirectoryDirty marks any cached data dirty for this directory. This is necessary in order
// to ensure that this node does not retain stale state throughout its lifetime across multiple
// open directory handles.
//
// Currently this means invalidating any readdir caches.
func (i *inodeOperations) markDirectoryDirty() {
	i.readdirMu.Lock()
	defer i.readdirMu.Unlock()
	i.readdirCache = nil
}
<|endoftext|>


<commit_before>package tools

import (
	"fmt"
	"reflect"
	"strconv"
)

type ParseModelMap map[string]string

func ParseModel(model interface{}) (values ParseModelMap) {
	values = make(ParseModelMap)

	iVal := reflect.ValueOf(model).Elem()
	typ := iVal.Type()

	for i := 0; i < iVal.NumField(); i++ {
		f := iVal.Field(i)
		var v string

		switch f.Interface().(type) {
		case int, int8, int16, int32, int64:
			v = strconv.FormatInt(f.Int(), 10)
		case uint, uint8, uint16, uint32, uint64:
			v = strconv.FormatUint(f.Uint(), 10)
		case float32:
			v = strconv.FormatFloat(f.Float(), 'f', 4, 32)
		case float64:
			v = strconv.FormatFloat(f.Float(), 'f', 4, 64)
		case []byte:
			v = string(f.Bytes())
		case string:
			v = f.String()
		}

		values[typ.Field(i).Name] = v
	}
	return
}
<commit_msg>del path<commit_after>package tools

import (
	"reflect"
	"strconv"
)

type ParseModelMap map[string]string

func ParseModel(model interface{}) (values ParseModelMap) {
	values = make(ParseModelMap)

	iVal := reflect.ValueOf(model).Elem()
	typ := iVal.Type()

	for i := 0; i < iVal.NumField(); i++ {
		f := iVal.Field(i)
		var v string

		switch f.Interface().(type) {
		case int, int8, int16, int32, int64:
			v = strconv.FormatInt(f.Int(), 10)
		case uint, uint8, uint16, uint32, uint64:
			v = strconv.FormatUint(f.Uint(), 10)
		case float32:
			v = strconv.FormatFloat(f.Float(), 'f', 4, 32)
		case float64:
			v = strconv.FormatFloat(f.Float(), 'f', 4, 64)
		case []byte:
			v = string(f.Bytes())
		case string:
			v = f.String()
		}

		values[typ.Field(i).Name] = v
	}
	return
}
<|endoftext|>