<commit_before>package tarfile

import (
	"archive/tar"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/containers/image/docker/reference"
	"github.com/containers/image/internal/tmpdir"
	"github.com/containers/image/manifest"
	"github.com/containers/image/types"
	"github.com/opencontainers/go-digest"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// Destination is a partial implementation of types.ImageDestination for writing to an io.Writer.
type Destination struct {
	writer    io.Writer
	tar       *tar.Writer
	reference reference.NamedTagged
	// Other state.
	blobs  map[digest.Digest]types.BlobInfo // list of already-sent blobs
	config []byte
}

// NewDestination returns a tarfile.Destination for the specified io.Writer.
func NewDestination(dest io.Writer, ref reference.NamedTagged) *Destination {
	return &Destination{
		writer:    dest,
		tar:       tar.NewWriter(dest),
		reference: ref,
		blobs:     make(map[digest.Digest]types.BlobInfo),
	}
}

// SupportedManifestMIMETypes tells which manifest mime types the destination supports
// If an empty slice or nil it's returned, then any mime type can be tried to upload
func (d *Destination) SupportedManifestMIMETypes() []string {
	return []string{
		manifest.DockerV2Schema2MediaType, // We rely on the types.Image.UpdatedImage schema conversion capabilities.
	}
}

// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
// Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil.
func (d *Destination) SupportsSignatures() error {
	return errors.Errorf("Storing signatures for docker tar files is not supported")
}

// AcceptsForeignLayerURLs returns false iff foreign layers in manifest should be actually
// uploaded to the image destination, true otherwise.
func (d *Destination) AcceptsForeignLayerURLs() bool {
	return false
}

// MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime OS. False otherwise.
func (d *Destination) MustMatchRuntimeOS() bool {
	return false
}

// PutBlob writes contents of stream and returns data representing the result (with all data filled in).
// inputInfo.Digest can be optionally provided if known; it is not mandatory for the implementation to verify it.
// inputInfo.Size is the expected length of stream, if known.
// WARNING: The contents of stream are being verified on the fly.  Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
// to any other readers for download using the supplied digest.
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
func (d *Destination) PutBlob(stream io.Reader, inputInfo types.BlobInfo, isConfig bool) (types.BlobInfo, error) {
	// Ouch, we need to stream the blob into a temporary file just to determine the size.
	// When the layer is decompressed, we also have to generate the digest on uncompressed datas.
	if inputInfo.Size == -1 || inputInfo.Digest.String() == "" {
		logrus.Debugf("docker tarfile: input with unknown size, streaming to disk first ...")
		streamCopy, err := ioutil.TempFile(tmpdir.TemporaryDirectoryForBigFiles(), "docker-tarfile-blob")
		if err != nil {
			return types.BlobInfo{}, err
		}
		defer os.Remove(streamCopy.Name())
		defer streamCopy.Close()

		digester := digest.Canonical.Digester()
		tee := io.TeeReader(stream, digester.Hash())
		size, err := io.Copy(streamCopy, tee)
		if err != nil {
			return types.BlobInfo{}, err
		}
		_, err = streamCopy.Seek(0, os.SEEK_SET)
		if err != nil {
			return types.BlobInfo{}, err
		}
		inputInfo.Size = size // inputInfo is a struct, so we are only modifying our copy.
		if inputInfo.Digest == "" {
			inputInfo.Digest = digester.Digest()
		}
		stream = streamCopy
		logrus.Debugf("... streaming done")
	}

	// Maybe the blob has been already sent
	ok, size, err := d.HasBlob(inputInfo)
	if err != nil {
		return types.BlobInfo{}, err
	}
	if ok {
		return types.BlobInfo{Digest: inputInfo.Digest, Size: size}, nil
	}

	if isConfig {
		buf, err := ioutil.ReadAll(stream)
		if err != nil {
			return types.BlobInfo{}, errors.Wrap(err, "Error reading Config file stream")
		}
		d.config = buf
		if err := d.sendFile(inputInfo.Digest.Hex()+".json", inputInfo.Size, bytes.NewReader(buf)); err != nil {
			return types.BlobInfo{}, errors.Wrap(err, "Error writing Config file")
		}
	} else {
		if err := d.sendFile(filepath.Join(inputInfo.Digest.Hex(), legacyLayerFileName), inputInfo.Size, stream); err != nil {
			return types.BlobInfo{}, err
		}
	}
	d.blobs[inputInfo.Digest] = types.BlobInfo{Digest: inputInfo.Digest, Size: inputInfo.Size}
	return types.BlobInfo{Digest: inputInfo.Digest, Size: inputInfo.Size}, nil
}

// HasBlob returns true iff the image destination already contains a blob with
// the matching digest which can be reapplied using ReapplyBlob.  Unlike
// PutBlob, the digest can not be empty.  If HasBlob returns true, the size of
// the blob must also be returned.  If the destination does not contain the
// blob, or it is unknown, HasBlob ordinarily returns (false, -1, nil); it
// returns a non-nil error only on an unexpected failure.
func (d *Destination) HasBlob(info types.BlobInfo) (bool, int64, error) {
	if info.Digest == "" {
		return false, -1, errors.Errorf("Can not check for a blob with unknown digest")
	}
	if blob, ok := d.blobs[info.Digest]; ok {
		return true, blob.Size, nil
	}
	return false, -1, nil
}

// ReapplyBlob informs the image destination that a blob for which HasBlob
// previously returned true would have been passed to PutBlob if it had
// returned false.  Like HasBlob and unlike PutBlob, the digest can not be
// empty.  If the blob is a filesystem layer, this signifies that the changes
// it describes need to be applied again when composing a filesystem tree.
func (d *Destination) ReapplyBlob(info types.BlobInfo) (types.BlobInfo, error) {
	return info, nil
}

func (d *Destination) createRepositoriesFile(rootLayerID string) error {
	repositories := map[string]map[string]string{
		d.reference.Name(): {d.reference.Tag(): rootLayerID}}
	b, err := json.Marshal(repositories)
	if err != nil {
		return errors.Wrap(err, "Error marshaling repositories")
	}
	if err := d.sendBytes(legacyRepositoriesFileName, b); err != nil {
		return errors.Wrap(err, "Error writing config json file")
	}
	return nil
}

// PutManifest writes manifest to the destination.
// FIXME? This should also receive a MIME type if known, to differentiate between schema versions.
// If the destination is in principle available, refuses this manifest type (e.g. it does not recognize the schema),
// but may accept a different manifest type, the returned error must be an ManifestTypeRejectedError.
func (d *Destination) PutManifest(m []byte) error {
	// We do not bother with types.ManifestTypeRejectedError; our .SupportedManifestMIMETypes() above is already providing only one alternative,
	// so the caller trying a different manifest kind would be pointless.
	var man manifest.Schema2
	if err := json.Unmarshal(m, &man); err != nil {
		return errors.Wrap(err, "Error parsing manifest")
	}
	if man.SchemaVersion != 2 || man.MediaType != manifest.DockerV2Schema2MediaType {
		return errors.Errorf("Unsupported manifest type, need a Docker schema 2 manifest")
	}

	layerPaths, err := d.writeLegacyLayerMetadata(man.LayersDescriptors)
	if err != nil {
		return err
	}

	if len(man.LayersDescriptors) > 0 {
		if err := d.createRepositoriesFile(man.LayersDescriptors[len(man.LayersDescriptors)-1].Digest.Hex()); err != nil {
			return err
		}
	}

	// For github.com/docker/docker consumers, this works just as well as
	//   refString := ref.String()
	// because when reading the RepoTags strings, github.com/docker/docker/reference
	// normalizes both of them to the same value.
	//
	// Doing it this way to include the normalized-out `docker.io[/library]` does make
	// a difference for github.com/projectatomic/docker consumers, with the
	// “Add --add-registry and --block-registry options to docker daemon” patch.
	// These consumers treat reference strings which include a hostname and reference
	// strings without a hostname differently.
	//
	// Using the host name here is more explicit about the intent, and it has the same
	// effect as (docker pull) in projectatomic/docker, which tags the result using
	// a hostname-qualified reference.
	// See https://github.com/containers/image/issues/72 for a more detailed
	// analysis and explanation.
	refString := fmt.Sprintf("%s:%s", d.reference.Name(), d.reference.Tag())
	items := []ManifestItem{{
		Config:       man.ConfigDescriptor.Digest.Hex() + ".json",
		RepoTags:     []string{refString},
		Layers:       layerPaths,
		Parent:       "",
		LayerSources: nil,
	}}
	itemsBytes, err := json.Marshal(&items)
	if err != nil {
		return err
	}

	// FIXME? Do we also need to support the legacy format?
	return d.sendBytes(manifestFileName, itemsBytes)
}

// writeLegacyLayerMetadata writes legacy VERSION and configuration files for all layers
func (d *Destination) writeLegacyLayerMetadata(layerDescriptors []manifest.Schema2Descriptor) (layerPaths []string, err error) {
	for i, l := range layerDescriptors {
		layerPaths = append(layerPaths, filepath.Join(l.Digest.Hex(), legacyLayerFileName))
		b := []byte("1.0")
		if err := d.sendBytes(filepath.Join(l.Digest.Hex(), legacyVersionFileName), b); err != nil {
			return []string{}, errors.Wrap(err, "Error writing VERSION file")
		}

		// The legacy format requires a config file per layer
		layerConfig := make(map[string]interface{})
		id := l.Digest.Hex()
		layerConfig["id"] = id

		// The root layer doesn't have any parent
		if i != 0 {
			layerConfig["parent"] = layerDescriptors[i-1].Digest.Hex()
		}
		// The root layer configuration file is generated by using subpart of the image configuration
		if i == len(layerDescriptors)-1 {
			var config map[string]*json.RawMessage
			err := json.Unmarshal(d.config, &config)
			if err != nil {
				return []string{}, errors.Wrap(err, "Error unmarshaling config")
			}
			for _, attr := range [7]string{"architecture", "config", "container", "container_config", "created", "docker_version", "os"} {
				layerConfig[attr] = config[attr]
			}
		}
		b, err := json.Marshal(layerConfig)
		if err != nil {
			return []string{}, errors.Wrap(err, "Error marshaling layer config")
		}
		if err := d.sendBytes(filepath.Join(id, legacyConfigFileName), b); err != nil {
			return []string{}, errors.Wrap(err, "Error writing config json file")
		}
	}
	return layerPaths, nil
}

type tarFI struct {
	path string
	size int64
}

func (t *tarFI) Name() string {
	return t.path
}
func (t *tarFI) Size() int64 {
	return t.size
}
func (t *tarFI) Mode() os.FileMode {
	return 0444
}
func (t *tarFI) ModTime() time.Time {
	return time.Unix(0, 0)
}
func (t *tarFI) IsDir() bool {
	return false
}
func (t *tarFI) Sys() interface{} {
	return nil
}

// sendBytes sends a path into the tar stream.
func (d *Destination) sendBytes(path string, b []byte) error {
	return d.sendFile(path, int64(len(b)), bytes.NewReader(b))
}

// sendFile sends a file into the tar stream.
func (d *Destination) sendFile(path string, expectedSize int64, stream io.Reader) error {
	hdr, err := tar.FileInfoHeader(&tarFI{path: path, size: expectedSize}, "")
	if err != nil {
		return nil
	}
	logrus.Debugf("Sending as tar file %s", path)
	if err := d.tar.WriteHeader(hdr); err != nil {
		return err
	}
	size, err := io.Copy(d.tar, stream)
	if err != nil {
		return err
	}
	if size != expectedSize {
		return errors.Errorf("Size mismatch when copying %s, expected %d, got %d", path, expectedSize, size)
	}
	return nil
}

// PutSignatures adds the given signatures to the docker tarfile (currently not
// supported). MUST be called after PutManifest (signatures reference manifest
// contents)
func (d *Destination) PutSignatures(signatures [][]byte) error {
	if len(signatures) != 0 {
		return errors.Errorf("Storing signatures for docker tar files is not supported")
	}
	return nil
}

// Commit finishes writing data to the underlying io.Writer.
// It is the caller's responsibility to close it, if necessary.
func (d *Destination) Commit() error {
	return d.tar.Close()
}
<commit_msg>docker-archive: repeated layers are symlinked in the tar file<commit_after>package tarfile

import (
	"archive/tar"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/containers/image/docker/reference"
	"github.com/containers/image/internal/tmpdir"
	"github.com/containers/image/manifest"
	"github.com/containers/image/types"
	"github.com/opencontainers/go-digest"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// Destination is a partial implementation of types.ImageDestination for writing to an io.Writer.
type Destination struct {
	writer    io.Writer
	tar       *tar.Writer
	reference reference.NamedTagged
	// Other state.
	blobs  map[digest.Digest]types.BlobInfo // list of already-sent blobs
	config []byte
}

// NewDestination returns a tarfile.Destination for the specified io.Writer.
func NewDestination(dest io.Writer, ref reference.NamedTagged) *Destination {
	return &Destination{
		writer:    dest,
		tar:       tar.NewWriter(dest),
		reference: ref,
		blobs:     make(map[digest.Digest]types.BlobInfo),
	}
}

// SupportedManifestMIMETypes tells which manifest mime types the destination supports
// If an empty slice or nil it's returned, then any mime type can be tried to upload
func (d *Destination) SupportedManifestMIMETypes() []string {
	return []string{
		manifest.DockerV2Schema2MediaType, // We rely on the types.Image.UpdatedImage schema conversion capabilities.
	}
}

// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
// Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil.
func (d *Destination) SupportsSignatures() error {
	return errors.Errorf("Storing signatures for docker tar files is not supported")
}

// AcceptsForeignLayerURLs returns false iff foreign layers in manifest should be actually
// uploaded to the image destination, true otherwise.
func (d *Destination) AcceptsForeignLayerURLs() bool {
	return false
}

// MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime OS. False otherwise.
func (d *Destination) MustMatchRuntimeOS() bool {
	return false
}

// PutBlob writes contents of stream and returns data representing the result (with all data filled in).
// inputInfo.Digest can be optionally provided if known; it is not mandatory for the implementation to verify it.
// inputInfo.Size is the expected length of stream, if known.
// WARNING: The contents of stream are being verified on the fly.  Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
// to any other readers for download using the supplied digest.
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
func (d *Destination) PutBlob(stream io.Reader, inputInfo types.BlobInfo, isConfig bool) (types.BlobInfo, error) {
	// Ouch, we need to stream the blob into a temporary file just to determine the size.
	// When the layer is decompressed, we also have to generate the digest on uncompressed datas.
	if inputInfo.Size == -1 || inputInfo.Digest.String() == "" {
		logrus.Debugf("docker tarfile: input with unknown size, streaming to disk first ...")
		streamCopy, err := ioutil.TempFile(tmpdir.TemporaryDirectoryForBigFiles(), "docker-tarfile-blob")
		if err != nil {
			return types.BlobInfo{}, err
		}
		defer os.Remove(streamCopy.Name())
		defer streamCopy.Close()

		digester := digest.Canonical.Digester()
		tee := io.TeeReader(stream, digester.Hash())
		size, err := io.Copy(streamCopy, tee)
		if err != nil {
			return types.BlobInfo{}, err
		}
		_, err = streamCopy.Seek(0, os.SEEK_SET)
		if err != nil {
			return types.BlobInfo{}, err
		}
		inputInfo.Size = size // inputInfo is a struct, so we are only modifying our copy.
		if inputInfo.Digest == "" {
			inputInfo.Digest = digester.Digest()
		}
		stream = streamCopy
		logrus.Debugf("... streaming done")
	}

	// Maybe the blob has been already sent
	ok, size, err := d.HasBlob(inputInfo)
	if err != nil {
		return types.BlobInfo{}, err
	}
	if ok {
		return types.BlobInfo{Digest: inputInfo.Digest, Size: size}, nil
	}

	if isConfig {
		buf, err := ioutil.ReadAll(stream)
		if err != nil {
			return types.BlobInfo{}, errors.Wrap(err, "Error reading Config file stream")
		}
		d.config = buf
		if err := d.sendFile(inputInfo.Digest.Hex()+".json", inputInfo.Size, bytes.NewReader(buf)); err != nil {
			return types.BlobInfo{}, errors.Wrap(err, "Error writing Config file")
		}
	} else {
		if err := d.sendFile(filepath.Join(inputInfo.Digest.Hex(), legacyLayerFileName), inputInfo.Size, stream); err != nil {
			return types.BlobInfo{}, err
		}
	}
	d.blobs[inputInfo.Digest] = types.BlobInfo{Digest: inputInfo.Digest, Size: inputInfo.Size}
	return types.BlobInfo{Digest: inputInfo.Digest, Size: inputInfo.Size}, nil
}

// HasBlob returns true iff the image destination already contains a blob with
// the matching digest which can be reapplied using ReapplyBlob.  Unlike
// PutBlob, the digest can not be empty.  If HasBlob returns true, the size of
// the blob must also be returned.  If the destination does not contain the
// blob, or it is unknown, HasBlob ordinarily returns (false, -1, nil); it
// returns a non-nil error only on an unexpected failure.
func (d *Destination) HasBlob(info types.BlobInfo) (bool, int64, error) {
	if info.Digest == "" {
		return false, -1, errors.Errorf("Can not check for a blob with unknown digest")
	}
	if blob, ok := d.blobs[info.Digest]; ok {
		return true, blob.Size, nil
	}
	return false, -1, nil
}

// ReapplyBlob informs the image destination that a blob for which HasBlob
// previously returned true would have been passed to PutBlob if it had
// returned false.  Like HasBlob and unlike PutBlob, the digest can not be
// empty.  If the blob is a filesystem layer, this signifies that the changes
// it describes need to be applied again when composing a filesystem tree.
func (d *Destination) ReapplyBlob(info types.BlobInfo) (types.BlobInfo, error) {
	return info, nil
}

func (d *Destination) createRepositoriesFile(rootLayerID string) error {
	repositories := map[string]map[string]string{
		d.reference.Name(): {d.reference.Tag(): rootLayerID}}
	b, err := json.Marshal(repositories)
	if err != nil {
		return errors.Wrap(err, "Error marshaling repositories")
	}
	if err := d.sendBytes(legacyRepositoriesFileName, b); err != nil {
		return errors.Wrap(err, "Error writing config json file")
	}
	return nil
}

// PutManifest writes manifest to the destination.
// FIXME? This should also receive a MIME type if known, to differentiate between schema versions.
// If the destination is in principle available, refuses this manifest type (e.g. it does not recognize the schema),
// but may accept a different manifest type, the returned error must be an ManifestTypeRejectedError.
func (d *Destination) PutManifest(m []byte) error {
	// We do not bother with types.ManifestTypeRejectedError; our .SupportedManifestMIMETypes() above is already providing only one alternative,
	// so the caller trying a different manifest kind would be pointless.
	var man manifest.Schema2
	if err := json.Unmarshal(m, &man); err != nil {
		return errors.Wrap(err, "Error parsing manifest")
	}
	if man.SchemaVersion != 2 || man.MediaType != manifest.DockerV2Schema2MediaType {
		return errors.Errorf("Unsupported manifest type, need a Docker schema 2 manifest")
	}

	layerPaths, err := d.writeLegacyLayerMetadata(man.LayersDescriptors)
	if err != nil {
		return err
	}

	if len(man.LayersDescriptors) > 0 {
		if err := d.createRepositoriesFile(man.LayersDescriptors[len(man.LayersDescriptors)-1].Digest.Hex()); err != nil {
			return err
		}
	}

	// For github.com/docker/docker consumers, this works just as well as
	//   refString := ref.String()
	// because when reading the RepoTags strings, github.com/docker/docker/reference
	// normalizes both of them to the same value.
	//
	// Doing it this way to include the normalized-out `docker.io[/library]` does make
	// a difference for github.com/projectatomic/docker consumers, with the
	// “Add --add-registry and --block-registry options to docker daemon” patch.
	// These consumers treat reference strings which include a hostname and reference
	// strings without a hostname differently.
	//
	// Using the host name here is more explicit about the intent, and it has the same
	// effect as (docker pull) in projectatomic/docker, which tags the result using
	// a hostname-qualified reference.
	// See https://github.com/containers/image/issues/72 for a more detailed
	// analysis and explanation.
	refString := fmt.Sprintf("%s:%s", d.reference.Name(), d.reference.Tag())
	items := []ManifestItem{{
		Config:       man.ConfigDescriptor.Digest.Hex() + ".json",
		RepoTags:     []string{refString},
		Layers:       layerPaths,
		Parent:       "",
		LayerSources: nil,
	}}
	itemsBytes, err := json.Marshal(&items)
	if err != nil {
		return err
	}

	// FIXME? Do we also need to support the legacy format?
	return d.sendBytes(manifestFileName, itemsBytes)
}

// writeRepeatedLayer creates a symlink to the existing layer. It
// returns a new layer ID which is computed by using the layerID and the parentID.
func (d *Destination) writeRepeatedLayer(layerID string, parentID string) (newLayerID string, err error) {
	newLayerID = digest.Canonical.FromString(layerID + " " + parentID).Hex()
	layerPath := filepath.Join(newLayerID, legacyLayerFileName)
	targetPath := filepath.Join("../", layerID, legacyLayerFileName)
	if err := d.sendSymlink(layerPath, targetPath); err != nil {
		return "", errors.Wrap(err, "Error writing symlink %s -> %s")
	}
	return
}

// writeLegacyLayerMetadata writes legacy VERSION and configuration files for all layers
func (d *Destination) writeLegacyLayerMetadata(layerDescriptors []manifest.Schema2Descriptor) (layerPaths []string, err error) {
	var parentID string

	for i, l := range layerDescriptors {
		layerID := l.Digest.Hex()

		// If the layer already exists, a new layer id is generated
		// and a symlink is created that targets the already existing layer.
		for _, lParent := range layerDescriptors[:i] {
			if layerID == lParent.Digest.Hex() {
				layerID, err = d.writeRepeatedLayer(layerID, parentID)
				if err != nil {
					return []string{}, err
				}
				break
			}
		}

		layerPaths = append(layerPaths, filepath.Join(layerID, legacyLayerFileName))
		b := []byte("1.0")
		if err := d.sendBytes(filepath.Join(layerID, legacyVersionFileName), b); err != nil {
			return []string{}, errors.Wrap(err, "Error writing VERSION file")
		}

		// The legacy format requires a config file per layer
		layerConfig := make(map[string]interface{})
		layerConfig["id"] = layerID

		// The root layer doesn't have any parent
		if i != 0 {
			layerConfig["parent"] = parentID
		}
		// The root layer configuration file is generated by using subpart of the image configuration
		if i == len(layerDescriptors)-1 {
			var config map[string]*json.RawMessage
			err := json.Unmarshal(d.config, &config)
			if err != nil {
				return []string{}, errors.Wrap(err, "Error unmarshaling config")
			}
			for _, attr := range [7]string{"architecture", "config", "container", "container_config", "created", "docker_version", "os"} {
				layerConfig[attr] = config[attr]
			}
		}
		b, err := json.Marshal(layerConfig)
		if err != nil {
			return []string{}, errors.Wrap(err, "Error marshaling layer config")
		}
		if err := d.sendBytes(filepath.Join(layerID, legacyConfigFileName), b); err != nil {
			return []string{}, errors.Wrap(err, "Error writing config json file")
		}
		parentID = layerID
	}
	return layerPaths, nil
}

type tarFI struct {
	path      string
	size      int64
	isSymlink bool
}

func (t *tarFI) Name() string {
	return t.path
}
func (t *tarFI) Size() int64 {
	return t.size
}
func (t *tarFI) Mode() os.FileMode {
	if t.isSymlink {
		return os.ModeSymlink
	}
	return 0444
}
func (t *tarFI) ModTime() time.Time {
	return time.Unix(0, 0)
}
func (t *tarFI) IsDir() bool {
	return false
}
func (t *tarFI) Sys() interface{} {
	return nil
}

// sendSymlink sends a symlink into the tar stream.
func (d *Destination) sendSymlink(path string, target string) error {
	hdr, err := tar.FileInfoHeader(&tarFI{path: path, size: 0, isSymlink: true}, target)
	if err != nil {
		return nil
	}
	logrus.Debugf("Sending as tar link %s -> %s", path, target)
	return d.tar.WriteHeader(hdr)
}

// sendBytes sends a path into the tar stream.
func (d *Destination) sendBytes(path string, b []byte) error {
	return d.sendFile(path, int64(len(b)), bytes.NewReader(b))
}

// sendFile sends a file into the tar stream.
func (d *Destination) sendFile(path string, expectedSize int64, stream io.Reader) error {
	hdr, err := tar.FileInfoHeader(&tarFI{path: path, size: expectedSize}, "")
	if err != nil {
		return nil
	}
	logrus.Debugf("Sending as tar file %s", path)
	if err := d.tar.WriteHeader(hdr); err != nil {
		return err
	}
	size, err := io.Copy(d.tar, stream)
	if err != nil {
		return err
	}
	if size != expectedSize {
		return errors.Errorf("Size mismatch when copying %s, expected %d, got %d", path, expectedSize, size)
	}
	return nil
}

// PutSignatures adds the given signatures to the docker tarfile (currently not
// supported). MUST be called after PutManifest (signatures reference manifest
// contents)
func (d *Destination) PutSignatures(signatures [][]byte) error {
	if len(signatures) != 0 {
		return errors.Errorf("Storing signatures for docker tar files is not supported")
	}
	return nil
}

// Commit finishes writing data to the underlying io.Writer.
// It is the caller's responsibility to close it, if necessary.
func (d *Destination) Commit() error {
	return d.tar.Close()
}
<|endoftext|>


<commit_before>package amqp_test

import (
	"errors"
	"fmt"
	"github.com/streadway/amqp"
	"log"
	"os"
	"time"
)

// This exports a Queue object that wraps this library. It
// automatically reconnects when the connection fails, and
// blocks all pushes until the connection succeeds. It also
// confirms every outgoing message, so none are lost.
// It doesn't automatically ack each message, but leaves that
// to the parent process, since it is usage-dependent.
//
// Try running this in one terminal, and `rabbitmq-server` in another.
// Stop & restart RabbitMQ to see how the queue reacts.
func Example() {
	name := "job_queue"
	addr := "amqp://guest:guest@localhost:5672/"
	queue := New(name, addr)
	message := []byte("message")
	// Attempt to push a message every 2 seconds
	for {
		time.Sleep(time.Second * 3)
		if err := queue.Push(message); err != nil {
			fmt.Printf("Push failed: %s\n", err)
		} else {
			fmt.Println("Push succeeded!")
		}
	}
}

// Queue represents a connection to a specific queue.
type Queue struct {
	name            string
	logger          *log.Logger
	connection      *amqp.Connection
	channel         *amqp.Channel
	done            chan bool
	notifyConnClose chan *amqp.Error
	notifyChanClose chan *amqp.Error
	notifyConfirm   chan amqp.Confirmation
	isReady         bool
}

const (
	// When reconnecting to the server after connection failure
	reconnectDelay = 5 * time.Second

	// When setting up the channel after a channel exception
	resetupDelay = 2 * time.Second

	// When resending messages the server didn't confirm
	resendDelay = 5 * time.Second
)

var (
	errNotConnected  = errors.New("not connected to the queue")
	errAlreadyClosed = errors.New("already closed: not connected to the queue")
	errShutdown      = errors.New("queue is shutting down")
)

// New creates a new queue instance, and automatically
// attempts to connect to the server.
func New(name string, addr string) *Queue {
	queue := Queue{
		logger: log.New(os.Stdout, "", log.LstdFlags),
		name:   name,
		done:   make(chan bool),
	}
	go queue.handleReconnect(addr)
	return &queue
}

// handleReconnect will wait for a connection error on
// notifyConnClose, and then continously attempt to reconnect.
func (queue *Queue) handleReconnect(addr string) {
	for {
		queue.isReady = false
		log.Println("Attempting to connect")

		conn, err := queue.connect(addr)

		if err != nil {
			log.Println("Failed to connect. Retrying...")

			select {
			case <-queue.done:
				return
			case <-time.After(reconnectDelay):
			}
			continue
		}

		if done := queue.handelResetup(conn); done {
			break
		}
	}
}

// connect will create a new amqp connection
func (queue *Queue) connect(addr string) (*amqp.Connection, error) {
	conn, err := amqp.Dial(addr)

	if err != nil {
		return nil, err
	}

	queue.changeConnection(conn)
	log.Println("Connected!")
	return conn, nil
}

func (queue *Queue) handelResetup(conn *amqp.Connection) bool {
	for {
		queue.isReady = false

		err := queue.setup(conn)

		if err != nil {
			log.Println("Failed to setup queue. Retrying...")

			select {
			case <-queue.done:
				return true
			case <-time.After(resetupDelay):
			}
			continue
		}

		select {
		case <-queue.done:
			return true
		case <-queue.notifyConnClose:
			log.Println("Connection closed. Reconnecting...")
			return false
		case <-queue.notifyChanClose:
			log.Println("Channel closed. Re-running setup...")
		}
	}
}

// setup will setup channel & declare queue
func (queue *Queue) setup(conn *amqp.Connection) error {
	ch, err := conn.Channel()

	if err != nil {
		return err
	}

	err = ch.Confirm(false)

	if err != nil {
		return err
	}
	_, err = ch.QueueDeclare(
		queue.name,
		false, // Durable
		false, // Delete when unused
		false, // Exclusive
		false, // No-wait
		nil,   // Arguments
	)

	if err != nil {
		return err
	}

	queue.changeChannel(ch)
	queue.isReady = true
	log.Println("Setup!")

	return nil
}

// changeConnection takes a new connection to the queue,
// and updates the close listener to reflect this.
func (queue *Queue) changeConnection(connection *amqp.Connection) {
	queue.connection = connection
	queue.notifyConnClose = make(chan *amqp.Error)
	queue.connection.NotifyClose(queue.notifyConnClose)
}

// changeChannel takes a new channel to the queue,
// and updates the channel listeners to reflect this.
func (queue *Queue) changeChannel(channel *amqp.Channel) {
	queue.channel = channel
	queue.notifyChanClose = make(chan *amqp.Error)
	queue.notifyConfirm = make(chan amqp.Confirmation)
	queue.channel.NotifyClose(queue.notifyChanClose)
	queue.channel.NotifyPublish(queue.notifyConfirm)
}

// Push will push data onto the queue, and wait for a confirm.
// If no confirms are recieved until within the resendTimeout,
// it continuously resends messages until a confirm is recieved.
// This will block until the server sends a confirm. Errors are
// only returned if the push action itself fails, see UnsafePush.
func (queue *Queue) Push(data []byte) error {
	if !queue.isReady {
		return errors.New("failed to push push: not connected")
	}
	for {
		err := queue.UnsafePush(data)
		if err != nil {
			queue.logger.Println("Push failed. Retrying...")
			select {
			case <-queue.done:
				return errShutdown
			case <-time.After(resendDelay):
			}
			continue
		}
		select {
		case confirm := <-queue.notifyConfirm:
			if confirm.Ack {
				queue.logger.Println("Push confirmed!")
				return nil
			}
		case <-time.After(resendDelay):
		}
		queue.logger.Println("Push didn't confirm. Retrying...")
	}
}

// UnsafePush will push to the queue without checking for
// confirmation. It returns an error if it fails to connect.
// No guarantees are provided for whether the server will
// recieve the message.
func (queue *Queue) UnsafePush(data []byte) error {
	if !queue.isReady {
		return errNotConnected
	}
	return queue.channel.Publish(
		"",         // Exchange
		queue.name, // Routing key
		false,      // Mandatory
		false,      // Immediate
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        data,
		},
	)
}

// Stream will continuously put queue items on the channel.
// It is required to call delivery.Ack when it has been
// successfully processed, or delivery.Nack when it fails.
// Ignoring this will cause data to build up on the server.
func (queue *Queue) Stream() (<-chan amqp.Delivery, error) {
	if !queue.isReady {
		return nil, errNotConnected
	}
	return queue.channel.Consume(
		queue.name,
		"",    // Consumer
		false, // Auto-Ack
		false, // Exclusive
		false, // No-local
		false, // No-Wait
		nil,   // Args
	)
}

// Close will cleanly shutdown the channel and connection.
func (queue *Queue) Close() error {
	if !queue.isReady {
		return errAlreadyClosed
	}
	err := queue.channel.Close()
	if err != nil {
		return err
	}
	err = queue.connection.Close()
	if err != nil {
		return err
	}
	close(queue.done)
	queue.isReady = false
	return nil
}
<commit_msg>Update namings<commit_after>package amqp_test

import (
	"errors"
	"fmt"
	"github.com/streadway/amqp"
	"log"
	"os"
	"time"
)

// This exports a Session object that wraps this library. It
// automatically reconnects when the connection fails, and
// blocks all pushes until the connection succeeds. It also
// confirms every outgoing message, so none are lost.
// It doesn't automatically ack each message, but leaves that
// to the parent process, since it is usage-dependent.
//
// Try running this in one terminal, and `rabbitmq-server` in another.
// Stop & restart RabbitMQ to see how the queue reacts.
func Example() {
	name := "job_queue"
	addr := "amqp://guest:guest@localhost:5672/"
	queue := New(name, addr)
	message := []byte("message")
	// Attempt to push a message every 2 seconds
	for {
		time.Sleep(time.Second * 3)
		if err := queue.Push(message); err != nil {
			fmt.Printf("Push failed: %s\n", err)
		} else {
			fmt.Println("Push succeeded!")
		}
	}
}

type Session struct {
	name            string
	logger          *log.Logger
	connection      *amqp.Connection
	channel         *amqp.Channel
	done            chan bool
	notifyConnClose chan *amqp.Error
	notifyChanClose chan *amqp.Error
	notifyConfirm   chan amqp.Confirmation
	isReady         bool
}

const (
	// When reconnecting to the server after connection failure
	reconnectDelay = 5 * time.Second

	// When setting up the channel after a channel exception
	reInitDelay = 2 * time.Second

	// When resending messages the server didn't confirm
	resendDelay = 5 * time.Second
)

var (
	errNotConnected  = errors.New("not connected to a server")
	errAlreadyClosed = errors.New("already closed: not connected to the server")
	errShutdown      = errors.New("session is shutting down")
)

// New creates a new consumer state instance, and automatically
// attempts to connect to the server.
func New(name string, addr string) *Session {
	session := Session{
		logger: log.New(os.Stdout, "", log.LstdFlags),
		name:   name,
		done:   make(chan bool),
	}
	go session.handleReconnect(addr)
	return &session
}

// handleReconnect will wait for a connection error on
// notifyConnClose, and then continuously attempt to reconnect.
func (session *Session) handleReconnect(addr string) {
	for {
		session.isReady = false
		log.Println("Attempting to connect")

		conn, err := session.connect(addr)

		if err != nil {
			log.Println("Failed to connect. Retrying...")

			select {
			case <-session.done:
				return
			case <-time.After(reconnectDelay):
			}
			continue
		}

		if done := session.handleReInit(conn); done {
			break
		}
	}
}

// connect will create a new AMQP connection
func (session *Session) connect(addr string) (*amqp.Connection, error) {
	conn, err := amqp.Dial(addr)

	if err != nil {
		return nil, err
	}

	session.changeConnection(conn)
	log.Println("Connected!")
	return conn, nil
}

// handleReconnect will wait for a channel error
// and then continuously attempt to re-initialize both channels
func (session *Session) handleReInit(conn *amqp.Connection) bool {
	for {
		session.isReady = false

		err := session.init(conn)

		if err != nil {
			log.Println("Failed to initialize channel. Retrying...")

			select {
			case <-session.done:
				return true
			case <-time.After(reInitDelay):
			}
			continue
		}

		select {
		case <-session.done:
			return true
		case <-session.notifyConnClose:
			log.Println("Connection closed. Reconnecting...")
			return false
		case <-session.notifyChanClose:
			log.Println("Channel closed. Re-running init...")
		}
	}
}

// init will initialize channel & declare queue
func (session *Session) init(conn *amqp.Connection) error {
	ch, err := conn.Channel()

	if err != nil {
		return err
	}

	err = ch.Confirm(false)

	if err != nil {
		return err
	}
	_, err = ch.QueueDeclare(
		session.name,
		false, // Durable
		false, // Delete when unused
		false, // Exclusive
		false, // No-wait
		nil,   // Arguments
	)

	if err != nil {
		return err
	}

	session.changeChannel(ch)
	session.isReady = true
	log.Println("Setup!")

	return nil
}

// changeConnection takes a new connection to the queue,
// and updates the close listener to reflect this.
func (session *Session) changeConnection(connection *amqp.Connection) {
	session.connection = connection
	session.notifyConnClose = make(chan *amqp.Error)
	session.connection.NotifyClose(session.notifyConnClose)
}

// changeChannel takes a new channel to the queue,
// and updates the channel listeners to reflect this.
func (session *Session) changeChannel(channel *amqp.Channel) {
	session.channel = channel
	session.notifyChanClose = make(chan *amqp.Error)
	session.notifyConfirm = make(chan amqp.Confirmation)
	session.channel.NotifyClose(session.notifyChanClose)
	session.channel.NotifyPublish(session.notifyConfirm)
}

// Push will push data onto the queue, and wait for a confirm.
// If no confirms are received until within the resendTimeout,
// it continuously re-sends messages until a confirm is received.
// This will block until the server sends a confirm. Errors are
// only returned if the push action itself fails, see UnsafePush.
func (session *Session) Push(data []byte) error {
	if !session.isReady {
		return errors.New("failed to push push: not connected")
	}
	for {
		err := session.UnsafePush(data)
		if err != nil {
			session.logger.Println("Push failed. Retrying...")
			select {
			case <-session.done:
				return errShutdown
			case <-time.After(resendDelay):
			}
			continue
		}
		select {
		case confirm := <-session.notifyConfirm:
			if confirm.Ack {
				session.logger.Println("Push confirmed!")
				return nil
			}
		case <-time.After(resendDelay):
		}
		session.logger.Println("Push didn't confirm. Retrying...")
	}
}

// UnsafePush will push to the queue without checking for
// confirmation. It returns an error if it fails to connect.
// No guarantees are provided for whether the server will
// recieve the message.
func (session *Session) UnsafePush(data []byte) error {
	if !session.isReady {
		return errNotConnected
	}
	return session.channel.Publish(
		"",           // Exchange
		session.name, // Routing key
		false,        // Mandatory
		false,        // Immediate
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        data,
		},
	)
}

