<commit_before>package postailer

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"syscall"
)

type Postailer struct {
	FilePath string
	PosFile  string

	pos     *position
	oldfile bool
	rcloser io.ReadCloser
}

type position struct {
	Inode uint  `json:"inode"`
	Pos   int64 `json:"pos"`
}

func Open(filepath, posfile string) (*Postailer, error) {
	pt := &Postailer{
		FilePath: filepath,
		PosFile:  posfile,
	}
	err := pt.Open()
	if err != nil {
		return nil, err
	}
	return pt, nil
}

func loadPos(fname string) (*position, error) {
	pos := &position{}
	_, err := os.Stat(fname)
	if err == nil { // posfile exists
		b, err := ioutil.ReadFile(fname)
		if err == nil {
			err := json.Unmarshal(b, pos)
			return pos, err
		}
	}
	return pos, nil
}

func (pt *Postailer) Open() error {
	// XXX error handling
	pt.pos, _ = loadPos(pt.PosFile)
	fi, err := os.Stat(pt.FilePath)
	// XXX may be missing the file for a moment while rotating...
	if err != nil {
		return err
	}
	inode := detectInode(fi)
	if pt.pos.Inode > 0 && inode != pt.pos.Inode {
		if oldFile, err := findFileByInode(pt.pos.Inode, filepath.Dir(pt.FilePath)); err == nil {
			oldf, err := os.Open(oldFile)
			if err != nil {
				return err
			}
			oldfi, _ := oldf.Stat()
			if oldfi.Size() > pt.pos.Pos {
				oldf.Seek(pt.pos.Pos, 0)
				pt.oldfile = true
				pt.rcloser = oldf
				return nil
			}
		}
		// XXX error handling?
		pt.pos.Pos = 0
	}
	pt.pos.Inode = inode
	f, err := os.Open(pt.FilePath)
	if err != nil {
		return err
	}
	if pt.pos.Pos < fi.Size() {
		f.Seek(pt.pos.Pos, 0)
	} else {
		pt.pos.Pos = 0
	}
	pt.rcloser = f
	return nil
}

func (pt *Postailer) Read(p []byte) (int, error) {
	n, err := pt.rcloser.Read(p)
	pt.pos.Pos += int64(n)
	if !(pt.oldfile && ((err == nil && n < len(p)) || (err == io.EOF))) {
		return n, err
	}
	pt.rcloser.Close()
	f, err := os.Open(pt.FilePath)
	if err != nil {
		return n, err
	}
	fi, err := f.Stat()
	if err != nil {
		return n, err
	}
	pt.pos = &position{Inode: detectInode(fi)}
	pt.oldfile = false
	pt.rcloser = f

	if n == len(p) {
		return n, err
	}
	buf := make([]byte, len(p)-n)
	nn, err := pt.rcloser.Read(buf)
	for i := 0; i < nn; i++ {
		p[n+i] = buf[i]
	}
	pt.pos.Pos = int64(nn)
	return n + nn, err
}

func (pt *Postailer) Close() error {
	defer pt.rcloser.Close()
	return savePos(pt.PosFile, pt.pos)
}

func savePos(posfile string, pos *position) error {
	b, _ := json.Marshal(pos)
	return writeFileAtomically(posfile, b)
}

// XXX may be dirty, should be return error?
func detectInode(fi os.FileInfo) uint {
	if stat, ok := fi.Sys().(*syscall.Stat_t); ok {
		return uint(stat.Ino)
	}
	return 0
}

func findFileByInode(inode uint, dir string) (string, error) {
	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		return "", err
	}
	for _, fi := range fis {
		if detectInode(fi) == inode {
			return filepath.Join(dir, fi.Name()), nil
		}
	}
	// XXX rough error
	return "", fmt.Errorf("file not found")
}

func writeFileAtomically(f string, contents []byte) error {
	// MUST be located on same disk partition
	tmpf, err := ioutil.TempFile(filepath.Dir(f), "tmp")
	if err != nil {
		return err
	}
	// os.Remove here works successfully when tmpf.Write fails or os.Rename fails.
	// In successful case, os.Remove fails because the temporary file is already renamed.
	defer os.Remove(tmpf.Name())
	_, err = tmpf.Write(contents)
	tmpf.Close() // should be called before rename
	if err != nil {
		return err
	}
	return os.Rename(tmpf.Name(), f)
}
<commit_msg>adjust interface<commit_after>package postailer

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"syscall"
)

type Postailer struct {
	filePath string
	posFile  string

	pos     *position
	oldfile bool
	rcloser io.ReadCloser
}

type position struct {
	Inode uint  `json:"inode"`
	Pos   int64 `json:"pos"`
}

func Open(filepath, posfile string) (*Postailer, error) {
	pt := &Postailer{
		filePath: filepath,
		posFile:  posfile,
	}
	err := pt.open()
	if err != nil {
		return nil, err
	}
	return pt, nil
}

func loadPos(fname string) (*position, error) {
	pos := &position{}
	_, err := os.Stat(fname)
	if err == nil { // posfile exists
		b, err := ioutil.ReadFile(fname)
		if err == nil {
			err := json.Unmarshal(b, pos)
			return pos, err
		}
	}
	return pos, nil
}

func (pt *Postailer) open() error {
	// XXX error handling
	pt.pos, _ = loadPos(pt.posFile)
	fi, err := os.Stat(pt.filePath)
	// XXX may be missing the file for a moment while rotating...
	if err != nil {
		return err
	}
	inode := detectInode(fi)
	if pt.pos.Inode > 0 && inode != pt.pos.Inode {
		if oldFile, err := findFileByInode(pt.pos.Inode, filepath.Dir(pt.filePath)); err == nil {
			oldf, err := os.Open(oldFile)
			if err != nil {
				return err
			}
			oldfi, _ := oldf.Stat()
			if oldfi.Size() > pt.pos.Pos {
				oldf.Seek(pt.pos.Pos, 0)
				pt.oldfile = true
				pt.rcloser = oldf
				return nil
			}
		}
		// XXX error handling?
		pt.pos.Pos = 0
	}
	pt.pos.Inode = inode
	f, err := os.Open(pt.filePath)
	if err != nil {
		return err
	}
	if pt.pos.Pos < fi.Size() {
		f.Seek(pt.pos.Pos, 0)
	} else {
		pt.pos.Pos = 0
	}
	pt.rcloser = f
	return nil
}

func (pt *Postailer) Read(p []byte) (int, error) {
	n, err := pt.rcloser.Read(p)
	pt.pos.Pos += int64(n)
	if !(pt.oldfile && ((err == nil && n < len(p)) || (err == io.EOF))) {
		return n, err
	}
	pt.rcloser.Close()
	f, err := os.Open(pt.filePath)
	if err != nil {
		return n, err
	}
	fi, err := f.Stat()
	if err != nil {
		return n, err
	}
	pt.pos = &position{Inode: detectInode(fi)}
	pt.oldfile = false
	pt.rcloser = f

	if n == len(p) {
		return n, err
	}
	buf := make([]byte, len(p)-n)
	nn, err := pt.rcloser.Read(buf)
	for i := 0; i < nn; i++ {
		p[n+i] = buf[i]
	}
	pt.pos.Pos = int64(nn)
	return n + nn, err
}

func (pt *Postailer) Close() error {
	defer pt.rcloser.Close()
	return savePos(pt.posFile, pt.pos)
}

func savePos(posfile string, pos *position) error {
	b, _ := json.Marshal(pos)
	return writeFileAtomically(posfile, b)
}

// XXX may be dirty, should be return error?
func detectInode(fi os.FileInfo) uint {
	if stat, ok := fi.Sys().(*syscall.Stat_t); ok {
		return uint(stat.Ino)
	}
	return 0
}

func findFileByInode(inode uint, dir string) (string, error) {
	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		return "", err
	}
	for _, fi := range fis {
		if detectInode(fi) == inode {
			return filepath.Join(dir, fi.Name()), nil
		}
	}
	// XXX rough error
	return "", fmt.Errorf("file not found")
}

func writeFileAtomically(f string, contents []byte) error {
	// MUST be located on same disk partition
	tmpf, err := ioutil.TempFile(filepath.Dir(f), "tmp")
	if err != nil {
		return err
	}
	// os.Remove here works successfully when tmpf.Write fails or os.Rename fails.
	// In successful case, os.Remove fails because the temporary file is already renamed.
	defer os.Remove(tmpf.Name())
	_, err = tmpf.Write(contents)
	tmpf.Close() // should be called before rename
	if err != nil {
		return err
	}
	return os.Rename(tmpf.Name(), f)
}
<|endoftext|>


<commit_before>// Copyright 2020 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package serve

import (
	"fmt"
	"os"
	"os/signal"
	"time"

	"github.com/cilium/cilium/pkg/hubble/relay"
	"github.com/cilium/cilium/pkg/hubble/relay/relayoption"
	"golang.org/x/sys/unix"

	"github.com/spf13/cobra"
)

var flag struct {
	debug         bool
	dialTimeout   time.Duration
	listenAddress string
	peerService   string
	retryTimeout  time.Duration
}

// New creates a new serve command.
func New() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "serve",
		Short: "Run the gRPC proxy server",
		Long:  `Run the gRPC proxy server.`,
		RunE: func(_ *cobra.Command, _ []string) error {
			return runServe()
		},
	}
	cmd.Flags().BoolVarP(&flag.debug, "debug", "D", false, "Run in debug mode")
	cmd.Flags().DurationVar(&flag.dialTimeout, "dial-timeout", relayoption.Default.DialTimeout, "Dial timeout when connecting to hubble peers")
	cmd.Flags().DurationVar(&flag.retryTimeout, "retry-timeout", relayoption.Default.RetryTimeout, "Time to wait before attempting to reconnect to a hubble peer when the connection is lost")
	cmd.Flags().StringVar(&flag.listenAddress, "listen-address", relayoption.Default.ListenAddress, "Address on which to listen")
	cmd.Flags().StringVar(&flag.peerService, "peer-service", relayoption.Default.HubbleTarget, "Address of the server that implements the peer gRPC service")
	return cmd
}

func runServe() error {
	opts := []relayoption.Option{
		relayoption.WithDialTimeout(flag.dialTimeout),
		relayoption.WithHubbleTarget(flag.peerService),
		relayoption.WithListenAddress(flag.listenAddress),
		relayoption.WithRetryTimeout(flag.retryTimeout),
	}
	if flag.debug {
		opts = append(opts, relayoption.WithDebug())
	}
	srv, err := relay.NewServer(opts...)
	if err != nil {
		return fmt.Errorf("cannot create hubble-relay server: %v", err)
	}
	go func() {
		sigs := make(chan os.Signal, 1)
		signal.Notify(sigs, unix.SIGINT, unix.SIGTERM)
		<-sigs
		srv.Stop()
	}()
	return srv.Serve()
}
<commit_msg>hubble-relay: make flags from serve command non-global<commit_after>// Copyright 2020 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package serve

import (
	"fmt"
	"os"
	"os/signal"
	"time"

	"github.com/cilium/cilium/pkg/hubble/relay"
	"github.com/cilium/cilium/pkg/hubble/relay/relayoption"
	"golang.org/x/sys/unix"

	"github.com/spf13/cobra"
)

type flags struct {
	debug         bool
	dialTimeout   time.Duration
	listenAddress string
	peerService   string
	retryTimeout  time.Duration
}

// New creates a new serve command.
func New() *cobra.Command {
	var f flags
	cmd := &cobra.Command{
		Use:   "serve",
		Short: "Run the gRPC proxy server",
		Long:  `Run the gRPC proxy server.`,
		RunE: func(_ *cobra.Command, _ []string) error {
			return runServe(f)
		},
	}
	cmd.Flags().BoolVarP(
		&f.debug, "debug", "D", false, "Run in debug mode",
	)
	cmd.Flags().DurationVar(
		&f.dialTimeout, "dial-timeout",
		relayoption.Default.DialTimeout,
		"Dial timeout when connecting to hubble peers")
	cmd.Flags().DurationVar(
		&f.retryTimeout, "retry-timeout",
		relayoption.Default.RetryTimeout,
		"Time to wait before attempting to reconnect to a hubble peer when the connection is lost")
	cmd.Flags().StringVar(
		&f.listenAddress, "listen-address",
		relayoption.Default.ListenAddress,
		"Address on which to listen")
	cmd.Flags().StringVar(
		&f.peerService, "peer-service",
		relayoption.Default.HubbleTarget,
		"Address of the server that implements the peer gRPC service")
	return cmd
}