// Stream will continuously put queue items on the channel.
// It is required to call delivery.Ack when it has been
// successfully processed, or delivery.Nack when it fails.
// Ignoring this will cause data to build up on the server.
func (session *Session) Stream() (<-chan amqp.Delivery, error) {
	if !session.isReady {
		return nil, errNotConnected
	}
	return session.channel.Consume(
		session.name,
		"",    // Consumer
		false, // Auto-Ack
		false, // Exclusive
		false, // No-local
		false, // No-Wait
		nil,   // Args
	)
}

// Close will cleanly shutdown the channel and connection.
func (session *Session) Close() error {
	if !session.isReady {
		return errAlreadyClosed
	}
	err := session.channel.Close()
	if err != nil {
		return err
	}
	err = session.connection.Close()
	if err != nil {
		return err
	}
	close(session.done)
	session.isReady = false
	return nil
}
<|endoftext|>


<commit_before>package ov

import (
	"encoding/json"
	"errors"
	"strings"

	"github.com/HewlettPackard/oneview-golang/rest"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/docker/machine/libmachine/log"
)

type parentBundle struct {
	ParentBundleName string `json:"parentBundleName,omitempty"`
	ReleaseDate      string `json:"releaseDate,omitempty"`
	Version          string `json:"version,omitempty"`
}

type HotFixes struct {
	HotfixName  string `json:"hotfixName,omitempty"`
	ReleaseDate string `json:"releaseDate,omitempty"`
	ResourceId  string `json:"resourceId,omitempty"`
}

type FWComponents struct {
	ComponentVersion string          `json:"componentVersion,omitempty"`
	FileName         string          `json:"fileName,omitempty"`
	Name             string          `json:"name,omitempty"`
	SwKeyNameList    []utils.Nstring `json:"swKeyNameList,omitempty"`
}

type FirmwareDrivers struct {
	BaselineShortName     string              `json:"baselineShortName,omitempty"`
	BundleSize            int                 `json:"bundleSize,omitempty"`
	BundleType            string              `json:"bundleType,omitempty"`
	Category              string              `json:"category,omitempty"`
	Created               string              `json:"created,omitempty"`
	Description           string              `json:"description,omitempty"`
	ETAG                  string              `json:"eTag,omitempty"`
	EsxiOsDriverMetaData  []utils.Nstring     `json:"esxiOsDriverMetaData,omitempty"`
	FwComponents          []FWComponents      `json:"fwComponents,omitempty"`
	Hotfixes              []HotFixes          `json:"hotfixes,omitempty"`
	HpsumVersion          string              `json:"hpsumVersion,omitempty"`
	IsoFileName           string              `json:"isoFileName,omitempty"`
	LastTaskUri           string              `json:"lastTaskUri,omitempty"`
	Locations             map[string]string   `json:"locations,omitempty"`
	Mirrorlist            map[string][]string `json:"mirrorlist,omitempty"`
	Modified              string              `json:"modified,omitempty"`
	Name                  string              `json:"name,omitempty"`
	ParentBundle          parentBundle        `json:"parentBundle,omitempty"`
	ReleaseDate           string              `json:"releaseDate,omitempty"`
	ResourceId            string              `json:"resourceId,omitempty"`
	ResourceState         string              `json:"resourceState,omitempty"`
	ScopesUri             string              `json:"scopesUri,omitempty"`
	SignatureFileName     string              `json:"signatureFileName,omitempty"`
	SignatureFileRequired bool                `json:"signatureFileRequired,omitempty"`
	State                 string              `json:"state,omitempty"`
	Status                string              `json:"status,omitempty"`
	SupportedLanguages    string              `json:"supportedLanguages,omitempty"`
	SupportedOSList       []utils.Nstring     `json:"supportedOSList,omitempty"`
	SwPackagesFullPath    string              `json:"swPackagesFullPath,omitempty"`
	Type                  string              `json:"type,omitempty"`
	Uri                   utils.Nstring       `json:"uri,omitempty"`
	Uuid                  string              `json:"uuid,omitempty"`
	Version               string              `json:"version,omitempty"`
	XmlKeyName            string              `json:"xmlKeyName,omitempty"`
}