func runServe(f flags) error {
	opts := []relayoption.Option{
		relayoption.WithDialTimeout(f.dialTimeout),
		relayoption.WithHubbleTarget(f.peerService),
		relayoption.WithListenAddress(f.listenAddress),
		relayoption.WithRetryTimeout(f.retryTimeout),
	}
	if f.debug {
		opts = append(opts, relayoption.WithDebug())
	}
	srv, err := relay.NewServer(opts...)
	if err != nil {
		return fmt.Errorf("cannot create hubble-relay server: %v", err)
	}
	go func() {
		sigs := make(chan os.Signal, 1)
		signal.Notify(sigs, unix.SIGINT, unix.SIGTERM)
		<-sigs
		srv.Stop()
	}()
	return srv.Serve()
}
<|endoftext|>


<commit_before>package projects

import "time"

const (
	// GetSbomEndpoint is the current endpoint for retrieving an organization's SBOMs
	GetSbomEndpoint = "v1/project/getSBOM"
	// GetSbomsEndpoint is the current endpoint for retrieving an organization's SBOMs
	GetSbomsEndpoint = "v1/project/getSBOMs"
)

// SBOMMetadata contains various piece of metadata about a particular SBOM
type SBOMMetadata struct {
	EntryCount                  int `json:"entry_count"`
	ResolvedEntryCount          int `json:"resolved_entry_count"`
	PartiallyResolvedEntryCount int `json:"partially_resolved_entry_count"`
	UnresolvedEntryCount        int `json:"unresolved_entry_count"`
}

// SBOM represents a software list containing zero or more SBOMEntry objects.
type SBOM struct {
	ID               string       `json:"id"`
	Name             string       `json:"sbom_name"`
	Version          string       `json:"sbom_version"`
	Supplier         string       `json:"supplier_name"`
	ContactName      string       `json:"contact_name"`
	ContactEmail     string       `json:"contact_email"`
	MonitorFrequency string       `json:"monitor_frequency"`
	SbomStatus       string       `json:"sbom_status"`
	CreatedAt        time.Time    `json:"created_at"`
	UpdatedAt        time.Time    `json:"updated_at"`
	DeletedAt        *time.Time   `json:"deleted_at"`
	EntryCount       int          `json:"entry_count"`
	Metadata         SBOMMetadata `json:"metadata"`
	Entries          []SBOMEntry  `json:"entries"`
	TeamID           string       `json:"team_id"`
	OrgID            string       `json:"org_id"`
	RulesetID        string       `json:"ruleset_id"`
}
<commit_msg>[DEV-1126] Add stats structs<commit_after>package projects

import "time"

const (
	// GetSbomEndpoint is the current endpoint for retrieving an organization's SBOMs
	GetSbomEndpoint = "v1/project/getSBOM"
	// GetSbomsEndpoint is the current endpoint for retrieving an organization's SBOMs
	GetSbomsEndpoint = "v1/project/getSBOMs"
)

// SBOMMetadata contains various piece of metadata about a particular SBOM
type SBOMMetadata struct {
	EntryCount                  int `json:"entry_count"`
	ResolvedEntryCount          int `json:"resolved_entry_count"`
	PartiallyResolvedEntryCount int `json:"partially_resolved_entry_count"`
	UnresolvedEntryCount        int `json:"unresolved_entry_count"`
}

// SBOM represents a software list containing zero or more SBOMEntry objects.
type SBOM struct {
	ID               string       `json:"id"`
	Name             string       `json:"name"`
	Version          string       `json:"version"`
	Supplier         string       `json:"supplier"`
	ContactName      string       `json:"contact_name"`
	ContactEmail     string       `json:"contact_email"`
	MonitorFrequency string       `json:"monitor_frequency"`
	SbomStatus       string       `json:"status"`
	CreatedAt        time.Time    `json:"created_at"`
	UpdatedAt        time.Time    `json:"updated_at"`
	DeletedAt        *time.Time   `json:"deleted_at"`
	EntryCount       int          `json:"entry_count"`
	Metadata         SBOMMetadata `json:"metadata"`
	Stats            Stats        `json:"stats"`
	Entries          []SBOMEntry  `json:"entries"`
	TeamID           string       `json:"team_id"`
	OrgID            string       `json:"org_id"`
	RulesetID        string       `json:"ruleset_id"`
}

type Risk struct {
	Score  int `json:"score"`
	Scopes struct {
		Ecosystem   int `json:"ecosystem"`
		SupplyChain int `json:"supply_chain"`
		Software    int `json:"software"`
	} `json:"scopes"`
}

type Compliance struct {
	Passing int `json:"passing"`
	Failing int `json:"failing"`
}

type Resolution struct {
	Resolved          int `json:"resolved"`
	PartiallyResolved int `json:"partially_resolved"`
	Unresolved        int `json:"unresolved"`
}

type Stats struct {
	Risk       Risk       `json:"risk"`
	Compliance Compliance `json:"compliance"`
	Resolution Resolution `json:"resolution"`
}

type SoftwareInventorySummary struct {
	ID            string `json:"id"`
	Organization  Stats  `json:"organization"`
	SoftwareLists []SBOM `json:"software_lists"`
}
<|endoftext|>


<commit_before>package pkcs7

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io/ioutil"
	"os"
	"testing"
)

//
// Test key and certificate are in testdata/ and were generated with OpenSSL as follows:
//
//   openssl genrsa -out test.key 2048
//   openssl req -nodes -new -key test.key -out test.csr -subj "/C=CA/ST=Ontario/L=Toronto/O=Stefan Arentz/OU=Golang Hacks/CN=example.com"
//   openssl x509 -req -days 1825 -in test.csr -signkey test.key -out test.crt
//

func loadPKCS1PrivateKey(path string) (*rsa.PrivateKey, error) {
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(bytes)
	if block == nil {
		return nil, errors.New("Invalid key; no PEM data found")
	}
	if block.Type != "RSA PRIVATE KEY" {
		return nil, errors.New("Invalid key; no RSA PRIVATE KEY block")
	}
	return x509.ParsePKCS1PrivateKey(block.Bytes)
}

func loadCertificate(path string) (*x509.Certificate, error) {
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(bytes)
	if block == nil {
		return nil, errors.New("Invalid certificate; no PEM data found")
	}
	if block.Type != "CERTIFICATE" {
		return nil, errors.New("Invalid certificate; no CERTIFICATE block")
	}
	return x509.ParseCertificate(block.Bytes)
}

func Test_Sign(t *testing.T) {
	key, err := loadPKCS1PrivateKey("testdata/test.key")
	if err != nil {
		panic(err)
	}

	cert, err := loadCertificate("testdata/test.crt")
	if err != nil {
		panic(err)
	}

	f, err := os.Open("testdata/test.txt")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	signature, err := Sign(f, cert, key)
	if err != nil {
		t.Error("Cannot Sign:", err)
	}

	if len(signature) == 0 {
		t.Error("Signature is zero length")
	}
}
<commit_msg>Add OpenSSL verification as unit test<commit_after>package pkcs7

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io/ioutil"
	"os"
	"os/exec"
	"testing"
)

//
// Test key and certificate are in testdata/ and were generated with OpenSSL as follows:
//
//   openssl genrsa -out test.key 2048
//   openssl req -nodes -new -key test.key -out test.csr -subj "/C=CA/ST=Ontario/L=Toronto/O=Stefan Arentz/OU=Golang Hacks/CN=example.com"
//   openssl x509 -req -days 1825 -in test.csr -signkey test.key -out test.crt
//

func loadPKCS1PrivateKey(path string) (*rsa.PrivateKey, error) {
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(bytes)
	if block == nil {
		return nil, errors.New("Invalid key; no PEM data found")
	}
	if block.Type != "RSA PRIVATE KEY" {
		return nil, errors.New("Invalid key; no RSA PRIVATE KEY block")
	}
	return x509.ParsePKCS1PrivateKey(block.Bytes)
}

func loadCertificate(path string) (*x509.Certificate, error) {
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(bytes)
	if block == nil {
		return nil, errors.New("Invalid certificate; no PEM data found")
	}
	if block.Type != "CERTIFICATE" {
		return nil, errors.New("Invalid certificate; no CERTIFICATE block")
	}
	return x509.ParseCertificate(block.Bytes)
}

func Test_Sign(t *testing.T) {
	key, err := loadPKCS1PrivateKey("testdata/test.key")
	if err != nil {
		panic(err)
	}

	cert, err := loadCertificate("testdata/test.crt")
	if err != nil {
		panic(err)
	}

	f, err := os.Open("testdata/test.txt")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	signature, err := Sign(f, cert, key)
	if err != nil {
		t.Error("Cannot Sign:", err)
	}

	if len(signature) == 0 {
		t.Error("Signature is zero length")
	}

	// Verify the signature with OpenSSL

	if err := ioutil.WriteFile("/tmp/signature", signature, 0600); err != nil {
		panic(err)
	}

	cmd := exec.Command("openssl", "smime", "-verify", "-in", "/tmp/signature", "-content", "testdata/test.txt", "-inform", "der", "-noverify")
	if err := cmd.Start(); err != nil {
		t.Error("Failed to start openssl:", err)
	}

	if err := cmd.Wait(); err != nil {
		t.Error("Failed to verify signature with openssl:", err)
	}
}
<|endoftext|>


<commit_before>package apps

import (
	"io"
	"net/http"
	"os"
	"path"
	"time"

	"github.com/cozy/swift"
	"github.com/spf13/afero"
)

// FileServer interface defines a way to access and serve the application's
// data files.
type FileServer interface {
	Open(slug, version, file string) (io.ReadCloser, error)
	ServeFileContent(w http.ResponseWriter, req *http.Request,
		slug, version, file string) error
}

type swiftServer struct {
	c         *swift.Connection
	container string
}

type aferoServer struct {
	mkPath func(slug, version, file string) string
	fs     afero.Fs
}

// NewSwiftFileServer returns provides the apps.FileServer implementation
// using the swift backend as file server.
func NewSwiftFileServer(conn *swift.Connection, appsType AppType) FileServer {
	return &swiftServer{
		c:         conn,
		container: containerName(appsType),
	}
}

func (s *swiftServer) Open(slug, version, file string) (io.ReadCloser, error) {
	objName := defaultMakePath(slug, version, file)
	f, _, err := s.c.ObjectOpen(s.container, objName, false, nil)
	if err != nil {
		return nil, wrapSwiftErr(err)
	}
	return f, nil
}

func (s *swiftServer) ServeFileContent(w http.ResponseWriter, req *http.Request, slug, version, file string) error {
	objName := defaultMakePath(slug, version, file)
	f, o, err := s.c.ObjectOpen(s.container, objName, false, nil)
	if err != nil {
		return wrapSwiftErr(err)
	}
	defer f.Close()
	lastModified, _ := time.Parse(http.TimeFormat, o["Last-Modified"])
	w.Header().Set("Etag", o["Etag"])
	http.ServeContent(w, req, objName, lastModified, f)
	return nil
}

// NewAferoFileServer returns a simple wrapper of the afero.Fs interface that
// provides the apps.FileServer interface.
//
// You can provide a makePath method to define how the file name should be
// created from the application's slug, version and file name. If not provided,
// the standard VFS concatenation (starting with vfs.WebappsDirName) is used.
func NewAferoFileServer(fs afero.Fs, makePath func(slug, version, file string) string) FileServer {
	if makePath == nil {
		makePath = defaultMakePath
	}
	return &aferoServer{
		mkPath: makePath,
		fs:     fs,
	}
}

func (s *aferoServer) Open(slug, version, file string) (io.ReadCloser, error) {
	filepath := s.mkPath(slug, version, file)
	f, err := s.open(filepath)
	if os.IsNotExist(err) {
		return s.open(retroCompatMakePath(slug, version, file))
	}
	return f, err
}
func (s *aferoServer) open(filepath string) (io.ReadCloser, error) {
	return s.fs.Open(filepath)
}