type FirmwareDriversList struct {
	Category    string            `json:"category,omitempty"`
	Count       int               `json:"count,omitempty"`
	Created     string            `json:"created,omitempty"`
	ETAG        string            `json:"eTag,omitempty"`
	Members     []FirmwareDrivers `json:"members,omitempty"`
	Modified    string            `json:"modified,omitempty"`
	NextPageURI utils.Nstring     `json:"nextPageUri,omitempty"`
	PrevPageURI utils.Nstring     `json:"prevPageUri,omitempty"`
	Start       int               `json:"start,omitempty"`
	Total       int               `json:"total,omitempty"`
	Type        string            `json:"type,omitempty"`
	Uri         utils.Nstring     `json:"uri,omitempty"`
}

type CustomServicePack struct {
	BaselineUri        string          `json:"baselineUri,omitempty"`
	CustomBaselineName string          `json:"customBaselineName,omitempty"`
	HotfixUris         []utils.Nstring `json:"hotfixUris,omitempty"`
	InitialScopeUris   []utils.Nstring `json:"initialScopeUris,omitempty"`
}

func (c *OVClient) GetFirmwareBaselineList(sort string, start string, count string) (FirmwareDriversList, error) {
	var (
		uri      = "/rest/firmware-drivers"
		firmware FirmwareDriversList
		q        = make(map[string]interface{})
	)

	if sort != "" {
		q["sort"] = sort
	}

	if start != "" {
		q["start"] = start
	}

	if count != "" {
		q["count"] = count
	}

	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	// Setup query
	if len(q) > 0 {
		c.SetQueryString(q)
	}
	data, err := c.RestAPICall(rest.GET, uri, nil)
	if err != nil {
		return firmware, err
	}

	log.Debugf("GetFirmwareBaseline %s", data)
	if err := json.Unmarshal(data, &firmware); err != nil {
		return firmware, err
	}
	return firmware, nil

}

func (c *OVClient) GetFirmwareBaselineById(id string) (FirmwareDrivers, error) {
	var (
		uri        = "/rest/firmware-drivers/" + id
		firmwareId FirmwareDrivers
	)
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())

	data, err := c.RestAPICall(rest.GET, uri, nil)
	if err != nil {
		return firmwareId, err
	}

	log.Debugf("GetFirmwareBaseline %s", data)
	if err := json.Unmarshal(data, &firmwareId); err != nil {
		return firmwareId, err
	}
	return firmwareId, nil
}

func (c *OVClient) GetFirmwareBaselineByNameandVersion(name string) (FirmwareDrivers, error) {
	var fwname, version string
	fwNameVersion := strings.SplitAfter(name, ",")
	if len(fwNameVersion) < 1 {
		return FirmwareDrivers{}, errors.New("firmware name not provided")
	}
	if len(fwNameVersion) == 2 {
		fwname, version = fwNameVersion[0], fwNameVersion[1]
	} else {
		fwname = fwNameVersion[0]
	}

	firmwareList, err := c.GetFirmwareBaselineList("", "", "")

	if firmwareList.Total > 0 {

		for i := range firmwareList.Members {
			if version != "" {
				if firmwareList.Members[i].Name != fwname && firmwareList.Members[i].Version != version {
					continue
				} else {

					return firmwareList.Members[i], err

				}

			} else {
				if firmwareList.Members[i].Name != fwname {
					continue
				} else {

					return firmwareList.Members[i], err

				}
			}

		}

	}
	return FirmwareDrivers{}, err
}

func (c *OVClient) CreateCustomServicePack(sp CustomServicePack, force string) error {
	var (
		uri = "/rest/firmware-drivers/"
		t   *Task
	)
	q := make(map[string]interface{})
	if force != "" {
		q["force"] = force
	}
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())

	if len(q) > 0 {
		c.SetQueryString(q)
	}
	t = t.NewProfileTask(c)
	t.ResetTask()
	log.Debugf("task -> %+v", t)

	data, err := c.RestAPICall(rest.POST, uri, sp)
	if err != nil {
		log.Errorf("Error submitting create firmware baseline request: %s", err)
		t.TaskIsDone = true
		return err
	}

	log.Debugf("CreateFirmwareBaseline")
	if err := json.Unmarshal(data, &t); err != nil {
		t.TaskIsDone = true
		log.Errorf("Error with task un-marshal: %s", err)
		return err
	}
	err = t.Wait()
	if err != nil {
		return err
	}
	return nil
}

func (c *OVClient) DeleteFirmwareBaseline(id string, force string) error {
	var (
		firmware FirmwareDrivers
		err      error
		t        *Task
		uri      = "/rest/firmware-drivers/" + id
	)

	firmware, err = c.GetFirmwareBaselineById(id)
	if err != nil {
		return err
	}
	if firmware.Name != "" {
		q := make(map[string]interface{})
		if force != "" {
			q["force"] = force
		}
		if len(q) > 0 {
			c.SetQueryString(q)
		}
		t = t.NewProfileTask(c)
		t.ResetTask()
		log.Debugf("REST : %s \n %+v\n", firmware.Uri, firmware)
		log.Debugf("task -> %+v", t)
		uri = firmware.Uri.String()
		if uri == "" {
			log.Warn("Unable to post delete, no uri found.")
			t.TaskIsDone = true
			return err
		}
		data, err := c.RestAPICall(rest.DELETE, uri, nil)
		if err != nil {
			log.Errorf("Error submitting delete firmware baseline request: %s", err)
			t.TaskIsDone = true
			return err
		}

		log.Debugf("Response firmware baseline network %s", data)
		if err := json.Unmarshal([]byte(data), &t); err != nil {
			t.TaskIsDone = true
			log.Errorf("Error with task un-marshal: %s", err)
			return err
		}
		err = t.Wait()
		if err != nil {
			return err
		}
		return nil
	} else {
		log.Infof("Firmware Baseline could not be found to delete, %s, skipping delete ...", id)
	}
	return nil
}
<commit_msg>Update firmware_drivers.go<commit_after>package ov

import (
	"encoding/json"
	"errors"
	"strings"

	"github.com/HewlettPackard/oneview-golang/rest"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/docker/machine/libmachine/log"
)

type parentBundle struct {
	ParentBundleName string `json:"parentBundleName,omitempty"`
	ReleaseDate      string `json:"releaseDate,omitempty"`
	Version          string `json:"version,omitempty"`
}

type HotFixes struct {
	HotfixName  string `json:"hotfixName,omitempty"`
	ReleaseDate string `json:"releaseDate,omitempty"`
	ResourceId  string `json:"resourceId,omitempty"`
}

type FWComponents struct {
	ComponentVersion string          `json:"componentVersion,omitempty"`
	FileName         string          `json:"fileName,omitempty"`
	Name             string          `json:"name,omitempty"`
	SwKeyNameList    []utils.Nstring `json:"swKeyNameList,omitempty"`
}

type FirmwareDrivers struct {
	BaselineShortName     string              `json:"baselineShortName,omitempty"`
	BundleSize            int                 `json:"bundleSize,omitempty"`
	BundleType            string              `json:"bundleType,omitempty"`
	Category              string              `json:"category,omitempty"`
	Created               string              `json:"created,omitempty"`
	Description           string              `json:"description,omitempty"`
	ETAG                  string              `json:"eTag,omitempty"`
	EsxiOsDriverMetaData  []utils.Nstring     `json:"esxiOsDriverMetaData,omitempty"`
	FwComponents          []FWComponents      `json:"fwComponents,omitempty"`
	Hotfixes              []HotFixes          `json:"hotfixes,omitempty"`
	HpsumVersion          string              `json:"hpsumVersion,omitempty"`
	IsoFileName           string              `json:"isoFileName,omitempty"`
	LastTaskUri           string              `json:"lastTaskUri,omitempty"`
	Locations             map[string]string   `json:"locations,omitempty"`
	Mirrorlist            map[string][]string `json:"mirrorlist,omitempty"`
	Modified              string              `json:"modified,omitempty"`
	Name                  string              `json:"name,omitempty"`
	ParentBundle          parentBundle        `json:"parentBundle,omitempty"`
	ReleaseDate           string              `json:"releaseDate,omitempty"`
	ResourceId            string              `json:"resourceId,omitempty"`
	ResourceState         string              `json:"resourceState,omitempty"`
	ScopesUri             string              `json:"scopesUri,omitempty"`
	SignatureFileName     string              `json:"signatureFileName,omitempty"`
	SignatureFileRequired bool                `json:"signatureFileRequired,omitempty"`
	State                 string              `json:"state,omitempty"`
	Status                string              `json:"status,omitempty"`
	SupportedLanguages    string              `json:"supportedLanguages,omitempty"`
	SupportedOSList       []utils.Nstring     `json:"supportedOSList,omitempty"`
	SwPackagesFullPath    string              `json:"swPackagesFullPath,omitempty"`
	Type                  string              `json:"type,omitempty"`
	Uri                   utils.Nstring       `json:"uri,omitempty"`
	Uuid                  string              `json:"uuid,omitempty"`
	Version               string              `json:"version,omitempty"`
	XmlKeyName            string              `json:"xmlKeyName,omitempty"`
}

type FirmwareDriversList struct {
	Category    string            `json:"category,omitempty"`
	Count       int               `json:"count,omitempty"`
	Created     string            `json:"created,omitempty"`
	ETAG        string            `json:"eTag,omitempty"`
	Members     []FirmwareDrivers `json:"members,omitempty"`
	Modified    string            `json:"modified,omitempty"`
	NextPageURI utils.Nstring     `json:"nextPageUri,omitempty"`
	PrevPageURI utils.Nstring     `json:"prevPageUri,omitempty"`
	Start       int               `json:"start,omitempty"`
	Total       int               `json:"total,omitempty"`
	Type        string            `json:"type,omitempty"`
	Uri         utils.Nstring     `json:"uri,omitempty"`
}

type CustomServicePack struct {
	BaselineUri        string          `json:"baselineUri,omitempty"`
	CustomBaselineName string          `json:"customBaselineName,omitempty"`
	HotfixUris         []utils.Nstring `json:"hotfixUris,omitempty"`
	InitialScopeUris   []utils.Nstring `json:"initialScopeUris,omitempty"`
}

func (c *OVClient) GetFirmwareBaselineList(sort string, start string, count string) (FirmwareDriversList, error) {
	var (
		uri      = "/rest/firmware-drivers"
		firmware FirmwareDriversList
		q        = make(map[string]interface{})
	)

	if sort != "" {
		q["sort"] = sort
	}

	if start != "" {
		q["start"] = start
	}

	if count != "" {
		q["count"] = count
	}

	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	// Setup query
	if len(q) > 0 {
		c.SetQueryString(q)
	}
	data, err := c.RestAPICall(rest.GET, uri, nil)
	if err != nil {
		return firmware, err
	}

	log.Debugf("GetFirmwareBaseline %s", data)
	if err := json.Unmarshal(data, &firmware); err != nil {
		return firmware, err
	}
	return firmware, nil

}

func (c *OVClient) GetFirmwareBaselineById(id string) (FirmwareDrivers, error) {
	var (
		uri        = "/rest/firmware-drivers/" + id
		firmwareId FirmwareDrivers
	)
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())

	data, err := c.RestAPICall(rest.GET, uri, nil)
	if err != nil {
		return firmwareId, err
	}

	log.Debugf("GetFirmwareBaseline %s", data)
	if err := json.Unmarshal(data, &firmwareId); err != nil {
		return firmwareId, err
	}
	return firmwareId, nil
}

func (c *OVClient) GetFirmwareBaselineByNameandVersion(name string) (FirmwareDrivers, error) {
	var fwname, version string
	fwNameVersion := strings.SplitAfter(name, ",")
	if len(fwNameVersion) < 1 {
		return FirmwareDrivers{}, errors.New("firmware name not provided")
	}
	if len(fwNameVersion) == 2 {
		fwname, version = strings.TrimSpace(fwNameVersion[0]), strings.TrimSpace(fwNameVersion[1])
	} else {
		fwname = fwNameVersion[0]
	}

	firmwareList, err := c.GetFirmwareBaselineList("", "", "")

	if firmwareList.Total > 0 {

		for i := range firmwareList.Members {
			if version != "" {
				if firmwareList.Members[i].Name != fwname && firmwareList.Members[i].Version != version {
					continue
				} else {

					return firmwareList.Members[i], err

				}

			} else {
				if firmwareList.Members[i].Name != fwname {
					continue
				} else {

					return firmwareList.Members[i], err

				}
			}

		}

	}
	return FirmwareDrivers{}, err
}

func (c *OVClient) CreateCustomServicePack(sp CustomServicePack, force string) error {
	var (
		uri = "/rest/firmware-drivers/"
		t   *Task
	)
	q := make(map[string]interface{})
	if force != "" {
		q["force"] = force
	}
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())

	if len(q) > 0 {
		c.SetQueryString(q)
	}
	t = t.NewProfileTask(c)
	t.ResetTask()
	log.Debugf("task -> %+v", t)

	data, err := c.RestAPICall(rest.POST, uri, sp)
	if err != nil {
		log.Errorf("Error submitting create firmware baseline request: %s", err)
		t.TaskIsDone = true
		return err
	}

	log.Debugf("CreateFirmwareBaseline")
	if err := json.Unmarshal(data, &t); err != nil {
		t.TaskIsDone = true
		log.Errorf("Error with task un-marshal: %s", err)
		return err
	}
	err = t.Wait()
	if err != nil {
		return err
	}
	return nil
}

func (c *OVClient) DeleteFirmwareBaseline(id string, force string) error {
	var (
		firmware FirmwareDrivers
		err      error
		t        *Task
		uri      = "/rest/firmware-drivers/" + id
	)

	firmware, err = c.GetFirmwareBaselineById(id)
	if err != nil {
		return err
	}
	if firmware.Name != "" {
		q := make(map[string]interface{})
		if force != "" {
			q["force"] = force
		}
		if len(q) > 0 {
			c.SetQueryString(q)
		}
		t = t.NewProfileTask(c)
		t.ResetTask()
		log.Debugf("REST : %s \n %+v\n", firmware.Uri, firmware)
		log.Debugf("task -> %+v", t)
		uri = firmware.Uri.String()
		if uri == "" {
			log.Warn("Unable to post delete, no uri found.")
			t.TaskIsDone = true
			return err
		}
		data, err := c.RestAPICall(rest.DELETE, uri, nil)
		if err != nil {
			log.Errorf("Error submitting delete firmware baseline request: %s", err)
			t.TaskIsDone = true
			return err
		}

		log.Debugf("Response firmware baseline network %s", data)
		if err := json.Unmarshal([]byte(data), &t); err != nil {
			t.TaskIsDone = true
			log.Errorf("Error with task un-marshal: %s", err)
			return err
		}
		err = t.Wait()
		if err != nil {
			return err
		}
		return nil
	} else {
		log.Infof("Firmware Baseline could not be found to delete, %s, skipping delete ...", id)
	}
	return nil
}
<|endoftext|>


<commit_before>package mcstore

import (
	"net/http"

	r "github.com/dancannon/gorethink"
	"github.com/emicklei/go-restful"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/dai"
	"github.com/materials-commons/mcstore/pkg/db/schema"
	"github.com/materials-commons/mcstore/pkg/domain"
)

type projectAccessFilterDAI struct {
	projects dai.Projects
	access   domain.Access
}