func (s *aferoServer) ServeFileContent(w http.ResponseWriter, req *http.Request, slug, version, file string) error {
	filepath := s.mkPath(slug, version, file)
	err := s.serveFileContent(w, req, filepath)
	if os.IsNotExist(err) {
		return s.serveFileContent(w, req, retroCompatMakePath(slug, version, file))
	}
	return err
}
func (s *aferoServer) serveFileContent(w http.ResponseWriter, req *http.Request, filepath string) error {
	infos, err := s.fs.Stat(filepath)
	if err != nil {
		return err
	}
	r, err := s.fs.Open(filepath)
	if err != nil {
		return err
	}
	defer r.Close()
	http.ServeContent(w, req, filepath, infos.ModTime(), r)
	return nil
}

func defaultMakePath(slug, version, file string) string {
	basepath := path.Join("/", slug, version)
	filepath := path.Join("/", file)
	return path.Join(basepath, filepath)
}

// FIXME: retro-compatibility code to serve application that were not installed
// in a versioned directory.
func retroCompatMakePath(slug, version, file string) string {
	return path.Join("/", slug, file)
}

func containerName(appsType AppType) string {
	switch appsType {
	case Webapp:
		return "apps-web"
	case Konnector:
		return "apps-konnectors"
	}
	panic("Unknown AppType")
}

func wrapSwiftErr(err error) error {
	if err == swift.ObjectNotFound || err == swift.ContainerNotFound {
		return os.ErrNotExist
	}
	return err
}
<commit_msg>Fix: No need to path escape for swift<commit_after>package apps

import (
	"io"
	"net/http"
	"os"
	"path"
	"time"

	"github.com/cozy/swift"
	"github.com/spf13/afero"
)

// FileServer interface defines a way to access and serve the application's
// data files.
type FileServer interface {
	Open(slug, version, file string) (io.ReadCloser, error)
	ServeFileContent(w http.ResponseWriter, req *http.Request,
		slug, version, file string) error
}

type swiftServer struct {
	c         *swift.Connection
	container string
}

type aferoServer struct {
	mkPath func(slug, version, file string) string
	fs     afero.Fs
}

// NewSwiftFileServer returns provides the apps.FileServer implementation
// using the swift backend as file server.
func NewSwiftFileServer(conn *swift.Connection, appsType AppType) FileServer {
	return &swiftServer{
		c:         conn,
		container: containerName(appsType),
	}
}

func (s *swiftServer) Open(slug, version, file string) (io.ReadCloser, error) {
	objName := s.makeObjectName(slug, version, file)
	f, _, err := s.c.ObjectOpen(s.container, objName, false, nil)
	if err != nil {
		return nil, wrapSwiftErr(err)
	}
	return f, nil
}

func (s *swiftServer) ServeFileContent(w http.ResponseWriter, req *http.Request, slug, version, file string) error {
	objName := s.makeObjectName(slug, version, file)
	f, o, err := s.c.ObjectOpen(s.container, objName, false, nil)
	if err != nil {
		return wrapSwiftErr(err)
	}
	defer f.Close()
	lastModified, _ := time.Parse(http.TimeFormat, o["Last-Modified"])
	w.Header().Set("Etag", o["Etag"])
	http.ServeContent(w, req, objName, lastModified, f)
	return nil
}

func (s *swiftServer) makeObjectName(slug, version, file string) string {
	return path.Join(slug, version, file)
}

// NewAferoFileServer returns a simple wrapper of the afero.Fs interface that
// provides the apps.FileServer interface.
//
// You can provide a makePath method to define how the file name should be
// created from the application's slug, version and file name. If not provided,
// the standard VFS concatenation (starting with vfs.WebappsDirName) is used.
func NewAferoFileServer(fs afero.Fs, makePath func(slug, version, file string) string) FileServer {
	if makePath == nil {
		makePath = defaultMakePath
	}
	return &aferoServer{
		mkPath: makePath,
		fs:     fs,
	}
}

func (s *aferoServer) Open(slug, version, file string) (io.ReadCloser, error) {
	filepath := s.mkPath(slug, version, file)
	f, err := s.open(filepath)
	if os.IsNotExist(err) {
		return s.open(retroCompatMakePath(slug, version, file))
	}
	return f, err
}
func (s *aferoServer) open(filepath string) (io.ReadCloser, error) {
	return s.fs.Open(filepath)
}

func (s *aferoServer) ServeFileContent(w http.ResponseWriter, req *http.Request, slug, version, file string) error {
	filepath := s.mkPath(slug, version, file)
	err := s.serveFileContent(w, req, filepath)
	if os.IsNotExist(err) {
		return s.serveFileContent(w, req, retroCompatMakePath(slug, version, file))
	}
	return err
}
func (s *aferoServer) serveFileContent(w http.ResponseWriter, req *http.Request, filepath string) error {
	infos, err := s.fs.Stat(filepath)
	if err != nil {
		return err
	}
	r, err := s.fs.Open(filepath)
	if err != nil {
		return err
	}
	defer r.Close()
	http.ServeContent(w, req, filepath, infos.ModTime(), r)
	return nil
}

func defaultMakePath(slug, version, file string) string {
	basepath := path.Join("/", slug, version)
	filepath := path.Join("/", file)
	return path.Join(basepath, filepath)
}

// FIXME: retro-compatibility code to serve application that were not installed
// in a versioned directory.
func retroCompatMakePath(slug, version, file string) string {
	return path.Join("/", slug, file)
}

func containerName(appsType AppType) string {
	switch appsType {
	case Webapp:
		return "apps-web"
	case Konnector:
		return "apps-konnectors"
	}
	panic("Unknown AppType")
}

func wrapSwiftErr(err error) error {
	if err == swift.ObjectNotFound || err == swift.ContainerNotFound {
		return os.ErrNotExist
	}
	return err
}
<|endoftext|>


<commit_before>package build

import (
	"fmt"

	"github.com/flant/werf/pkg/container_runtime"

	"github.com/fatih/color"

	"github.com/flant/logboek"

	"github.com/flant/werf/pkg/build/stage"
	"github.com/flant/werf/pkg/docker_registry"
	"github.com/flant/werf/pkg/image"
	"github.com/flant/werf/pkg/logging"
)

type BaseImageType string

const (
	ImageFromRegistryAsBaseImage BaseImageType = "ImageFromRegistryBaseImage"
	StageAsBaseImage             BaseImageType = "StageBaseImage"
)

type Image struct {
	name string

	baseImageName      string
	baseImageImageName string
	baseImageRepoId    string

	stages            []stage.Interface
	lastNonEmptyStage stage.Interface
	contentSignature  string
	isArtifact        bool
	isDockerfileImage bool

	baseImageType    BaseImageType
	stageAsBaseImage stage.Interface
	baseImage        *container_runtime.StageImage
}

func (i *Image) LogName() string {
	return logging.ImageLogName(i.name, i.isArtifact)
}

func (i *Image) LogDetailedName() string {
	return logging.ImageLogProcessName(i.name, i.isArtifact)
}

func (i *Image) LogProcessStyle() *logboek.Style {
	return ImageLogProcessStyle(i.isArtifact)
}

func (i *Image) LogTagStyle() *logboek.Style {
	return ImageLogTagStyle(i.isArtifact)
}

func ImageLogProcessStyle(isArtifact bool) *logboek.Style {
	return imageDefaultStyle(isArtifact)
}

func ImageLogTagStyle(isArtifact bool) *logboek.Style {
	return imageDefaultStyle(isArtifact)
}

func imageDefaultStyle(isArtifact bool) *logboek.Style {
	var attributes []color.Attribute
	if isArtifact {
		attributes = []color.Attribute{color.FgCyan, color.Bold}
	} else {
		attributes = []color.Attribute{color.FgYellow, color.Bold}
	}

	return &logboek.Style{Attributes: attributes}
}

func (i *Image) SetStages(stages []stage.Interface) {
	i.stages = stages
}

func (i *Image) GetStages() []stage.Interface {
	return i.stages
}

func (i *Image) SetLastNonEmptyStage(stg stage.Interface) {
	i.lastNonEmptyStage = stg
}

func (i *Image) GetLastNonEmptyStage() stage.Interface {
	return i.lastNonEmptyStage
}

func (i *Image) SetContentSignature(sig string) {
	i.contentSignature = sig
}

func (i *Image) GetContentSignature() string {
	return i.contentSignature
}

func (i *Image) GetStage(name stage.StageName) stage.Interface {
	for _, s := range i.stages {
		if s.Name() == name {
			return s
		}
	}

	return nil
}

func (i *Image) GetName() string {
	return i.name
}

func (i *Image) GetLogName() string {
	return i.LogName()
}

func (i *Image) SetupBaseImage(c *Conveyor) {
	if i.baseImageImageName != "" {
		i.baseImageType = StageAsBaseImage
		i.stageAsBaseImage = c.GetImage(i.baseImageImageName).GetLastNonEmptyStage()
		i.baseImage = c.GetOrCreateStageImage(nil, i.stageAsBaseImage.GetImage().Name())
	} else {
		i.baseImageType = ImageFromRegistryAsBaseImage
		i.baseImage = c.GetOrCreateStageImage(nil, i.baseImageName)
	}
}

func (i *Image) GetBaseImage() *container_runtime.StageImage {
	return i.baseImage
}

func (i *Image) FetchBaseImage(c *Conveyor) error {
	switch i.baseImageType {
	case ImageFromRegistryAsBaseImage:
		containerRuntime := c.ContainerRuntime.(*container_runtime.LocalDockerServerRuntime)

		if inspect, err := containerRuntime.GetImageInspect(i.baseImage.Name()); err != nil {
			return fmt.Errorf("unable to inspect local image %s: %s", i.baseImage.Name(), err)
		} else if inspect != nil {
			// TODO: do not use container_runtime.StageImage for base image
			i.baseImage.SetStagesStorageImageInfo(image.NewInfoFromInspect(i.baseImage.Name(), inspect))

			baseImageRepoId, err := i.getFromBaseImageIdFromRegistry(c, i.baseImage.Name())
			if baseImageRepoId == inspect.ID || err != nil {
				if err != nil {
					logboek.LogWarnF("WARNING: cannot get base image id (%s): %s\n", i.baseImage.Name(), err)
					logboek.LogWarnF("WARNING: using existing image %s without pull\n", i.baseImage.Name())
					logboek.Warn.LogOptionalLn()
				}

				return nil
			}
		}

		logProcessOptions := logboek.LevelLogProcessOptions{Style: logboek.HighlightStyle()}
		return logboek.Default.LogProcess(fmt.Sprintf("Pulling base image %s", i.baseImage.Name()), logProcessOptions, func() error {
			return c.ContainerRuntime.PullImageFromRegistry(&container_runtime.DockerImage{Image: i.baseImage})
		})
	case StageAsBaseImage:
		if err := c.ContainerRuntime.RefreshImageObject(&container_runtime.DockerImage{Image: i.baseImage}); err != nil {
			return err
		}
		if err := fetchStage(c.StagesStorage, i.stageAsBaseImage); err != nil {
			return err
		}
	default:
		panic(fmt.Sprintf("unknown base image type %q", i.baseImageType))
	}

	return nil
}

func (i *Image) getFromBaseImageIdFromRegistry(c *Conveyor, baseImageName string) (string, error) {
	if i.baseImageRepoId != "" {
		return i.baseImageRepoId, nil
	} else if cachedBaseImageRepoId, exist := c.baseImagesRepoIdsCache[baseImageName]; exist {
		i.baseImageRepoId = cachedBaseImageRepoId
		return cachedBaseImageRepoId, nil
	} else if cachedBaseImagesRepoErr, exist := c.baseImagesRepoErrCache[baseImageName]; exist {
		return "", cachedBaseImagesRepoErr
	}

	var fetchedBaseRepoImage *image.Info
	processMsg := fmt.Sprintf("Trying to get from base image id from registry (%s)", baseImageName)
	if err := logboek.Info.LogProcessInline(processMsg, logboek.LevelLogProcessInlineOptions{}, func() error {
		var fetchImageIdErr error
		fetchedBaseRepoImage, fetchImageIdErr = docker_registry.API().GetRepoImage(baseImageName)
		if fetchImageIdErr != nil {
			c.baseImagesRepoErrCache[baseImageName] = fetchImageIdErr
			return fmt.Errorf("can not get base image id from registry (%s): %s", baseImageName, fetchImageIdErr)
		}

		return nil
	}); err != nil {
		return "", err
	}

	i.baseImageRepoId = fetchedBaseRepoImage.ID
	c.baseImagesRepoIdsCache[baseImageName] = i.baseImageRepoId

	return i.baseImageRepoId, nil
}
<commit_msg>Remote storages support: fix panic 'github.com/flant/werf/pkg/build/stage.(*BaseStage).getServiceMountsFromLabels'<commit_after>package build

import (
	"fmt"

	"github.com/flant/werf/pkg/container_runtime"

	"github.com/fatih/color"

	"github.com/flant/logboek"

	"github.com/flant/werf/pkg/build/stage"
	"github.com/flant/werf/pkg/docker_registry"
	"github.com/flant/werf/pkg/image"
	"github.com/flant/werf/pkg/logging"
)

type BaseImageType string

const (
	ImageFromRegistryAsBaseImage BaseImageType = "ImageFromRegistryBaseImage"
	StageAsBaseImage             BaseImageType = "StageBaseImage"
)

type Image struct {
	name string

	baseImageName      string
	baseImageImageName string
	baseImageRepoId    string

	stages            []stage.Interface
	lastNonEmptyStage stage.Interface
	contentSignature  string
	isArtifact        bool
	isDockerfileImage bool

	baseImageType    BaseImageType
	stageAsBaseImage stage.Interface
	baseImage        *container_runtime.StageImage
}

func (i *Image) LogName() string {
	return logging.ImageLogName(i.name, i.isArtifact)
}

func (i *Image) LogDetailedName() string {
	return logging.ImageLogProcessName(i.name, i.isArtifact)
}

func (i *Image) LogProcessStyle() *logboek.Style {
	return ImageLogProcessStyle(i.isArtifact)
}

func (i *Image) LogTagStyle() *logboek.Style {
	return ImageLogTagStyle(i.isArtifact)
}

func ImageLogProcessStyle(isArtifact bool) *logboek.Style {
	return imageDefaultStyle(isArtifact)
}

func ImageLogTagStyle(isArtifact bool) *logboek.Style {
	return imageDefaultStyle(isArtifact)
}

func imageDefaultStyle(isArtifact bool) *logboek.Style {
	var attributes []color.Attribute
	if isArtifact {
		attributes = []color.Attribute{color.FgCyan, color.Bold}
	} else {
		attributes = []color.Attribute{color.FgYellow, color.Bold}
	}

	return &logboek.Style{Attributes: attributes}
}

func (i *Image) SetStages(stages []stage.Interface) {
	i.stages = stages
}

func (i *Image) GetStages() []stage.Interface {
	return i.stages
}

func (i *Image) SetLastNonEmptyStage(stg stage.Interface) {
	i.lastNonEmptyStage = stg
}

func (i *Image) GetLastNonEmptyStage() stage.Interface {
	return i.lastNonEmptyStage
}

func (i *Image) SetContentSignature(sig string) {
	i.contentSignature = sig
}

func (i *Image) GetContentSignature() string {
	return i.contentSignature
}

func (i *Image) GetStage(name stage.StageName) stage.Interface {
	for _, s := range i.stages {
		if s.Name() == name {
			return s
		}
	}

	return nil
}

func (i *Image) GetName() string {
	return i.name
}

func (i *Image) GetLogName() string {
	return i.LogName()
}

func (i *Image) SetupBaseImage(c *Conveyor) {
	if i.baseImageImageName != "" {
		i.baseImageType = StageAsBaseImage
		i.stageAsBaseImage = c.GetImage(i.baseImageImageName).GetLastNonEmptyStage()
		i.baseImage = c.GetOrCreateStageImage(nil, i.stageAsBaseImage.GetImage().Name())
	} else {
		i.baseImageType = ImageFromRegistryAsBaseImage
		i.baseImage = c.GetOrCreateStageImage(nil, i.baseImageName)
	}
}

func (i *Image) GetBaseImage() *container_runtime.StageImage {
	return i.baseImage
}

func (i *Image) FetchBaseImage(c *Conveyor) error {
	switch i.baseImageType {
	case ImageFromRegistryAsBaseImage:
		containerRuntime := c.ContainerRuntime.(*container_runtime.LocalDockerServerRuntime)

		if inspect, err := containerRuntime.GetImageInspect(i.baseImage.Name()); err != nil {
			return fmt.Errorf("unable to inspect local image %s: %s", i.baseImage.Name(), err)
		} else if inspect != nil {
			// TODO: do not use container_runtime.StageImage for base image
			i.baseImage.SetStagesStorageImageInfo(image.NewInfoFromInspect(i.baseImage.Name(), inspect))

			baseImageRepoId, err := i.getFromBaseImageIdFromRegistry(c, i.baseImage.Name())
			if baseImageRepoId == inspect.ID || err != nil {
				if err != nil {
					logboek.LogWarnF("WARNING: cannot get base image id (%s): %s\n", i.baseImage.Name(), err)
					logboek.LogWarnF("WARNING: using existing image %s without pull\n", i.baseImage.Name())
					logboek.Warn.LogOptionalLn()
				}

				return nil
			}
		}

		logProcessOptions := logboek.LevelLogProcessOptions{Style: logboek.HighlightStyle()}
		if err := logboek.Default.LogProcess(fmt.Sprintf("Pulling base image %s", i.baseImage.Name()), logProcessOptions, func() error {
			return c.ContainerRuntime.PullImageFromRegistry(&container_runtime.DockerImage{Image: i.baseImage})
		}); err != nil {
			return err
		}

		if inspect, err := containerRuntime.GetImageInspect(i.baseImage.Name()); err != nil {
			return fmt.Errorf("unable to inspect local image %s: %s", i.baseImage.Name(), err)
		} else if inspect == nil {
			return fmt.Errorf("unable to inspect local image %s after successful pull: image is not exists", i.baseImage.Name(), err)
		} else {
			// TODO: do not use container_runtime.StageImage for base image
			i.baseImage.SetStagesStorageImageInfo(image.NewInfoFromInspect(i.baseImage.Name(), inspect))
		}
	case StageAsBaseImage:
		if err := c.ContainerRuntime.RefreshImageObject(&container_runtime.DockerImage{Image: i.baseImage}); err != nil {
			return err
		}
		if err := fetchStage(c.StagesStorage, i.stageAsBaseImage); err != nil {
			return err
		}
	default:
		panic(fmt.Sprintf("unknown base image type %q", i.baseImageType))
	}

	return nil
}