func newProjectAccessFilterDAI(session *r.Session) *projectAccessFilterDAI {
	files := dai.NewRFiles(session)
	users := dai.NewRUsers(session)
	projects := dai.NewRProjects(session)
	access := domain.NewAccess(projects, files, users)
	return &projectAccessFilterDAI{
		projects: projects,
		access:   access,
	}
}

func projectAccessFilter(request *restful.Request, response *restful.Response, chain *restful.FilterChain) {
	user := request.Attribute("user").(schema.User)
	session := request.Attribute("session").(*r.Session)

	var p struct {
		ProjectID string `json:"project_id"`
	}

	if err := request.ReadEntity(&p); err != nil {
		response.WriteErrorString(http.StatusNotAcceptable, "No project_id found")
		return
	}

	f := newProjectAccessFilterDAI(session)
	if project, err := f.getProjectValidatingAccess(p.ProjectID, user.ID); err != nil {
		response.WriteErrorString(http.StatusUnauthorized, "No access to project")
	} else {
		request.SetAttribute("project", *project)
		chain.ProcessFilter(request, response)
	}
}

// getProjectValidatingAccess retrieves the project with the given projectID. It checks that the
// given user has access to that project.
func (f *projectAccessFilterDAI) getProjectValidatingAccess(projectID, user string) (*schema.Project, error) {
	project, err := f.projects.ByID(projectID)
	switch {
	case err != nil:
		return nil, err
	case !f.access.AllowedByOwner(projectID, user):
		return nil, app.ErrNoAccess
	default:
		return project, nil
	}
}
<commit_msg>Use ws error mapping for status codes.<commit_after>package mcstore

import (
	"net/http"

	r "github.com/dancannon/gorethink"
	"github.com/emicklei/go-restful"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/dai"
	"github.com/materials-commons/mcstore/pkg/db/schema"
	"github.com/materials-commons/mcstore/pkg/domain"
	"github.com/materials-commons/mcstore/pkg/ws"
)

type projectAccessFilterDAI struct {
	projects dai.Projects
	access   domain.Access
}

func newProjectAccessFilterDAI(session *r.Session) *projectAccessFilterDAI {
	files := dai.NewRFiles(session)
	users := dai.NewRUsers(session)
	projects := dai.NewRProjects(session)
	access := domain.NewAccess(projects, files, users)
	return &projectAccessFilterDAI{
		projects: projects,
		access:   access,
	}
}

func projectAccessFilter(request *restful.Request, response *restful.Response, chain *restful.FilterChain) {
	user := request.Attribute("user").(schema.User)
	session := request.Attribute("session").(*r.Session)

	var p struct {
		ProjectID string `json:"project_id"`
	}

	if err := request.ReadEntity(&p); err != nil {
		response.WriteErrorString(http.StatusNotAcceptable, "No project_id found")
		return
	}

	f := newProjectAccessFilterDAI(session)
	if project, err := f.getProjectValidatingAccess(p.ProjectID, user.ID); err != nil {
		ws.WriteError(err, response)
	} else {
		request.SetAttribute("project", *project)
		chain.ProcessFilter(request, response)
	}
}

// getProjectValidatingAccess retrieves the project with the given projectID. It checks that the
// given user has access to that project.
func (f *projectAccessFilterDAI) getProjectValidatingAccess(projectID, user string) (*schema.Project, error) {
	project, err := f.projects.ByID(projectID)
	switch {
	case err != nil:
		return nil, err
	case !f.access.AllowedByOwner(projectID, user):
		return nil, app.ErrNoAccess
	default:
		return project, nil
	}
}
<|endoftext|>


<commit_before>package p2p

import (
	"net"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestNewNetAddress(t *testing.T) {
	assert, require := assert.New(t), require.New(t)

	tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
	require.Nil(err)
	addr := NewNetAddress(tcpAddr)

	assert.Equal("127.0.0.1:8080", addr.String())

	assert.NotPanics(func() {
		NewNetAddress(&net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8000})
	}, "Calling NewNetAddress with UDPAddr should not panic in testing")
}

func TestNewNetAddressString(t *testing.T) {
	assert := assert.New(t)

	tests := []struct {
		addr    string
		correct bool
	}{
		{"127.0.0.1:8080", true},
		// {"127.0.0:8080", false},
		{"a", false},
		{"127.0.0.1:a", false},
		{"a:8080", false},
		{"8082", false},
		{"127.0.0:8080000", false},
	}

	for _, t := range tests {
		addr, err := NewNetAddressString(t.addr)
		if t.correct {
			if assert.Nil(err, t.addr) {
				assert.Equal(t.addr, addr.String())
			}
		} else {
			assert.NotNil(err, t.addr)
		}
	}
}

func TestNewNetAddressStrings(t *testing.T) {
	assert, require := assert.New(t), require.New(t)
	addrs, err := NewNetAddressStrings([]string{"127.0.0.1:8080", "127.0.0.2:8080"})
	require.Nil(err)

	assert.Equal(2, len(addrs))
}

func TestNewNetAddressIPPort(t *testing.T) {
	assert := assert.New(t)
	addr := NewNetAddressIPPort(net.ParseIP("127.0.0.1"), 8080)

	assert.Equal("127.0.0.1:8080", addr.String())
}

func TestNetAddressProperties(t *testing.T) {
	assert, require := assert.New(t), require.New(t)

	// TODO add more test cases
	tests := []struct {
		addr     string
		valid    bool
		local    bool
		routable bool
	}{
		{"127.0.0.1:8080", true, true, false},
		{"ya.ru:80", true, false, true},
	}

	for _, t := range tests {
		addr, err := NewNetAddressString(t.addr)
		require.Nil(err)

		assert.Equal(t.valid, addr.Valid())
		assert.Equal(t.local, addr.Local())
		assert.Equal(t.routable, addr.Routable())
	}
}

func TestNetAddressReachabilityTo(t *testing.T) {
	assert, require := assert.New(t), require.New(t)

	// TODO add more test cases
	tests := []struct {
		addr         string
		other        string
		reachability int
	}{
		{"127.0.0.1:8080", "127.0.0.1:8081", 0},
		{"ya.ru:80", "127.0.0.1:8080", 1},
	}

	for _, t := range tests {
		addr, err := NewNetAddressString(t.addr)
		require.Nil(err)

		other, err := NewNetAddressString(t.other)
		require.Nil(err)

		assert.Equal(t.reachability, addr.ReachabilityTo(other))
	}
}
<commit_msg>fix test using uncommon names<commit_after>package p2p

import (
	"net"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestNewNetAddress(t *testing.T) {
	assert, require := assert.New(t), require.New(t)

	tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
	require.Nil(err)
	addr := NewNetAddress(tcpAddr)

	assert.Equal("127.0.0.1:8080", addr.String())

	assert.NotPanics(func() {
		NewNetAddress(&net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8000})
	}, "Calling NewNetAddress with UDPAddr should not panic in testing")
}

func TestNewNetAddressString(t *testing.T) {
	assert := assert.New(t)

	tests := []struct {
		addr    string
		correct bool
	}{
		{"127.0.0.1:8080", true},
		// {"127.0.0:8080", false},
		{"notahost", false},
		{"127.0.0.1:notapath", false},
		{"notahost:8080", false},
		{"8082", false},
		{"127.0.0:8080000", false},
	}

	for _, t := range tests {
		addr, err := NewNetAddressString(t.addr)
		if t.correct {
			if assert.Nil(err, t.addr) {
				assert.Equal(t.addr, addr.String())
			}
		} else {
			assert.NotNil(err, t.addr)
		}
	}
}

func TestNewNetAddressStrings(t *testing.T) {
	assert, require := assert.New(t), require.New(t)
	addrs, err := NewNetAddressStrings([]string{"127.0.0.1:8080", "127.0.0.2:8080"})
	require.Nil(err)

	assert.Equal(2, len(addrs))
}

func TestNewNetAddressIPPort(t *testing.T) {
	assert := assert.New(t)
	addr := NewNetAddressIPPort(net.ParseIP("127.0.0.1"), 8080)

	assert.Equal("127.0.0.1:8080", addr.String())
}

func TestNetAddressProperties(t *testing.T) {
	assert, require := assert.New(t), require.New(t)

	// TODO add more test cases
	tests := []struct {
		addr     string
		valid    bool
		local    bool
		routable bool
	}{
		{"127.0.0.1:8080", true, true, false},
		{"ya.ru:80", true, false, true},
	}

	for _, t := range tests {
		addr, err := NewNetAddressString(t.addr)
		require.Nil(err)

		assert.Equal(t.valid, addr.Valid())
		assert.Equal(t.local, addr.Local())
		assert.Equal(t.routable, addr.Routable())
	}
}

func TestNetAddressReachabilityTo(t *testing.T) {
	assert, require := assert.New(t), require.New(t)

	// TODO add more test cases
	tests := []struct {
		addr         string
		other        string
		reachability int
	}{
		{"127.0.0.1:8080", "127.0.0.1:8081", 0},
		{"ya.ru:80", "127.0.0.1:8080", 1},
	}

	for _, t := range tests {
		addr, err := NewNetAddressString(t.addr)
		require.Nil(err)

		other, err := NewNetAddressString(t.other)
		require.Nil(err)

		assert.Equal(t.reachability, addr.ReachabilityTo(other))
	}
}
<|endoftext|>


<commit_before>//go:build linux && cgo && !agent
// +build linux,cgo,!agent

package db

import (
	"github.com/lxc/lxd/shared/api"
)

// Code generation directives.
//
//go:generate -command mapper lxd-generate db mapper -t certificates.mapper.go
//go:generate mapper reset
//
//go:generate mapper stmt -p db -e certificate objects
//go:generate mapper stmt -p db -e certificate objects-by-Fingerprint
//go:generate mapper stmt -p db -e certificate projects-ref
//go:generate mapper stmt -p db -e certificate projects-ref-by-Fingerprint
//go:generate mapper stmt -p db -e certificate id
//go:generate mapper stmt -p db -e certificate create struct=Certificate
//go:generate mapper stmt -p db -e certificate create-projects-ref
//go:generate mapper stmt -p db -e certificate delete
//go:generate mapper stmt -p db -e certificate update struct=Certificate
//
//go:generate mapper method -p db -e certificate List
//go:generate mapper method -p db -e certificate Get
//go:generate mapper method -p db -e certificate ID struct=Certificate
//go:generate mapper method -p db -e certificate Exists struct=Certificate
//go:generate mapper method -p db -e certificate Create struct=Certificate
//go:generate mapper method -p db -e certificate ProjectsRef
//go:generate mapper method -p db -e certificate Delete
//go:generate mapper method -p db -e certificate Update struct=Certificate

// Certificate is here to pass the certificates content
// from the database around
type Certificate struct {
	ID          int
	Fingerprint string `db:"primary=yes&comparison=like"`
	Type        int
	Name        string
	Certificate string
	Restricted  bool
	Projects    []string
}

// ToAPI converts the database Certificate struct to an api.Certificate entry.
func (cert *Certificate) ToAPI() api.Certificate {
	resp := api.Certificate{}
	resp.Fingerprint = cert.Fingerprint
	resp.Certificate = cert.Certificate
	resp.Name = cert.Name
	resp.Restricted = cert.Restricted
	resp.Projects = cert.Projects
	if cert.Type == 1 {
		resp.Type = "client"
	} else {
		resp.Type = "unknown"
	}

	return resp
}