func (i *Image) getFromBaseImageIdFromRegistry(c *Conveyor, baseImageName string) (string, error) {
	if i.baseImageRepoId != "" {
		return i.baseImageRepoId, nil
	} else if cachedBaseImageRepoId, exist := c.baseImagesRepoIdsCache[baseImageName]; exist {
		i.baseImageRepoId = cachedBaseImageRepoId
		return cachedBaseImageRepoId, nil
	} else if cachedBaseImagesRepoErr, exist := c.baseImagesRepoErrCache[baseImageName]; exist {
		return "", cachedBaseImagesRepoErr
	}

	var fetchedBaseRepoImage *image.Info
	processMsg := fmt.Sprintf("Trying to get from base image id from registry (%s)", baseImageName)
	if err := logboek.Info.LogProcessInline(processMsg, logboek.LevelLogProcessInlineOptions{}, func() error {
		var fetchImageIdErr error
		fetchedBaseRepoImage, fetchImageIdErr = docker_registry.API().GetRepoImage(baseImageName)
		if fetchImageIdErr != nil {
			c.baseImagesRepoErrCache[baseImageName] = fetchImageIdErr
			return fmt.Errorf("can not get base image id from registry (%s): %s", baseImageName, fetchImageIdErr)
		}

		return nil
	}); err != nil {
		return "", err
	}

	i.baseImageRepoId = fetchedBaseRepoImage.ID
	c.baseImagesRepoIdsCache[baseImageName] = i.baseImageRepoId

	return i.baseImageRepoId, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package image

import (
	"fmt"
	"os"

	"github.com/GoogleCloudPlatform/kaniko/pkg/version"
	"github.com/containers/image/types"

	img "github.com/GoogleCloudPlatform/container-diff/pkg/image"
	"github.com/GoogleCloudPlatform/kaniko/pkg/constants"
	"github.com/containers/image/copy"
	"github.com/containers/image/docker"
	"github.com/containers/image/signature"
	"github.com/containers/image/transports/alltransports"
	"github.com/sirupsen/logrus"
)

// sourceImage is the image that will be modified by the executor

// InitializeSourceImage initializes the source image with the base image
func NewSourceImage(srcImg string) (*img.MutableSource, error) {
	if srcImg == constants.NoBaseImage {
		return img.NewMutableSource(nil)
	}
	logrus.Infof("Initializing source image %s", srcImg)
	ref, err := docker.ParseReference("//" + srcImg)
	if err != nil {
		return nil, err
	}
	return img.NewMutableSource(ref)
}

// PushImage pushes the final image
func PushImage(ms *img.MutableSource, destImg string) error {
	srcRef := &img.ProxyReference{
		ImageReference: nil,
		Src:            ms,
	}
	destRef, err := alltransports.ParseImageName("docker://" + destImg)
	if err != nil {
		return err
	}
	policyContext, err := getPolicyContext()
	if err != nil {
		return err
	}
	logrus.Infof("Pushing image to %s", destImg)

	opts := &copy.Options{
		DestinationCtx: &types.SystemContext{
			DockerRegistryUserAgent: fmt.Sprintf("kaniko/executor-%s", version.Version()),
		},
	}
	return copy.Image(policyContext, destRef, srcRef, opts)
}

// SetEnvVariables sets environment variables as specified in the image
func SetEnvVariables(ms *img.MutableSource) error {
	envVars := ms.Env()
	for key, val := range envVars {
		if err := os.Setenv(key, val); err != nil {
			return err
		}
		logrus.Debugf("Setting environment variable %s=%s", key, val)
	}
	return nil
}

func getPolicyContext() (*signature.PolicyContext, error) {
	policyContext, err := signature.NewPolicyContext(&signature.Policy{
		Default: signature.PolicyRequirements{signature.NewPRInsecureAcceptAnything()},
	})
	if err != nil {
		logrus.Debugf("Error retrieving policy context: %s", err)
		return nil, err
	}
	return policyContext, nil
}
<commit_msg>Update image.go<commit_after>/*
Copyright 2018 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package image

import (
	"fmt"
	"os"

	"github.com/GoogleCloudPlatform/kaniko/pkg/version"
	"github.com/containers/image/types"

	img "github.com/GoogleCloudPlatform/container-diff/pkg/image"
	"github.com/GoogleCloudPlatform/kaniko/pkg/constants"
	"github.com/containers/image/copy"
	"github.com/containers/image/docker"
	"github.com/containers/image/signature"
	"github.com/containers/image/transports/alltransports"
	"github.com/sirupsen/logrus"
)

// sourceImage is the image that will be modified by the executor

// NewSourceImage initializes the source image with the base image
func NewSourceImage(srcImg string) (*img.MutableSource, error) {
	if srcImg == constants.NoBaseImage {
		return img.NewMutableSource(nil)
	}
	logrus.Infof("Initializing source image %s", srcImg)
	ref, err := docker.ParseReference("//" + srcImg)
	if err != nil {
		return nil, err
	}
	return img.NewMutableSource(ref)
}

// PushImage pushes the final image
func PushImage(ms *img.MutableSource, destImg string) error {
	srcRef := &img.ProxyReference{
		ImageReference: nil,
		Src:            ms,
	}
	destRef, err := alltransports.ParseImageName("docker://" + destImg)
	if err != nil {
		return err
	}
	policyContext, err := getPolicyContext()
	if err != nil {
		return err
	}
	logrus.Infof("Pushing image to %s", destImg)

	opts := &copy.Options{
		DestinationCtx: &types.SystemContext{
			DockerRegistryUserAgent: fmt.Sprintf("kaniko/executor-%s", version.Version()),
		},
	}
	return copy.Image(policyContext, destRef, srcRef, opts)
}

// SetEnvVariables sets environment variables as specified in the image
func SetEnvVariables(ms *img.MutableSource) error {
	envVars := ms.Env()
	for key, val := range envVars {
		if err := os.Setenv(key, val); err != nil {
			return err
		}
		logrus.Debugf("Setting environment variable %s=%s", key, val)
	}
	return nil
}

func getPolicyContext() (*signature.PolicyContext, error) {
	policyContext, err := signature.NewPolicyContext(&signature.Policy{
		Default: signature.PolicyRequirements{signature.NewPRInsecureAcceptAnything()},
	})
	if err != nil {
		logrus.Debugf("Error retrieving policy context: %s", err)
		return nil, err
	}
	return policyContext, nil
}
<|endoftext|>


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

//go:build linux
// +build linux

package mount

import (
	"bytes"
	"fmt"
	"io"
	"os"

	"golang.org/x/sys/unix"
)

const blocksize = 65536

// These are inferred magic numbers from documents and partitions.
// Ones known to work are first, followed by a gap, followed by not
// tested ones. Please preserve this pattern.
var (
	EXT2     = []byte{0x53, 0xef}
	EXT3     = []byte{0x53, 0xef}
	EXT4     = []byte{0x53, 0xef}
	ISOFS    = []byte{1, 'C', 'D', '0', '0', '1'}
	SQUASHFS = []byte{'h', 's', 'q', 's'}
	XFS      = []byte{'X', 'F', 'S', 'B'}
	// There's no fixed magic number for the different FAT varieties
	// Usually they start with 0xEB but it's not mandatory.
	// Therefore we just list a few examples that we have seen in the wild.
	MSDOS = []byte{0xeb, 0x3c}
	VFAT  = []byte{0xeb, 0x58}
	// QEMU virtual VFAT
	VVFAT = []byte{0xeb, 0x3e}

	AAFS        = []byte{0x5a, 0x3c, 0x69, 0xf0}
	ADFS        = []byte{0xad, 0xf5}
	AFFS        = []byte{0xad, 0xff}
	AFS         = []byte{0x53, 0x46, 0x41, 0x4F}
	BDEVFS      = []byte{0x62, 0x64, 0x65, 0x76}
	BINDERFS    = []byte{0x6c, 0x6f, 0x6f, 0x70}
	BINFMTFS    = []byte{0x42, 0x49, 0x4e, 0x4d}
	BPF         = []byte{0xca, 0xfe, 0x4a, 0x11}
	BTRFS       = []byte{0x91, 0x23, 0x68, 0x3E}
	CGROUP      = []byte{0x27, 0xe0, 0xeb}
	CGROUP2     = []byte{0x63, 0x67, 0x72, 0x70}
	CODA        = []byte{0x73, 0x75, 0x72, 0x45}
	CRAMFS      = []byte{0x28, 0xcd, 0x3d, 0x45}
	CRAMFSOther = []byte{0x45, 0x3d, 0xcd, 0x28}
	DAXFS       = []byte{0x64, 0x64, 0x61, 0x78}
	DEBUGFS     = []byte{0x64, 0x62, 0x67, 0x20}
	DEVPTS      = []byte{0x1c, 0xd1}
	ECRYPTFS    = []byte{0xf1, 0x5f}
	EFIVARFS    = []byte{0xde, 0x5e, 0x81, 0xe4}
	EFS         = []byte{0x41, 0x4A, 0x53}
	// EXFAT seems to be a samsung file system.
	//EXFAT       = []byte{0x53, 0xef}
	F2FS      = []byte{0xF2, 0xF5, 0x20, 0x10}
	FUSE      = []byte{0x65, 0x73, 0x55, 0x46}
	FUTEXFS   = []byte{0xBA, 0xD1, 0xDE, 0xA}
	HOSTFS    = []byte{0x00, 0xc0, 0xff, 0xee}
	HPFS      = []byte{0xf9, 0x95, 0xe8, 0x49}
	HUGETLBFS = []byte{0x95, 0x84, 0x58, 0xf6}
	JFFS2     = []byte{0x72, 0xb6}
	JFS       = []byte{0x31, 0x53, 0x46, 0x4a}
	MTD       = []byte{0x11, 0x30, 0x78, 0x54}
	NFS       = []byte{0x69, 0x69}
	NILFS     = []byte{0x34, 0x34}
	NSFS      = []byte{0x6e, 0x73, 0x66, 0x73}
	// From docs, not tested.
	NTFS       = []byte{0xeb, 0x52, 0x90, 'N', 'T', 'F', 'S', ' ', ' ', ' ', ' '}
	OCFS2      = []byte{0x74, 0x61, 0x63, 0x6f}
	OPENPROM   = []byte{0x9f, 0xa1}
	OVERLAYFS  = []byte{0x79, 0x4c, 0x76, 0x30}
	PIPEFS     = []byte{0x50, 0x49, 0x50, 0x45}
	PROC       = []byte{0x9f, 0xa0}
	PSTOREFS   = []byte{0x61, 0x65, 0x67, 0x6C}
	QNX4       = []byte{0x00, 0x2f}
	QNX6       = []byte{0x68, 0x19, 0x11, 0x22}
	RAMFS      = []byte{0x85, 0x84, 0x58, 0xf6}
	RDTGROUP   = []byte{0x76, 0x55, 0x82, 1}
	ROMFS      = []byte{0x72, 0x75}
	SECURITYFS = []byte{0x73, 0x63, 0x66, 0x73}
	SELINUX    = []byte{0xf9, 0x7c, 0xff, 0x8c}
	SMACK      = []byte{0x43, 0x41, 0x5d, 0x53}
	SMB        = []byte{0x51, 0x7B}
	SOCKFS     = []byte{0x53, 0x4F, 0x43, 0x4B}
	SYSFS      = []byte{0x62, 0x65, 0x65, 0x72}
	TMPFS      = []byte{0x01, 0x02, 0x19, 0x94}
	TRACEFS    = []byte{0x74, 0x72, 0x61, 0x63}
	UBIFS      = []byte{0x24, 0x05, 0x19, 0x05}
	UDF        = []byte{0x15, 0x01, 0x33, 0x46}
	USBDEVICE  = []byte{0x9f, 0xa2}
	V9FS       = []byte{0x01, 0x02, 0x19, 0x97}
	XENFS      = []byte{0xab, 0xba, 0x19, 0x74}
	ZONEFS     = []byte{0x5a, 0x4f, 0x46, 0x53}
	ZSMALLOC   = []byte{0x58, 0x29, 0x58, 0x29}
)

type magic struct {
	magic []byte
	off   int64
	name  string
	flags uintptr
}

// magics is just a list of magic structs.
// One file system in particular shares a single magic for several types.
// For that reason, and reasons of space, this is a list, not a map.
// Performance is not really an issue: it is a short list, and there are simply
// not enough block devices/file systems for it to really matter.
// The ordering for the identical magic number file systems matters: ext4 is more
// desirable than ext2, so, we want to find ext4 first.
// The order should NOT BE ALPHABETIC, therefore; it should be ordered with known systems
// first, and, to break ties, with the most desirable of those systems first.
var magics = []magic{
	// From the filesystems magic:
	// 0x438   leshort         0xEF53          Linux
	{magic: EXT4, name: "ext4", off: 0x438},
	{magic: EXT3, name: "ext3", off: 0x438},
	{magic: EXT2, name: "ext2", off: 0x438},
	// We will always mount vfat; it's backward compatible (we think?)
	{magic: MSDOS, name: "vfat", off: 0},
	{magic: SQUASHFS, name: "squashfs", flags: MS_RDONLY, off: 0},
	{magic: ISOFS, name: "iso9660", flags: MS_RDONLY, off: 32768},
	{magic: VFAT, name: "vfat", off: 0},
	{magic: VVFAT, name: "vfat", off: 0},
	{magic: XFS, name: "xfs", off: 0},
}

var unknownMagics = []magic{
	//
	// here there be dragons.
	//
	{magic: V9FS, name: "9p", off: -1},
	{magic: ADFS, name: "adfs", off: -1},
	{magic: AFFS, name: "affs", off: -1},
	{magic: BTRFS, name: "btrfs", off: -1},
	{magic: SMB, name: "cifs", off: -1},
	{magic: SMB, name: "smb3", off: -1},
	{magic: CODA, name: "coda", off: -1},
	{magic: DEVPTS, name: "devpts", off: -1},
	{magic: ECRYPTFS, name: "ecryptfs", off: -1},
	{magic: EFIVARFS, name: "efivarfs", off: -1},
	{magic: EFS, name: "efs", off: -1},
	{magic: F2FS, name: "f2fs", off: -1},
	{magic: FUSE, name: "fuse", off: -1},
	// ?? {magic: GFS2, name: "gfs2", off: -1},
	// who care ... {magic: HFSPLUS_VOLHEAD_SIG, name: "hfsplus", off: -1},
	{magic: HOSTFS, name: "hostfs", off: -1},
	{magic: HPFS, name: "hpfs", off: -1},
	{magic: HUGETLBFS, name: "hugetlbfs", off: -1},
	{magic: JFFS2, name: "jffs2", off: -1},
	{magic: JFS, name: "jfs", off: -1},
	{magic: NFS, name: "nfs", off: -1},
	{magic: NTFS, name: "ntfs", off: -1},
	{magic: OPENPROM, name: "openpromfs", off: -1},
	{magic: OVERLAYFS, name: "overlay", off: -1},
	{magic: PIPEFS, name: "pipefs", off: -1},
	{magic: PROC, name: "proc", flags: MS_RDONLY, off: -1},
	{magic: PSTOREFS, name: "pstore", off: -1},
	{magic: QNX4, name: "qnx4", off: -1},
	{magic: QNX6, name: "qnx6", off: -1},
	{magic: RAMFS, name: "ramfs", off: -1},
	{magic: ROMFS, name: "romfs", flags: MS_RDONLY, off: -1},
	{magic: UBIFS, name: "ubifs", flags: MS_RDONLY, off: -1},
	{magic: UDF, name: "udf", off: -1},
	{magic: ZONEFS, name: "zonefs", off: -1},
}

// FindMagics finds all the magics matching a magic number.
func FindMagics(blk []byte) []magic {
	var b = bytes.NewReader(blk)
	var matches = []magic{}
	for _, v := range magics {
		var mag = make([]byte, len(v.magic))
		if n, err := b.ReadAt(mag, v.off); err != nil || n < len(mag) {
			continue
		}
		if bytes.Equal(v.magic, mag) {
			matches = append(matches, v)
		}
	}
	return matches
}

// FSFromBlock determines the file system type of a block device.
// It returns a string and an error. The error can be for an IO operation,
// an unknown magic number, or a magic with an unsupported file system.
// There is still a question here about whether this ought to act like
// a map and return a bool, not an error, since there are so many bogus
// block devices and we don't care about most of them.
func FSFromBlock(n string) (fs string, flags uintptr, err error) {
	// Make sure we can open, read 64k, stat it, find the magic in magics,
	// and find the file system it names.
	f, err := os.Open(n)
	if err != nil {
		return "", 0, err
	}
	defer f.Close()
	var block = make([]byte, blocksize)
	if _, err := io.ReadAtLeast(f, block, len(block)); err != nil {
		return "", 0, fmt.Errorf("no suitable filesystem for %q: %v", n, err)
	}

	magics := FindMagics(block)
	if len(magics) == 0 {
		return "", 0, fmt.Errorf("no suitable filesystem for %q", n)
	}

	for _, m := range magics {
		if err := FindFileSystem(m.name); err == nil {
			return m.name, m.flags, nil
		}
	}
	return "", 0, fmt.Errorf("no suitable filesystem for %q, from magics %q", n, magics)
}

// IsTmpfs tells if the file path given is under a tmpfs or ramfs.
func IsTmpRamfs(path string) (bool, error) {
	var s unix.Statfs_t
	if err := unix.Statfs(path, &s); err != nil {
		return false, err
	}
	// Force it to be int64 so that unix.RAMFS_MAGIC won't overflow on an
	// int32, which is the type for Type on some platforms.
	t := int64(s.Type)
	return t == unix.TMPFS_MAGIC || t == unix.RAMFS_MAGIC, nil
}
<commit_msg>mount: fix a function comment<commit_after>// Copyright 2021 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build linux
// +build linux

package mount

import (
	"bytes"
	"fmt"
	"io"
	"os"

	"golang.org/x/sys/unix"
)

const blocksize = 65536

// These are inferred magic numbers from documents and partitions.
// Ones known to work are first, followed by a gap, followed by not
// tested ones. Please preserve this pattern.
var (
	EXT2     = []byte{0x53, 0xef}
	EXT3     = []byte{0x53, 0xef}
	EXT4     = []byte{0x53, 0xef}
	ISOFS    = []byte{1, 'C', 'D', '0', '0', '1'}
	SQUASHFS = []byte{'h', 's', 'q', 's'}
	XFS      = []byte{'X', 'F', 'S', 'B'}
	// There's no fixed magic number for the different FAT varieties
	// Usually they start with 0xEB but it's not mandatory.
	// Therefore we just list a few examples that we have seen in the wild.
	MSDOS = []byte{0xeb, 0x3c}
	VFAT  = []byte{0xeb, 0x58}
	// QEMU virtual VFAT
	VVFAT = []byte{0xeb, 0x3e}

	AAFS        = []byte{0x5a, 0x3c, 0x69, 0xf0}
	ADFS        = []byte{0xad, 0xf5}
	AFFS        = []byte{0xad, 0xff}
	AFS         = []byte{0x53, 0x46, 0x41, 0x4F}
	BDEVFS      = []byte{0x62, 0x64, 0x65, 0x76}
	BINDERFS    = []byte{0x6c, 0x6f, 0x6f, 0x70}
	BINFMTFS    = []byte{0x42, 0x49, 0x4e, 0x4d}
	BPF         = []byte{0xca, 0xfe, 0x4a, 0x11}
	BTRFS       = []byte{0x91, 0x23, 0x68, 0x3E}
	CGROUP      = []byte{0x27, 0xe0, 0xeb}
	CGROUP2     = []byte{0x63, 0x67, 0x72, 0x70}
	CODA        = []byte{0x73, 0x75, 0x72, 0x45}
	CRAMFS      = []byte{0x28, 0xcd, 0x3d, 0x45}
	CRAMFSOther = []byte{0x45, 0x3d, 0xcd, 0x28}
	DAXFS       = []byte{0x64, 0x64, 0x61, 0x78}
	DEBUGFS     = []byte{0x64, 0x62, 0x67, 0x20}
	DEVPTS      = []byte{0x1c, 0xd1}
	ECRYPTFS    = []byte{0xf1, 0x5f}
	EFIVARFS    = []byte{0xde, 0x5e, 0x81, 0xe4}
	EFS         = []byte{0x41, 0x4A, 0x53}
	// EXFAT seems to be a samsung file system.
	//EXFAT       = []byte{0x53, 0xef}
	F2FS      = []byte{0xF2, 0xF5, 0x20, 0x10}
	FUSE      = []byte{0x65, 0x73, 0x55, 0x46}
	FUTEXFS   = []byte{0xBA, 0xD1, 0xDE, 0xA}
	HOSTFS    = []byte{0x00, 0xc0, 0xff, 0xee}
	HPFS      = []byte{0xf9, 0x95, 0xe8, 0x49}
	HUGETLBFS = []byte{0x95, 0x84, 0x58, 0xf6}
	JFFS2     = []byte{0x72, 0xb6}
	JFS       = []byte{0x31, 0x53, 0x46, 0x4a}
	MTD       = []byte{0x11, 0x30, 0x78, 0x54}
	NFS       = []byte{0x69, 0x69}
	NILFS     = []byte{0x34, 0x34}
	NSFS      = []byte{0x6e, 0x73, 0x66, 0x73}
	// From docs, not tested.
	NTFS       = []byte{0xeb, 0x52, 0x90, 'N', 'T', 'F', 'S', ' ', ' ', ' ', ' '}
	OCFS2      = []byte{0x74, 0x61, 0x63, 0x6f}
	OPENPROM   = []byte{0x9f, 0xa1}
	OVERLAYFS  = []byte{0x79, 0x4c, 0x76, 0x30}
	PIPEFS     = []byte{0x50, 0x49, 0x50, 0x45}
	PROC       = []byte{0x9f, 0xa0}
	PSTOREFS   = []byte{0x61, 0x65, 0x67, 0x6C}
	QNX4       = []byte{0x00, 0x2f}
	QNX6       = []byte{0x68, 0x19, 0x11, 0x22}
	RAMFS      = []byte{0x85, 0x84, 0x58, 0xf6}
	RDTGROUP   = []byte{0x76, 0x55, 0x82, 1}
	ROMFS      = []byte{0x72, 0x75}
	SECURITYFS = []byte{0x73, 0x63, 0x66, 0x73}
	SELINUX    = []byte{0xf9, 0x7c, 0xff, 0x8c}
	SMACK      = []byte{0x43, 0x41, 0x5d, 0x53}
	SMB        = []byte{0x51, 0x7B}
	SOCKFS     = []byte{0x53, 0x4F, 0x43, 0x4B}
	SYSFS      = []byte{0x62, 0x65, 0x65, 0x72}
	TMPFS      = []byte{0x01, 0x02, 0x19, 0x94}
	TRACEFS    = []byte{0x74, 0x72, 0x61, 0x63}
	UBIFS      = []byte{0x24, 0x05, 0x19, 0x05}
	UDF        = []byte{0x15, 0x01, 0x33, 0x46}
	USBDEVICE  = []byte{0x9f, 0xa2}
	V9FS       = []byte{0x01, 0x02, 0x19, 0x97}
	XENFS      = []byte{0xab, 0xba, 0x19, 0x74}
	ZONEFS     = []byte{0x5a, 0x4f, 0x46, 0x53}
	ZSMALLOC   = []byte{0x58, 0x29, 0x58, 0x29}
)

type magic struct {
	magic []byte
	off   int64
	name  string
	flags uintptr
}

// magics is just a list of magic structs.
// One file system in particular shares a single magic for several types.
// For that reason, and reasons of space, this is a list, not a map.
// Performance is not really an issue: it is a short list, and there are simply
// not enough block devices/file systems for it to really matter.
// The ordering for the identical magic number file systems matters: ext4 is more
// desirable than ext2, so, we want to find ext4 first.
// The order should NOT BE ALPHABETIC, therefore; it should be ordered with known systems
// first, and, to break ties, with the most desirable of those systems first.
var magics = []magic{
	// From the filesystems magic:
	// 0x438   leshort         0xEF53          Linux
	{magic: EXT4, name: "ext4", off: 0x438},
	{magic: EXT3, name: "ext3", off: 0x438},
	{magic: EXT2, name: "ext2", off: 0x438},
	// We will always mount vfat; it's backward compatible (we think?)
	{magic: MSDOS, name: "vfat", off: 0},
	{magic: SQUASHFS, name: "squashfs", flags: MS_RDONLY, off: 0},
	{magic: ISOFS, name: "iso9660", flags: MS_RDONLY, off: 32768},
	{magic: VFAT, name: "vfat", off: 0},
	{magic: VVFAT, name: "vfat", off: 0},
	{magic: XFS, name: "xfs", off: 0},
}

var unknownMagics = []magic{
	//
	// here there be dragons.
	//
	{magic: V9FS, name: "9p", off: -1},
	{magic: ADFS, name: "adfs", off: -1},
	{magic: AFFS, name: "affs", off: -1},
	{magic: BTRFS, name: "btrfs", off: -1},
	{magic: SMB, name: "cifs", off: -1},
	{magic: SMB, name: "smb3", off: -1},
	{magic: CODA, name: "coda", off: -1},
	{magic: DEVPTS, name: "devpts", off: -1},
	{magic: ECRYPTFS, name: "ecryptfs", off: -1},
	{magic: EFIVARFS, name: "efivarfs", off: -1},
	{magic: EFS, name: "efs", off: -1},
	{magic: F2FS, name: "f2fs", off: -1},
	{magic: FUSE, name: "fuse", off: -1},
	// ?? {magic: GFS2, name: "gfs2", off: -1},
	// who care ... {magic: HFSPLUS_VOLHEAD_SIG, name: "hfsplus", off: -1},
	{magic: HOSTFS, name: "hostfs", off: -1},
	{magic: HPFS, name: "hpfs", off: -1},
	{magic: HUGETLBFS, name: "hugetlbfs", off: -1},
	{magic: JFFS2, name: "jffs2", off: -1},
	{magic: JFS, name: "jfs", off: -1},
	{magic: NFS, name: "nfs", off: -1},
	{magic: NTFS, name: "ntfs", off: -1},
	{magic: OPENPROM, name: "openpromfs", off: -1},
	{magic: OVERLAYFS, name: "overlay", off: -1},
	{magic: PIPEFS, name: "pipefs", off: -1},
	{magic: PROC, name: "proc", flags: MS_RDONLY, off: -1},
	{magic: PSTOREFS, name: "pstore", off: -1},
	{magic: QNX4, name: "qnx4", off: -1},
	{magic: QNX6, name: "qnx6", off: -1},
	{magic: RAMFS, name: "ramfs", off: -1},
	{magic: ROMFS, name: "romfs", flags: MS_RDONLY, off: -1},
	{magic: UBIFS, name: "ubifs", flags: MS_RDONLY, off: -1},
	{magic: UDF, name: "udf", off: -1},
	{magic: ZONEFS, name: "zonefs", off: -1},
}

// FindMagics finds all the magics matching a magic number.
func FindMagics(blk []byte) []magic {
	var b = bytes.NewReader(blk)
	var matches = []magic{}
	for _, v := range magics {
		var mag = make([]byte, len(v.magic))
		if n, err := b.ReadAt(mag, v.off); err != nil || n < len(mag) {
			continue
		}
		if bytes.Equal(v.magic, mag) {
			matches = append(matches, v)
		}
	}
	return matches
}

// FSFromBlock determines the file system type of a block device.
// It returns a string and an error. The error can be for an IO operation,
// an unknown magic number, or a magic with an unsupported file system.
// There is still a question here about whether this ought to act like
// a map and return a bool, not an error, since there are so many bogus
// block devices and we don't care about most of them.
func FSFromBlock(n string) (fs string, flags uintptr, err error) {
	// Make sure we can open, read 64k, stat it, find the magic in magics,
	// and find the file system it names.
	f, err := os.Open(n)
	if err != nil {
		return "", 0, err
	}
	defer f.Close()
	var block = make([]byte, blocksize)
	if _, err := io.ReadAtLeast(f, block, len(block)); err != nil {
		return "", 0, fmt.Errorf("no suitable filesystem for %q: %v", n, err)
	}

	magics := FindMagics(block)
	if len(magics) == 0 {
		return "", 0, fmt.Errorf("no suitable filesystem for %q", n)
	}

	for _, m := range magics {
		if err := FindFileSystem(m.name); err == nil {
			return m.name, m.flags, nil
		}
	}
	return "", 0, fmt.Errorf("no suitable filesystem for %q, from magics %q", n, magics)
}

// IsTmpRamfs tells if the file path given is under a tmpfs or ramfs.
func IsTmpRamfs(path string) (bool, error) {
	var s unix.Statfs_t
	if err := unix.Statfs(path, &s); err != nil {
		return false, err
	}
	// Force it to be int64 so that unix.RAMFS_MAGIC won't overflow on an
	// int32, which is the type for Type on some platforms.
	t := int64(s.Type)
	return t == unix.TMPFS_MAGIC || t == unix.RAMFS_MAGIC, nil
}
<|endoftext|>


<commit_before>package store

import (
	log "github.com/Sirupsen/logrus"
	"github.com/garyburd/redigo/redis"
)

// RedisStore is the redis store.
type RedisStore struct {
	// The prefix to use for the key.
	Prefix string

	// github.com/garyburd/redigo Pool instance.
	Pool *redis.Pool

	// The maximum number of retry under race conditions.
	MaxRetry int
}

// NewRedisStore returns an instance of redis store.
func NewRedisStore(pool *redis.Pool) (Store, error) {
	return NewRedisStoreWithOptions(pool, Options{
		Prefix: "janus",
	})
}

// NewRedisStoreWithOptions returns an instance of redis store with custom options.
func NewRedisStoreWithOptions(pool *redis.Pool, options Options) (Store, error) {
	store := &RedisStore{
		Pool:   pool,
		Prefix: options.Prefix,
	}

	if _, err := store.ping(); err != nil {
		return nil, err
	}

	return store, nil
}

func (s *RedisStore) getConnection() redis.Conn {
	return s.Pool.Get()
}

// ping checks if redis is alive.
func (s *RedisStore) ping() (bool, error) {
	conn := s.getConnection()
	defer conn.Close()

	data, err := conn.Do("PING")
	if err != nil || data == nil {
		return false, err
	}

	return data == "PONG", nil
}

// Exists checks if a key exists in the store
func (s *RedisStore) Exists(key string) (bool, error) {
	conn := s.getConnection()
	defer conn.Close()

	return s.exists(conn, key)
}

// Get retreives a value from the store
func (s *RedisStore) Get(key string) (string, error) {
	conn := s.getConnection()
	defer conn.Close()

	return s.get(conn, key)
}

// Remove a value from the store
func (s *RedisStore) Remove(key string) error {
	conn := s.getConnection()
	defer conn.Close()

	return s.remove(conn, key)
}

// Set a value in the store
func (s *RedisStore) Set(key string, value string, expire int64) error {
	conn := s.getConnection()
	defer conn.Close()

	return s.set(conn, key, value, expire)
}

// Publish publishes to a topic in redis
func (s *RedisStore) Publish(topic string, data []byte) error {
	c := s.getConnection()
	_, err := c.Do("PUBLISH", topic, data)
	return err
}

// Subscribe subscribes to a topic in redis
func (s *RedisStore) Subscribe(channel string, callback func(interface{})) error {
	// Get a connection from a pool
	c := s.getConnection()
	defer c.Close()

	psc := redis.PubSubConn{Conn: c}
	if err := psc.Subscribe(channel); err != nil {
		return err
	}

	for {
		switch v := psc.Receive().(type) {
		case redis.Message:
			callback(v)
		case error:
			log.WithError(v).Debug("An error ocurred when getting the message")
			return v
		}
	}
}

func (s *RedisStore) exists(conn redis.Conn, key string) (bool, error) {
	exists, err := redis.Bool(conn.Do("EXISTS", key))
	if err != nil {
		return false, err
	}

	return exists, nil
}

func (s *RedisStore) remove(conn redis.Conn, key string) error {
	_, err := conn.Do("DEL", key)
	return err
}

func (s *RedisStore) get(conn redis.Conn, key string) (string, error) {
	return redis.String(conn.Do("GET", key))
}

func (s *RedisStore) set(conn redis.Conn, key string, value string, expire int64) error {
	command, args := getSetCommandAndArgs(key, value, expire)
	if _, err := conn.Do(command, args...); err != nil {
		return err
	}

	return nil
}

func getSetCommandAndArgs(key string, value string, expire int64) (string, []interface{}) {
	var args []interface{}
	if expire == 0 {
		args = append(args, key)
		args = append(args, value)
		return "SET", args
	}

	args = append(args, key)
	args = append(args, expire)
	args = append(args, value)
	return "SETEX", args
}
<commit_msg>Implementing subscriber<commit_after>package store

import (
	"encoding/json"

	log "github.com/Sirupsen/logrus"
	"github.com/garyburd/redigo/redis"
	"github.com/hellofresh/janus/pkg/notifier"
)

// RedisStore is the redis store.
type RedisStore struct {
	// The prefix to use for the key.
	Prefix string

	// github.com/garyburd/redigo Pool instance.
	Pool *redis.Pool

	// The maximum number of retry under race conditions.
	MaxRetry int
}

// NewRedisStore returns an instance of redis store.
func NewRedisStore(pool *redis.Pool) (Store, error) {
	return NewRedisStoreWithOptions(pool, Options{
		Prefix: "janus",
	})
}

// NewRedisStoreWithOptions returns an instance of redis store with custom options.
func NewRedisStoreWithOptions(pool *redis.Pool, options Options) (Store, error) {
	store := &RedisStore{
		Pool:   pool,
		Prefix: options.Prefix,
	}

	if _, err := store.ping(); err != nil {
		return nil, err
	}

	return store, nil
}

func (s *RedisStore) getConnection() redis.Conn {
	return s.Pool.Get()
}

// ping checks if redis is alive.
func (s *RedisStore) ping() (bool, error) {
	conn := s.getConnection()
	defer conn.Close()

	data, err := conn.Do("PING")
	if err != nil || data == nil {
		return false, err
	}

	return data == "PONG", nil
}

// Exists checks if a key exists in the store
func (s *RedisStore) Exists(key string) (bool, error) {
	conn := s.getConnection()
	defer conn.Close()

	return s.exists(conn, key)
}

// Get retreives a value from the store
func (s *RedisStore) Get(key string) (string, error) {
	conn := s.getConnection()
	defer conn.Close()

	return s.get(conn, key)
}

// Remove a value from the store
func (s *RedisStore) Remove(key string) error {
	conn := s.getConnection()
	defer conn.Close()

	return s.remove(conn, key)
}

// Set a value in the store
func (s *RedisStore) Set(key string, value string, expire int64) error {
	conn := s.getConnection()
	defer conn.Close()

	return s.set(conn, key, value, expire)
}

// Publish publishes to a topic in redis
func (s *RedisStore) Publish(topic string, data []byte) error {
	c := s.getConnection()
	_, err := c.Do("PUBLISH", topic, data)
	return err
}

// Subscribe subscribes to a topic in redis
func (s *RedisStore) Subscribe(channel string, callback func(notifier.Notification)) error {
	// Get a connection from a pool
	c := s.getConnection()
	defer c.Close()

	psc := redis.PubSubConn{Conn: c}
	if err := psc.Subscribe(channel); err != nil {
		return err
	}

	for {
		switch v := psc.Receive().(type) {
		case redis.Message:
			notif := notifier.Notification{}
			if err := json.Unmarshal(v.Data, &notif); err != nil {
				log.Error("Unmarshalling message body failed, malformed: ", err)
				return err
			}
			callback(notif)
		case error:
			log.WithError(v).Debug("An error ocurred when getting the message")
			return v
		}
	}
}

func (s *RedisStore) exists(conn redis.Conn, key string) (bool, error) {
	exists, err := redis.Bool(conn.Do("EXISTS", key))
	if err != nil {
		return false, err
	}

	return exists, nil
}

func (s *RedisStore) remove(conn redis.Conn, key string) error {
	_, err := conn.Do("DEL", key)
	return err
}

func (s *RedisStore) get(conn redis.Conn, key string) (string, error) {
	return redis.String(conn.Do("GET", key))
}

func (s *RedisStore) set(conn redis.Conn, key string, value string, expire int64) error {
	command, args := getSetCommandAndArgs(key, value, expire)
	if _, err := conn.Do(command, args...); err != nil {
		return err
	}

	return nil
}

func getSetCommandAndArgs(key string, value string, expire int64) (string, []interface{}) {
	var args []interface{}
	if expire == 0 {
		args = append(args, key)
		args = append(args, value)
		return "SET", args
	}

	args = append(args, key)
	args = append(args, expire)
	args = append(args, value)
	return "SETEX", args
}
<|endoftext|>


<commit_before>package store

import (
	"container/list"
	"gob"
	"io"
	"junta/util"
	"math"
	"os"
	"regexp"
	"strings"
)

type Event struct {
	Seqn  uint64
	Path  string
	Body  string

	// the cas token for the key/value pair as of this event.
	// if the operation set a value, `Cas` is `Seqn` in decimal.
	// if the operation deleted a value, `Cas` is `Missing`.
	Cas   string

	// the mutation that caused this event
	Mut   string

	Err   os.Error
}

const (
	Clobber = ""
	Missing = "0"
	Dir = "dir"
)

var (
	BadPathError = os.NewError("bad path")
	BadMutationError = os.NewError("bad mutation")
	TooLateError = os.NewError("too late")
	CasMismatchError = os.NewError("cas mismatch")
)

var waitRegexp = regexp.MustCompile(``)

type Store struct {
	applyCh chan apply
	lookupCh chan *lookup
	snapCh chan chan snap
	watchCh chan watch
	watches []watch
	todo map[uint64]apply
}

type apply struct {
	seqn uint64
	mutation string
}

type lookup struct {
	k string
	ch chan int
	v string
	cas string
}

type snap struct {
	ver uint64
	root node
}

type watch struct {
	pat string
	in, out chan Event
	re *regexp.Regexp
	stop uint64
}

// Creates a new, empty data store. Mutations will be applied in order,
// starting at number 1 (number 0 can be thought of as the creation of the
// store).
func New() *Store {
	s := &Store{
		applyCh: make(chan apply),
		lookupCh: make(chan *lookup),
		snapCh: make(chan chan snap),
		watchCh: make(chan watch),
		todo: make(map[uint64]apply),
		watches: []watch{},
	}
	go s.process()
	return s
}

func split(path string) []string {
	if path == "/" {
		return []string{}
	}
	return strings.Split(path[1:], "/", -1)
}

func join(parts []string) string {
	return "/" + strings.Join(parts, "/")
}

func checkPath(k string) os.Error {
	switch {
	case len(k) < 1,
	     k[0] != '/',
	     len(k) > 1 && k[len(k) - 1] == '/',
	     strings.Count(k, "=") > 0,
	     strings.Count(k, " ") > 0:
		return BadPathError
	}
	return nil
}

func EncodeSet(path, body string, cas string) (mutation string, err os.Error) {
	if err = checkPath(path); err != nil {
		return
	}
	return cas + ":" + path + "=" + body, nil
}

func EncodeDel(path string, cas string) (mutation string, err os.Error) {
	if err := checkPath(path); err != nil {
		return
	}
	return cas + ":" + path, nil
}

// MustEncodeSet is like EncodeSet but panics if the mutation cannot be
// encoded. It simplifies safe initialization of global variables holding
// mutations.
func MustEncodeSet(path, body, cas string) (mutation string) {
	m, err := EncodeSet(path, body, cas)
	if err != nil {
		panic(err)
	}
	return m
}

// MustEncodeDel is like EncodeDel but panics if the mutation cannot be
// encoded. It simplifies safe initialization of global variables holding
// mutations.
func MustEncodeDel(path, cas string) (mutation string) {
	m, err := EncodeDel(path, cas)
	if err != nil {
		panic(err)
	}
	return m
}

func decode(mutation string) (path, v, cas string, keep bool, err os.Error) {
	cm := strings.Split(mutation, ":", 2)

	if len(cm) != 2 {
		err = BadMutationError
		return
	}

	kv := strings.Split(cm[1], "=", 2)

	if err = checkPath(kv[0]); err != nil {
		return
	}

	switch len(kv) {
	case 1:
		return kv[0], "", cm[0], false, nil
	case 2:
		return kv[0], kv[1], cm[0], true, nil
	}
	panic("can't happen")
}

func (s *Store) notify(e Event) {
	for _, w := range s.watches {
		if w.re.MatchString(e.Path) {
			w.in <- e
		}
	}
}

func append(ws *[]watch, w watch) {
	l := len(*ws)
	if l + 1 > cap(*ws) {
		ns := make([]watch, (l + 1)*2)
		copy(ns, *ws)
		*ws = ns
	}
	*ws = (*ws)[0:l + 1]
	(*ws)[l] = w
}

// Unbounded in-order buffering
func buffer(in, out chan Event) {
	list := list.New()
	for {
		f, e := list.Front(), Event{}
		var ch chan Event
		if f != nil {
			e = f.Value.(Event)
			ch = out
		}
		select {
		case x := <-in:
			if closed(in) {
				return
			}
			list.PushBack(x)
		case ch <- e:
			list.Remove(f)
		}
	}
}

func (s *Store) process() {
	logger := util.NewLogger("store")

	ver := uint64(0)
	values := root

	for {
		// Take any incoming requests and queue them up.
		select {
		case a := <-s.applyCh:
			if a.seqn > ver {
				s.todo[a.seqn] = a
			}
		case r := <-s.lookupCh:
			r.v, r.cas = values.getp(r.k)
			r.ch <- 1
		case ch := <-s.snapCh:
			ch <- snap{ver, values}
		case w := <-s.watchCh:
			if w.stop > ver {
				append(&s.watches, w)
			} else {
				w.in <- Event{Seqn:w.stop, Err:TooLateError}
				close(w.in)
			}
		}

		// If we have any mutations that can be applied, do them.
		for t, ok := s.todo[ver+1]; ok; t, ok = s.todo[ver+1] {
			var nver uint64

			d := gob.NewDecoder(strings.NewReader(t.mutation))
			if t.seqn == 1 && d.Decode(&nver) == nil {
				var vx node
				err := d.Decode(&vx)
				if err == nil {
					values = vx
					for i := ver+1; i <= nver; i++ {
						s.todo[i] = apply{}, false
					}
					ver = nver
				} else {
					s.todo[ver+1] = apply{}, false
					ver++
				}
			} else {
				var ev Event
				values, ev = values.apply(t.seqn, t.mutation)
				logger.Logf("%v", ev)
				s.notify(ev)
				s.todo[ver+1] = apply{}, false
				ver++
			}
		}
	}
}

// Applies `mutation` in sequence at position `seqn`. A malformed mutation is
// treated as a no-op. If a mutation has already been applied at this position,
// this one is sliently ignored.
//
// If `mutation` is a snapshot, notifications will not be sent.
func (s *Store) Apply(seqn uint64, mutation string) {
	s.applyCh <- apply{seqn, mutation}
}

// Gets the value stored at `path`, if any. If no value is stored at `path`,
// `ok` is false.
func (s *Store) Lookup(path string) (body string, cas string) {
	l := lookup{k:path, ch:make(chan int)}
	s.lookupCh <- &l
	<-l.ch
	return l.v, l.cas
}

// Encodes the entire storage state, including the current sequence number, as
// a mutation, and writes the mutation to `w`. This mutation can be applied to
// an empty store to reproduce the state of this one.
//
// A snapshot must be applied at sequence number `1`. Once a snapshot has been
// applied, the store's current sequence number will be set to that of the
// snapshot.
//
// Note that applying a snapshot does not send notifications.
func (s *Store) Snapshot(w io.Writer) (err os.Error) {
	ch := make(chan snap)
	s.snapCh <- ch
	ss := <-ch
	err = gob.NewEncoder(w).Encode(ss.ver)
	if err != nil {
		return
	}

	err = gob.NewEncoder(w).Encode(ss.root)
	return
}

// Subscribes `ch` to receive notifications when mutations are applied to paths
// in the store. One event will be sent for each mutation iff the event's path
// matches `pattern`, a Unix-style glob pattern.
//
// Glob notation:
//  - `?` matches a single char in a single path component
//  - `*` matches zero or more chars in a single path component
//  - `**` matches zero or more chars in zero or more components
//
// Notifications will not be sent for changes made as the result of applying a
// snapshot.
func (s *Store) Watch(pattern string, ch chan Event) {
	re, _ := compileGlob(pattern)
	in := make(chan Event)
	go buffer(in, ch)
	s.watchCh <- watch{pat:pattern, out:ch, in:in, re:re, stop:math.MaxUint64}
}

// Waits for `seqn` and sends a single event representing the change made at
// that position.
//
// If `seqn` is in the past, the event's `Err` will be `TooLateError`.
func (s *Store) Wait(seqn uint64, ch chan Event) {
	all := make(chan Event)
	s.watchCh <- watch{in:all, re:waitRegexp, stop:seqn}
	go func() {
		for e := range all {
			if e.Seqn == seqn {
				close(all)
				ch <- e
			}
		}
	}()
}

func (st *Store) Sync(seqn uint64) {
	ch := make(chan Event)
	st.Wait(seqn, ch)
	<-ch
}
<commit_msg>refactor<commit_after>package store

import (
	"container/list"
	"gob"
	"io"
	"junta/util"
	"math"
	"os"
	"regexp"
	"strings"
)

type Event struct {
	Seqn  uint64
	Path  string
	Body  string

	// the cas token for the key/value pair as of this event.
	// if the operation set a value, `Cas` is `Seqn` in decimal.
	// if the operation deleted a value, `Cas` is `Missing`.
	Cas   string

	// the mutation that caused this event
	Mut   string

	Err   os.Error
}

const (
	Clobber = ""
	Missing = "0"
	Dir = "dir"
)

var (
	BadPathError = os.NewError("bad path")
	BadMutationError = os.NewError("bad mutation")
	TooLateError = os.NewError("too late")
	CasMismatchError = os.NewError("cas mismatch")
)

var waitRegexp = regexp.MustCompile(``)

type Store struct {
	applyCh chan apply
	lookupCh chan *lookup
	snapCh chan chan snap
	watchCh chan watch
	watches []watch
	todo map[uint64]apply
}

type apply struct {
	seqn uint64
	mutation string
}

type lookup struct {
	k string
	ch chan int
	v string
	cas string
}

type snap struct {
	ver uint64
	root node
}

type watch struct {
	pat string
	in, out chan Event
	re *regexp.Regexp
	stop uint64
}

// Creates a new, empty data store. Mutations will be applied in order,
// starting at number 1 (number 0 can be thought of as the creation of the
// store).
func New() *Store {
	s := &Store{
		applyCh: make(chan apply),
		lookupCh: make(chan *lookup),
		snapCh: make(chan chan snap),
		watchCh: make(chan watch),
		todo: make(map[uint64]apply),
		watches: []watch{},
	}
	go s.process()
	return s
}

func split(path string) []string {
	if path == "/" {
		return []string{}
	}
	return strings.Split(path[1:], "/", -1)
}

func join(parts []string) string {
	return "/" + strings.Join(parts, "/")
}

func checkPath(k string) os.Error {
	switch {
	case len(k) < 1,
	     k[0] != '/',
	     len(k) > 1 && k[len(k) - 1] == '/',
	     strings.Count(k, "=") > 0,
	     strings.Count(k, " ") > 0:
		return BadPathError
	}
	return nil
}

func EncodeSet(path, body string, cas string) (mutation string, err os.Error) {
	if err = checkPath(path); err != nil {
		return
	}
	return cas + ":" + path + "=" + body, nil
}

func EncodeDel(path string, cas string) (mutation string, err os.Error) {
	if err := checkPath(path); err != nil {
		return
	}
	return cas + ":" + path, nil
}

// MustEncodeSet is like EncodeSet but panics if the mutation cannot be
// encoded. It simplifies safe initialization of global variables holding
// mutations.
func MustEncodeSet(path, body, cas string) (mutation string) {
	m, err := EncodeSet(path, body, cas)
	if err != nil {
		panic(err)
	}
	return m
}

// MustEncodeDel is like EncodeDel but panics if the mutation cannot be
// encoded. It simplifies safe initialization of global variables holding
// mutations.
func MustEncodeDel(path, cas string) (mutation string) {
	m, err := EncodeDel(path, cas)
	if err != nil {
		panic(err)
	}
	return m
}

func decode(mutation string) (path, v, cas string, keep bool, err os.Error) {
	cm := strings.Split(mutation, ":", 2)

	if len(cm) != 2 {
		err = BadMutationError
		return
	}

	kv := strings.Split(cm[1], "=", 2)

	if err = checkPath(kv[0]); err != nil {
		return
	}

	switch len(kv) {
	case 1:
		return kv[0], "", cm[0], false, nil
	case 2:
		return kv[0], kv[1], cm[0], true, nil
	}
	panic("can't happen")
}

func (s *Store) notify(e Event) {
	for _, w := range s.watches {
		if w.re.MatchString(e.Path) {
			w.in <- e
		}
	}
}

func append(ws *[]watch, w watch) {
	l := len(*ws)
	if l + 1 > cap(*ws) {
		ns := make([]watch, (l + 1)*2)
		copy(ns, *ws)
		*ws = ns
	}
	*ws = (*ws)[0:l + 1]
	(*ws)[l] = w
}

// Unbounded in-order buffering
func buffer(in, out chan Event) {
	list := list.New()
	for {
		f, e := list.Front(), Event{}
		var ch chan Event
		if f != nil {
			e = f.Value.(Event)
			ch = out
		}
		select {
		case x := <-in:
			if closed(in) {
				return
			}
			list.PushBack(x)
		case ch <- e:
			list.Remove(f)
		}
	}
}

func (s *Store) process() {
	logger := util.NewLogger("store")

	ver := uint64(0)
	values := root

	for {
		// Take any incoming requests and queue them up.
		select {
		case a := <-s.applyCh:
			if a.seqn > ver {
				s.todo[a.seqn] = a
			}
		case r := <-s.lookupCh:
			r.v, r.cas = values.getp(r.k)
			r.ch <- 1
		case ch := <-s.snapCh:
			ch <- snap{ver, values}
		case w := <-s.watchCh:
			if w.stop > ver {
				append(&s.watches, w)
			} else {
				w.in <- Event{Seqn:w.stop, Err:TooLateError}
				close(w.in)
			}
		}

		// If we have any mutations that can be applied, do them.
		for t, ok := s.todo[ver+1]; ok; t, ok = s.todo[ver+1] {
			var nver uint64

			d := gob.NewDecoder(strings.NewReader(t.mutation))
			if t.seqn == 1 && d.Decode(&nver) == nil {
				var vx node
				err := d.Decode(&vx)
				if err == nil {
					values = vx
				} else {
					nver = ver + 1
				}
			} else {
				var ev Event
				values, ev = values.apply(t.seqn, t.mutation)
				logger.Logf("%v", ev)
				s.notify(ev)
				nver = ev.Seqn
			}
			for i := ver+1; i <= nver; i++ {
				s.todo[i] = apply{}, false
			}
			ver = nver
		}
	}
}

// Applies `mutation` in sequence at position `seqn`. A malformed mutation is
// treated as a no-op. If a mutation has already been applied at this position,
// this one is sliently ignored.
//
// If `mutation` is a snapshot, notifications will not be sent.
func (s *Store) Apply(seqn uint64, mutation string) {
	s.applyCh <- apply{seqn, mutation}
}

// Gets the value stored at `path`, if any. If no value is stored at `path`,
// `ok` is false.
func (s *Store) Lookup(path string) (body string, cas string) {
	l := lookup{k:path, ch:make(chan int)}
	s.lookupCh <- &l
	<-l.ch
	return l.v, l.cas
}

// Encodes the entire storage state, including the current sequence number, as
// a mutation, and writes the mutation to `w`. This mutation can be applied to
// an empty store to reproduce the state of this one.
//
// A snapshot must be applied at sequence number `1`. Once a snapshot has been
// applied, the store's current sequence number will be set to that of the
// snapshot.
//
// Note that applying a snapshot does not send notifications.
func (s *Store) Snapshot(w io.Writer) (err os.Error) {
	ch := make(chan snap)
	s.snapCh <- ch
	ss := <-ch
	err = gob.NewEncoder(w).Encode(ss.ver)
	if err != nil {
		return
	}

	err = gob.NewEncoder(w).Encode(ss.root)
	return
}

// Subscribes `ch` to receive notifications when mutations are applied to paths
// in the store. One event will be sent for each mutation iff the event's path
// matches `pattern`, a Unix-style glob pattern.
//
// Glob notation:
//  - `?` matches a single char in a single path component
//  - `*` matches zero or more chars in a single path component
//  - `**` matches zero or more chars in zero or more components
//
// Notifications will not be sent for changes made as the result of applying a
// snapshot.
func (s *Store) Watch(pattern string, ch chan Event) {
	re, _ := compileGlob(pattern)
	in := make(chan Event)
	go buffer(in, ch)
	s.watchCh <- watch{pat:pattern, out:ch, in:in, re:re, stop:math.MaxUint64}
}

// Waits for `seqn` and sends a single event representing the change made at
// that position.
//
// If `seqn` is in the past, the event's `Err` will be `TooLateError`.
func (s *Store) Wait(seqn uint64, ch chan Event) {
	all := make(chan Event)
	s.watchCh <- watch{in:all, re:waitRegexp, stop:seqn}
	go func() {
		for e := range all {
			if e.Seqn == seqn {
				close(all)
				ch <- e
			}
		}
	}()
}

func (st *Store) Sync(seqn uint64) {
	ch := make(chan Event)
	st.Wait(seqn, ch)
	<-ch
}
<|endoftext|>