// UpdateCertificateProjects updates the list of projects on a certificate.
func (c *ClusterTx) UpdateCertificateProjects(id int, projects []string) error {
	// Clear all projects from the restrictions.
	q := "DELETE FROM certificates_projects WHERE certificate_id=?"
	_, err := c.tx.Exec(q, id)
	if err != nil {
		return err
	}

	// Add the new restrictions.
	for _, name := range projects {
		projID, err := c.GetProjectID(name)
		if err != nil {
			return err
		}

		q := "INSERT INTO certificates_projects (certificate_id, project_id) VALUES (?, ?)"
		_, err = c.tx.Exec(q, id, projID)
		if err != nil {
			return err
		}
	}

	return nil
}

// CertificateFilter can be used to filter results yielded by GetCertInfos
type CertificateFilter struct {
	Fingerprint string // Matched with LIKE
}

// GetCertificate gets an CertBaseInfo object from the database.
// The argument fingerprint will be queried with a LIKE query, means you can
// pass a shortform and will get the full fingerprint.
// There can never be more than one certificate with a given fingerprint, as it is
// enforced by a UNIQUE constraint in the schema.
func (c *Cluster) GetCertificate(fingerprint string) (*Certificate, error) {
	var err error
	var cert *Certificate
	err = c.Transaction(func(tx *ClusterTx) error {
		cert, err = tx.GetCertificate(fingerprint + "%")
		if err != nil {
			return err
		}

		cert, err = tx.GetCertificate(cert.Fingerprint)
		return err
	})
	if err != nil {
		return nil, err
	}

	return cert, nil
}

// CreateCertificate stores a CertInfo object in the db, it will ignore the ID
// field from the CertInfo.
func (c *Cluster) CreateCertificate(cert Certificate) (int64, error) {
	var id int64
	var err error
	err = c.Transaction(func(tx *ClusterTx) error {
		id, err = tx.CreateCertificate(cert)
		return err
	})
	return id, err
}

// DeleteCertificate deletes a certificate from the db.
func (c *Cluster) DeleteCertificate(fingerprint string) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		return tx.DeleteCertificate(fingerprint)
	})
	return err
}

// UpdateCertificate updates a certificate in the db.
func (c *Cluster) UpdateCertificate(fingerprint string, cert Certificate) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		return tx.UpdateCertificate(fingerprint, cert)
	})
	return err
}

// UpdateCertificateProjects updates the list of projects on a certificate.
func (c *Cluster) UpdateCertificateProjects(id int, projects []string) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		return tx.UpdateCertificateProjects(id, projects)
	})
	return err
}
<commit_msg>lxd/db/certificates: Adds CertificateAPITypeToDBType and ToAPIType functions<commit_after>//go:build linux && cgo && !agent
// +build linux,cgo,!agent

package db

import (
	"fmt"

	"github.com/lxc/lxd/shared/api"
)

// Code generation directives.
//
//go:generate -command mapper lxd-generate db mapper -t certificates.mapper.go
//go:generate mapper reset
//
//go:generate mapper stmt -p db -e certificate objects
//go:generate mapper stmt -p db -e certificate objects-by-Fingerprint
//go:generate mapper stmt -p db -e certificate projects-ref
//go:generate mapper stmt -p db -e certificate projects-ref-by-Fingerprint
//go:generate mapper stmt -p db -e certificate id
//go:generate mapper stmt -p db -e certificate create struct=Certificate
//go:generate mapper stmt -p db -e certificate create-projects-ref
//go:generate mapper stmt -p db -e certificate delete
//go:generate mapper stmt -p db -e certificate update struct=Certificate
//
//go:generate mapper method -p db -e certificate List
//go:generate mapper method -p db -e certificate Get
//go:generate mapper method -p db -e certificate ID struct=Certificate
//go:generate mapper method -p db -e certificate Exists struct=Certificate
//go:generate mapper method -p db -e certificate Create struct=Certificate
//go:generate mapper method -p db -e certificate ProjectsRef
//go:generate mapper method -p db -e certificate Delete
//go:generate mapper method -p db -e certificate Update struct=Certificate

// CertificateTypeClient indicates a client certificate type.
const CertificateTypeClient = 1

// CertificateTypeServer indicates a server certificate type.
const CertificateTypeServer = 2

// CertificateAPITypeToDBType converts an API type to the equivalent DB type.
func CertificateAPITypeToDBType(apiType string) (int, error) {
	switch apiType {
	case api.CertificateTypeClient:
		return CertificateTypeClient, nil
	case api.CertificateTypeServer:
		return CertificateTypeServer, nil
	}

	return -1, fmt.Errorf("Invalid certificate type")
}

// Certificate is here to pass the certificates content
// from the database around
type Certificate struct {
	ID          int
	Fingerprint string `db:"primary=yes&comparison=like"`
	Type        int
	Name        string
	Certificate string
	Restricted  bool
	Projects    []string
}

// ToAPIType returns the API equivalent type.
func (cert *Certificate) ToAPIType() string {
	switch cert.Type {
	case CertificateTypeClient:
		return api.CertificateTypeClient
	case CertificateTypeServer:
		return api.CertificateTypeServer
	}

	return api.CertificateTypeUnknown
}

// ToAPI converts the database Certificate struct to an api.Certificate entry.
func (cert *Certificate) ToAPI() api.Certificate {
	resp := api.Certificate{}
	resp.Fingerprint = cert.Fingerprint
	resp.Certificate = cert.Certificate
	resp.Name = cert.Name
	resp.Restricted = cert.Restricted
	resp.Projects = cert.Projects
	resp.Type = cert.ToAPIType()

	return resp
}

// UpdateCertificateProjects updates the list of projects on a certificate.
func (c *ClusterTx) UpdateCertificateProjects(id int, projects []string) error {
	// Clear all projects from the restrictions.
	q := "DELETE FROM certificates_projects WHERE certificate_id=?"
	_, err := c.tx.Exec(q, id)
	if err != nil {
		return err
	}

	// Add the new restrictions.
	for _, name := range projects {
		projID, err := c.GetProjectID(name)
		if err != nil {
			return err
		}

		q := "INSERT INTO certificates_projects (certificate_id, project_id) VALUES (?, ?)"
		_, err = c.tx.Exec(q, id, projID)
		if err != nil {
			return err
		}
	}

	return nil
}

// CertificateFilter can be used to filter results yielded by GetCertInfos
type CertificateFilter struct {
	Fingerprint string // Matched with LIKE
}

// GetCertificate gets an CertBaseInfo object from the database.
// The argument fingerprint will be queried with a LIKE query, means you can
// pass a shortform and will get the full fingerprint.
// There can never be more than one certificate with a given fingerprint, as it is
// enforced by a UNIQUE constraint in the schema.
func (c *Cluster) GetCertificate(fingerprint string) (*Certificate, error) {
	var err error
	var cert *Certificate
	err = c.Transaction(func(tx *ClusterTx) error {
		cert, err = tx.GetCertificate(fingerprint + "%")
		if err != nil {
			return err
		}

		cert, err = tx.GetCertificate(cert.Fingerprint)
		return err
	})
	if err != nil {
		return nil, err
	}

	return cert, nil
}

// CreateCertificate stores a CertInfo object in the db, it will ignore the ID
// field from the CertInfo.
func (c *Cluster) CreateCertificate(cert Certificate) (int64, error) {
	var id int64
	var err error
	err = c.Transaction(func(tx *ClusterTx) error {
		id, err = tx.CreateCertificate(cert)
		return err
	})
	return id, err
}

// DeleteCertificate deletes a certificate from the db.
func (c *Cluster) DeleteCertificate(fingerprint string) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		return tx.DeleteCertificate(fingerprint)
	})
	return err
}

// UpdateCertificate updates a certificate in the db.
func (c *Cluster) UpdateCertificate(fingerprint string, cert Certificate) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		return tx.UpdateCertificate(fingerprint, cert)
	})
	return err
}

// UpdateCertificateProjects updates the list of projects on a certificate.
func (c *Cluster) UpdateCertificateProjects(id int, projects []string) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		return tx.UpdateCertificateProjects(id, projects)
	})
	return err
}
<|endoftext|>


<commit_before>package storage

import (
	"os"

	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/shared"
)

// ContainerPath returns the directory of a container or snapshot.
func ContainerPath(name string, isSnapshot bool) string {
	if isSnapshot {
		return shared.VarPath("snapshots", name)
	}

	return shared.VarPath("containers", name)
}

// GetStoragePoolMountPoint returns the mountpoint of the given pool.
// {LXD_DIR}/storage-pools/<pool>
// Deprecated, use GetPoolMountPoint in storage/drivers package.
func GetStoragePoolMountPoint(poolName string) string {
	return shared.VarPath("storage-pools", poolName)
}

// GetContainerMountPoint returns the mountpoint of the given container.
// ${LXD_DIR}/storage-pools/<pool>/containers/[<project_name>_]<container_name>
func GetContainerMountPoint(projectName string, poolName string, containerName string) string {
	return shared.VarPath("storage-pools", poolName, "containers", project.Prefix(projectName, containerName))
}

// GetSnapshotMountPoint returns the mountpoint of the given container snapshot.
// ${LXD_DIR}/storage-pools/<pool>/containers-snapshots/<snapshot_name>
func GetSnapshotMountPoint(projectName, poolName string, snapshotName string) string {
	return shared.VarPath("storage-pools", poolName, "containers-snapshots", project.Prefix(projectName, snapshotName))
}

// GetImageMountPoint returns the mountpoint of the given image.
// ${LXD_DIR}/storage-pools/<pool>/images/<fingerprint>
func GetImageMountPoint(poolName string, fingerprint string) string {
	return shared.VarPath("storage-pools", poolName, "images", fingerprint)
}

// GetStoragePoolVolumeMountPoint returns the mountpoint of the given pool volume.
// ${LXD_DIR}/storage-pools/<pool>/custom/<storage_volume>
func GetStoragePoolVolumeMountPoint(poolName string, volumeName string) string {
	return shared.VarPath("storage-pools", poolName, "custom", volumeName)
}

// GetStoragePoolVolumeSnapshotMountPoint returns the mountpoint of the given pool volume snapshot.
// ${LXD_DIR}/storage-pools/<pool>/custom-snapshots/<custom volume name>/<snapshot name>
func GetStoragePoolVolumeSnapshotMountPoint(poolName string, snapshotName string) string {
	return shared.VarPath("storage-pools", poolName, "custom-snapshots", snapshotName)
}

// CreateContainerMountpoint creates the provided container mountpoint and symlink.
func CreateContainerMountpoint(mountPoint string, mountPointSymlink string, privileged bool) error {
	var mode os.FileMode
	if privileged {
		mode = 0700
	} else {
		mode = 0711
	}

	mntPointSymlinkExist := shared.PathExists(mountPointSymlink)
	mntPointSymlinkTargetExist := shared.PathExists(mountPoint)

	var err error
	if !mntPointSymlinkTargetExist {
		err = os.MkdirAll(mountPoint, 0711)
		if err != nil {
			return err
		}
	}

	err = os.Chmod(mountPoint, mode)
	if err != nil {
		return err
	}

	if !mntPointSymlinkExist {
		err := os.Symlink(mountPoint, mountPointSymlink)
		if err != nil {
			return err
		}
	}

	return nil
}

// CreateSnapshotMountpoint creates the provided container snapshot mountpoint
// and symlink.
func CreateSnapshotMountpoint(snapshotMountpoint string, snapshotsSymlinkTarget string, snapshotsSymlink string) error {
	snapshotMntPointExists := shared.PathExists(snapshotMountpoint)
	mntPointSymlinkExist := shared.PathExists(snapshotsSymlink)

	if !snapshotMntPointExists {
		err := os.MkdirAll(snapshotMountpoint, 0711)
		if err != nil {
			return err
		}
	}

	if !mntPointSymlinkExist {
		err := os.Symlink(snapshotsSymlinkTarget, snapshotsSymlink)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>lxd/storage: Updates deprecation notice<commit_after>package storage

import (
	"os"

	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/shared"
)

// ContainerPath returns the directory of a container or snapshot.
func ContainerPath(name string, isSnapshot bool) string {
	if isSnapshot {
		return shared.VarPath("snapshots", name)
	}

	return shared.VarPath("containers", name)
}

// GetStoragePoolMountPoint returns the mountpoint of the given pool.
// {LXD_DIR}/storage-pools/<pool>
// Deprecated, use GetPoolMountPath in storage/drivers package.
func GetStoragePoolMountPoint(poolName string) string {
	return shared.VarPath("storage-pools", poolName)
}

// GetContainerMountPoint returns the mountpoint of the given container.
// ${LXD_DIR}/storage-pools/<pool>/containers/[<project_name>_]<container_name>
func GetContainerMountPoint(projectName string, poolName string, containerName string) string {
	return shared.VarPath("storage-pools", poolName, "containers", project.Prefix(projectName, containerName))
}

// GetSnapshotMountPoint returns the mountpoint of the given container snapshot.
// ${LXD_DIR}/storage-pools/<pool>/containers-snapshots/<snapshot_name>
func GetSnapshotMountPoint(projectName, poolName string, snapshotName string) string {
	return shared.VarPath("storage-pools", poolName, "containers-snapshots", project.Prefix(projectName, snapshotName))
}

// GetImageMountPoint returns the mountpoint of the given image.
// ${LXD_DIR}/storage-pools/<pool>/images/<fingerprint>
func GetImageMountPoint(poolName string, fingerprint string) string {
	return shared.VarPath("storage-pools", poolName, "images", fingerprint)
}

// GetStoragePoolVolumeMountPoint returns the mountpoint of the given pool volume.
// ${LXD_DIR}/storage-pools/<pool>/custom/<storage_volume>
func GetStoragePoolVolumeMountPoint(poolName string, volumeName string) string {
	return shared.VarPath("storage-pools", poolName, "custom", volumeName)
}

// GetStoragePoolVolumeSnapshotMountPoint returns the mountpoint of the given pool volume snapshot.
// ${LXD_DIR}/storage-pools/<pool>/custom-snapshots/<custom volume name>/<snapshot name>
func GetStoragePoolVolumeSnapshotMountPoint(poolName string, snapshotName string) string {
	return shared.VarPath("storage-pools", poolName, "custom-snapshots", snapshotName)
}

// CreateContainerMountpoint creates the provided container mountpoint and symlink.
func CreateContainerMountpoint(mountPoint string, mountPointSymlink string, privileged bool) error {
	var mode os.FileMode
	if privileged {
		mode = 0700
	} else {
		mode = 0711
	}

	mntPointSymlinkExist := shared.PathExists(mountPointSymlink)
	mntPointSymlinkTargetExist := shared.PathExists(mountPoint)

	var err error
	if !mntPointSymlinkTargetExist {
		err = os.MkdirAll(mountPoint, 0711)
		if err != nil {
			return err
		}
	}

	err = os.Chmod(mountPoint, mode)
	if err != nil {
		return err
	}

	if !mntPointSymlinkExist {
		err := os.Symlink(mountPoint, mountPointSymlink)
		if err != nil {
			return err
		}
	}

	return nil
}

// CreateSnapshotMountpoint creates the provided container snapshot mountpoint
// and symlink.
func CreateSnapshotMountpoint(snapshotMountpoint string, snapshotsSymlinkTarget string, snapshotsSymlink string) error {
	snapshotMntPointExists := shared.PathExists(snapshotMountpoint)
	mntPointSymlinkExist := shared.PathExists(snapshotsSymlink)

	if !snapshotMntPointExists {
		err := os.MkdirAll(snapshotMountpoint, 0711)
		if err != nil {
			return err
		}
	}

	if !mntPointSymlinkExist {
		err := os.Symlink(snapshotsSymlinkTarget, snapshotsSymlink)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package al

import (
	"strings"

	"github.com/cpmech/gosl/io"
)

// Qmember defined a Queue member by wrapping interface{}
type Qmember interface{}

// Queue implements a FIFO queue, a sequence where the first inserted will be the first removed.
// Think of arriving people at the Bank or DMV...
type Queue struct {
	bfSize   int       // guessed buffer size
	front    int       // index in ring of member at front
	back     int       // index in ring of member at back
	nMembers int       // current number of members
	ring     []Qmember // ring holds all data in a "ring fashion"
	Debug    bool      // debug flag
}

// NewQueue returns a new object
func NewQueue(guessedBufferSize int) (o *Queue) {
	o = new(Queue)
	o.bfSize = guessedBufferSize
	o.front = -1 // indicates first ring
	o.back = -1  // indicates first ring
	return
}

// Front returns the member @ front of queue (close to the DMV window...) or nil if empty
func (o *Queue) Front() Qmember {
	if o.nMembers == 0 {
		return nil
	}
	return o.ring[o.front]
}

// Back returns the member @ back (unlucky guy/girl...) or nil if empty.
// It is always the last item in the data array
func (o *Queue) Back() Qmember {
	if o.nMembers == 0 {
		return nil
	}
	return o.ring[o.back]
}

// Nmembers returns the length of queue; i.e. the number of members
func (o *Queue) Nmembers() int {
	return o.nMembers
}

// In receives a new member arrival
// TODO: implement use of different grow rates
func (o *Queue) In(member Qmember) {

	// debug
	if o.Debug {
		io.Pfgrey("in  : before: F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		defer func() {
			io.Pfyel("in  : after : F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		}()
	}

	// first ring
	if o.front < 0 {
		o.ring = make([]Qmember, 1, o.bfSize+1)
		o.ring[0] = member
		o.front = 0
		o.back = o.front
		o.nMembers = 1
		return
	}

	// no space available ⇒ grow ring
	if o.nMembers+1 > len(o.ring) {
		o.grow()
	}

	// updates
	o.back = (o.back + 1) % len(o.ring) // cyclic increment
	o.ring[o.back] = member
	o.nMembers++
}

// Out removes the member @ front and returns a pointer to him/her
// TODO: implement memory recovery
func (o *Queue) Out() (member Qmember) {

	// debug
	if o.Debug {
		io.Pfpink("out : before: F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		defer func() {
			io.Pfpink("out : after : F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		}()
	}

	// no members
	if o.nMembers == 0 {
		return nil
	}

	// simply move Front pointer
	member = o.Front()
	o.front = (o.front + 1) % len(o.ring) // cyclic increment
	o.nMembers--
	return
}

// String returns the string representation of this object
func (o *Queue) String() (l string) {
	if o.nMembers == 0 {
		return "[]"
	}
	if o.back < o.front {
		left := o.ring[o.front:]
		right := o.ring[:o.back+1]
		return strings.Replace(io.Sf("%v", left)+io.Sf("%v", right), "][", " ", 1)
	}
	return io.Sf("%v", o.ring[o.front:o.back+1])
}

// auxiliary ////////////////////////////////////////////////////////////////////////////////////

// grow grows ring
func (o *Queue) grow() {

	// debug
	if o.Debug {
		io.Pfblue("grow: before: F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		defer func() {
			io.Pfblue("grow: after : F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		}()
	}

	// temporary array
	tmp := make([]Qmember, o.nMembers+1, o.bfSize+o.nMembers+1)

	// members are at different sides
	if o.back < o.front {
		left := o.ring[o.front:]
		right := o.ring[:o.back+1]
		copy(tmp, left)
		copy(tmp[len(left):], right)

		// members are a the same side
	} else {
		copy(tmp, o.ring[o.front:o.back+1])
	}

	// set indices and replace ring. Note: nMembers remains unchanged
	o.front = 0
	o.back = o.nMembers - 1
	o.ring = tmp
}
<commit_msg>Fix typo<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package al

import (
	"strings"

	"github.com/cpmech/gosl/io"
)

// Qmember defines a Queue member by wrapping interface{}
type Qmember interface{}

// Queue implements a FIFO queue, a sequence where the first inserted will be the first removed.
// Think of arriving people at the Bank or DMV...
type Queue struct {
	bfSize   int       // guessed buffer size
	front    int       // index in ring of member at front
	back     int       // index in ring of member at back
	nMembers int       // current number of members
	ring     []Qmember // ring holds all data in a "ring fashion"
	Debug    bool      // debug flag
}

// NewQueue returns a new object
func NewQueue(guessedBufferSize int) (o *Queue) {
	o = new(Queue)
	o.bfSize = guessedBufferSize
	o.front = -1 // indicates first ring
	o.back = -1  // indicates first ring
	return
}

// Front returns the member @ front of queue (close to the DMV window...) or nil if empty
func (o *Queue) Front() Qmember {
	if o.nMembers == 0 {
		return nil
	}
	return o.ring[o.front]
}

// Back returns the member @ back (unlucky guy/girl...) or nil if empty.
// It is always the last item in the data array
func (o *Queue) Back() Qmember {
	if o.nMembers == 0 {
		return nil
	}
	return o.ring[o.back]
}

// Nmembers returns the length of queue; i.e. the number of members
func (o *Queue) Nmembers() int {
	return o.nMembers
}

// In receives a new member arrival
// TODO: implement use of different grow rates
func (o *Queue) In(member Qmember) {

	// debug
	if o.Debug {
		io.Pfgrey("in  : before: F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		defer func() {
			io.Pfyel("in  : after : F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		}()
	}

	// first ring
	if o.front < 0 {
		o.ring = make([]Qmember, 1, o.bfSize+1)
		o.ring[0] = member
		o.front = 0
		o.back = o.front
		o.nMembers = 1
		return
	}

	// no space available ⇒ grow ring
	if o.nMembers+1 > len(o.ring) {
		o.grow()
	}

	// updates
	o.back = (o.back + 1) % len(o.ring) // cyclic increment
	o.ring[o.back] = member
	o.nMembers++
}

// Out removes the member @ front and returns a pointer to him/her
// TODO: implement memory recovery
func (o *Queue) Out() (member Qmember) {

	// debug
	if o.Debug {
		io.Pfpink("out : before: F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		defer func() {
			io.Pfpink("out : after : F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		}()
	}

	// no members
	if o.nMembers == 0 {
		return nil
	}

	// simply move Front pointer
	member = o.Front()
	o.front = (o.front + 1) % len(o.ring) // cyclic increment
	o.nMembers--
	return
}

// String returns the string representation of this object
func (o *Queue) String() (l string) {
	if o.nMembers == 0 {
		return "[]"
	}
	if o.back < o.front {
		left := o.ring[o.front:]
		right := o.ring[:o.back+1]
		return strings.Replace(io.Sf("%v", left)+io.Sf("%v", right), "][", " ", 1)
	}
	return io.Sf("%v", o.ring[o.front:o.back+1])
}

// auxiliary ////////////////////////////////////////////////////////////////////////////////////

// grow grows ring
func (o *Queue) grow() {

	// debug
	if o.Debug {
		io.Pfblue("grow: before: F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		defer func() {
			io.Pfblue("grow: after : F=%d B=%d N=%d ring=%v\n", o.front, o.back, o.nMembers, o.ring)
		}()
	}

	// temporary array
	tmp := make([]Qmember, o.nMembers+1, o.bfSize+o.nMembers+1)

	// members are at different sides
	if o.back < o.front {
		left := o.ring[o.front:]
		right := o.ring[:o.back+1]
		copy(tmp, left)
		copy(tmp[len(left):], right)

		// members are a the same side
	} else {
		copy(tmp, o.ring[o.front:o.back+1])
	}

	// set indices and replace ring. Note: nMembers remains unchanged
	o.front = 0
	o.back = o.nMembers - 1
	o.ring = tmp
}
<|endoftext|>