<commit_before>package query

import (
	"database/sql"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
)

// Dump returns a SQL text dump of all rows across all tables, similar to
// sqlite3's dump feature
func Dump(tx *sql.Tx, schema string, schemaOnly bool) (string, error) {
	schemas := dumpParseSchema(schema)

	// Begin
	dump := `PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
`
	// Schema table
	tableDump, err := dumpTable(tx, "schema", dumpSchemaTable)
	if err != nil {
		return "", errors.Wrapf(err, "failed to dump table schema")
	}
	dump += tableDump

	// All other tables
	tables := make([]string, 0)
	for table := range schemas {
		tables = append(tables, table)
	}
	sort.Strings(tables)
	for _, table := range tables {
		if schemaOnly {
			// Dump only the schema.
			dump += schemas[table] + "\n"
			continue
		}
		tableDump, err := dumpTable(tx, table, schemas[table])
		if err != nil {
			return "", errors.Wrapf(err, "failed to dump table %s", table)
		}
		dump += tableDump
	}

	// Sequences (unless the schemaOnly flag is true)
	if !schemaOnly {
		tableDump, err = dumpTable(tx, "sqlite_sequence", "DELETE FROM sqlite_sequence;")
		if err != nil {
			return "", errors.Wrapf(err, "failed to dump table sqlite_sequence")
		}
		dump += tableDump
	}

	// Commit
	dump += "COMMIT;\n"

	return dump, nil
}

// Return a map from table names to their schema definition, taking a full
// schema SQL text generated with schema.Schema.Dump().
func dumpParseSchema(schema string) map[string]string {
	tables := map[string]string{}
	for _, statement := range strings.Split(schema, ";") {
		statement = strings.Trim(statement, " \n") + ";"
		if !strings.HasPrefix(statement, "CREATE TABLE") {
			continue
		}
		table := strings.Split(statement, " ")[2]
		tables[table] = statement
	}
	return tables
}

// Dump a single table, returning a SQL text containing statements for its
// schema and data.
func dumpTable(tx *sql.Tx, table, schema string) (string, error) {
	statements := []string{schema}

	// Query all rows.
	rows, err := tx.Query(fmt.Sprintf("SELECT * FROM %s ORDER BY rowid", table))
	if err != nil {
		return "", errors.Wrap(err, "failed to fetch rows")
	}

	// Figure column names
	columns, err := rows.Columns()
	if err != nil {
		return "", errors.Wrap(err, "failed to get columns")
	}

	// Generate an INSERT statement for each row.
	for i := 0; rows.Next(); i++ {
		raw := make([]interface{}, len(columns)) // Raw column values
		row := make([]interface{}, len(columns))
		for i := range raw {
			row[i] = &raw[i]
		}
		err := rows.Scan(row...)
		if err != nil {
			return "", errors.Wrapf(err, "failed to scan row %d")
		}
		values := make([]string, len(columns))
		for j, v := range raw {
			switch v := v.(type) {
			case int64:
				values[j] = strconv.FormatInt(v, 10)
			case string:
				values[j] = fmt.Sprintf("'%s'", v)
			case []byte:
				values[j] = fmt.Sprintf("'%s'", string(v))
			case time.Time:
				values[j] = strconv.FormatInt(v.Unix(), 10)
			default:
				if v != nil {
					return "", fmt.Errorf("bad type in column %s of row %d", columns[j], i)
				}
				values[j] = "NULL"
			}
		}
		statement := fmt.Sprintf("INSERT INTO %s VALUES(%s);", table, strings.Join(values, ","))
		statements = append(statements, statement)
	}
	return strings.Join(statements, "\n") + "\n", nil
}

// Schema of the schema table.
const dumpSchemaTable = `CREATE TABLE schema (
    id         INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    version    INTEGER NOT NULL,
    updated_at DATETIME NOT NULL,
    UNIQUE (version)
);`
<commit_msg>db: fix format string<commit_after>package query

import (
	"database/sql"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
)

// Dump returns a SQL text dump of all rows across all tables, similar to
// sqlite3's dump feature
func Dump(tx *sql.Tx, schema string, schemaOnly bool) (string, error) {
	schemas := dumpParseSchema(schema)

	// Begin
	dump := `PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
`
	// Schema table
	tableDump, err := dumpTable(tx, "schema", dumpSchemaTable)
	if err != nil {
		return "", errors.Wrapf(err, "failed to dump table schema")
	}
	dump += tableDump

	// All other tables
	tables := make([]string, 0)
	for table := range schemas {
		tables = append(tables, table)
	}
	sort.Strings(tables)
	for _, table := range tables {
		if schemaOnly {
			// Dump only the schema.
			dump += schemas[table] + "\n"
			continue
		}
		tableDump, err := dumpTable(tx, table, schemas[table])
		if err != nil {
			return "", errors.Wrapf(err, "failed to dump table %s", table)
		}
		dump += tableDump
	}

	// Sequences (unless the schemaOnly flag is true)
	if !schemaOnly {
		tableDump, err = dumpTable(tx, "sqlite_sequence", "DELETE FROM sqlite_sequence;")
		if err != nil {
			return "", errors.Wrapf(err, "failed to dump table sqlite_sequence")
		}
		dump += tableDump
	}

	// Commit
	dump += "COMMIT;\n"

	return dump, nil
}

// Return a map from table names to their schema definition, taking a full
// schema SQL text generated with schema.Schema.Dump().
func dumpParseSchema(schema string) map[string]string {
	tables := map[string]string{}
	for _, statement := range strings.Split(schema, ";") {
		statement = strings.Trim(statement, " \n") + ";"
		if !strings.HasPrefix(statement, "CREATE TABLE") {
			continue
		}
		table := strings.Split(statement, " ")[2]
		tables[table] = statement
	}
	return tables
}

// Dump a single table, returning a SQL text containing statements for its
// schema and data.
func dumpTable(tx *sql.Tx, table, schema string) (string, error) {
	statements := []string{schema}

	// Query all rows.
	rows, err := tx.Query(fmt.Sprintf("SELECT * FROM %s ORDER BY rowid", table))
	if err != nil {
		return "", errors.Wrap(err, "failed to fetch rows")
	}

	// Figure column names
	columns, err := rows.Columns()
	if err != nil {
		return "", errors.Wrap(err, "failed to get columns")
	}

	// Generate an INSERT statement for each row.
	for i := 0; rows.Next(); i++ {
		raw := make([]interface{}, len(columns)) // Raw column values
		row := make([]interface{}, len(columns))
		for i := range raw {
			row[i] = &raw[i]
		}
		err := rows.Scan(row...)
		if err != nil {
			return "", errors.Wrapf(err, "failed to scan row %d", i)
		}
		values := make([]string, len(columns))
		for j, v := range raw {
			switch v := v.(type) {
			case int64:
				values[j] = strconv.FormatInt(v, 10)
			case string:
				values[j] = fmt.Sprintf("'%s'", v)
			case []byte:
				values[j] = fmt.Sprintf("'%s'", string(v))
			case time.Time:
				values[j] = strconv.FormatInt(v.Unix(), 10)
			default:
				if v != nil {
					return "", fmt.Errorf("bad type in column %s of row %d", columns[j], i)
				}
				values[j] = "NULL"
			}
		}
		statement := fmt.Sprintf("INSERT INTO %s VALUES(%s);", table, strings.Join(values, ","))
		statements = append(statements, statement)
	}
	return strings.Join(statements, "\n") + "\n", nil
}

// Schema of the schema table.
const dumpSchemaTable = `CREATE TABLE schema (
    id         INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    version    INTEGER NOT NULL,
    updated_at DATETIME NOT NULL,
    UNIQUE (version)
);`
<|endoftext|>


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

package berglas

import (
	"context"
	"encoding/base64"
	"fmt"
	"net/http"

	"cloud.google.com/go/storage"
	"github.com/pkg/errors"
	"google.golang.org/api/googleapi"
	kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
)

// encryptAndWrite is a low-level function for encrypting and writing data.
func (c *Client) encryptAndWrite(
	ctx context.Context, bucket, object, key string, plaintext []byte,
	generation, metageneration int64) (*Secret, error) {

	// Generate a unique DEK and encrypt the plaintext locally (useful for large
	// pieces of data).
	dek, ciphertext, err := envelopeEncrypt(plaintext)
	if err != nil {
		return nil, errors.Wrap(err, "failed to perform envelope encryption")
	}

	// Encrypt the plaintext using a KMS key
	kmsResp, err := c.kmsClient.Encrypt(ctx, &kmspb.EncryptRequest{
		Name:                        key,
		Plaintext:                   dek,
		AdditionalAuthenticatedData: []byte(object),
	})
	if err != nil {
		return nil, errors.Wrap(err, "failed to encrypt secret")
	}
	encDEK := kmsResp.Ciphertext

	// Build the storage object contents. Contents will be of the format:
	//
	//    b64(kms_encrypted_dek):b64(dek_encrypted_plaintext)
	blob := fmt.Sprintf("%s:%s",
		base64.StdEncoding.EncodeToString(encDEK),
		base64.StdEncoding.EncodeToString(ciphertext))

	// If generation and metageneration are 0, then we should only create the
	// object if it does not exist. Otherwise, we should only perform an update if
	// the metagenerations match.
	var conds storage.Conditions
	if generation == 0 || metageneration == 0 {
		conds = storage.Conditions{
			DoesNotExist: true,
		}
	} else {
		conds = storage.Conditions{
			GenerationMatch:     generation,
			MetagenerationMatch: metageneration,
		}
	}

	// Create the writer
	iow := c.storageClient.
		Bucket(bucket).
		Object(object).
		If(conds).
		NewWriter(ctx)

	iow.ObjectAttrs.CacheControl = CacheControl
	iow.ChunkSize = ChunkSize

	if iow.Metadata == nil {
		iow.Metadata = make(map[string]string)
	}
	iow.Metadata[MetadataIDKey] = "1"
	iow.Metadata[MetadataKMSKey] = kmsKeyTrimVersion(key)

	// Write
	if _, err := iow.Write([]byte(blob)); err != nil {
		return nil, errors.Wrap(err, "failed to save encrypted ciphertext to storage")
	}

	// Close and flush
	if err := iow.Close(); err != nil {
		if terr, ok := err.(*googleapi.Error); ok {
			switch terr.Code {
			case http.StatusNotFound:
				return nil, errors.New("bucket does not exist")
			case http.StatusPreconditionFailed:
				if conds.DoesNotExist {
					return nil, errSecretAlreadyExists
				}
				return nil, errSecretModified
			default:
				return nil, errors.Wrap(err, "Writer.Close: googleapi.Error: failed to finalise bucket creation")
			}
		} else {
			return nil, errors.Wrap(err, "Writer.Close: failed to finalise bucket creation")
		}
	}

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

package berglas

import (
	"context"
	"encoding/base64"
	"fmt"
	"net/http"

	"cloud.google.com/go/storage"
	"github.com/pkg/errors"
	"google.golang.org/api/googleapi"
	kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
)

// encryptAndWrite is a low-level function for encrypting and writing data.
func (c *Client) encryptAndWrite(
	ctx context.Context, bucket, object, key string, plaintext []byte,
	generation, metageneration int64) (*Secret, error) {

	// Generate a unique DEK and encrypt the plaintext locally (useful for large
	// pieces of data).
	dek, ciphertext, err := envelopeEncrypt(plaintext)
	if err != nil {
		return nil, errors.Wrap(err, "failed to perform envelope encryption")
	}

	// Encrypt the plaintext using a KMS key
	kmsResp, err := c.kmsClient.Encrypt(ctx, &kmspb.EncryptRequest{
		Name:                        key,
		Plaintext:                   dek,
		AdditionalAuthenticatedData: []byte(object),
	})
	if err != nil {
		return nil, errors.Wrap(err, "failed to encrypt secret")
	}
	encDEK := kmsResp.Ciphertext

	// Build the storage object contents. Contents will be of the format:
	//
	//    b64(kms_encrypted_dek):b64(dek_encrypted_plaintext)
	blob := fmt.Sprintf("%s:%s",
		base64.StdEncoding.EncodeToString(encDEK),
		base64.StdEncoding.EncodeToString(ciphertext))

	// If generation and metageneration are 0, then we should only create the
	// object if it does not exist. Otherwise, we should only perform an update if
	// the metagenerations match.
	var conds storage.Conditions
	if generation == 0 || metageneration == 0 {
		conds = storage.Conditions{
			DoesNotExist: true,
		}
	} else {
		conds = storage.Conditions{
			GenerationMatch:     generation,
			MetagenerationMatch: metageneration,
		}
	}

	// Create the writer
	iow := c.storageClient.
		Bucket(bucket).
		Object(object).
		If(conds).
		NewWriter(ctx)

	iow.ObjectAttrs.CacheControl = CacheControl
	iow.ChunkSize = ChunkSize

	if iow.Metadata == nil {
		iow.Metadata = make(map[string]string)
	}
	iow.Metadata[MetadataIDKey] = "1"
	iow.Metadata[MetadataKMSKey] = kmsKeyTrimVersion(key)

	// Write
	if _, err := iow.Write([]byte(blob)); err != nil {
		return nil, errors.Wrap(err, "failed to save encrypted ciphertext to storage")
	}

	// Close and flush
	if err := iow.Close(); err != nil {
		if terr, ok := err.(*googleapi.Error); ok {
			switch terr.Code {
			case http.StatusNotFound:
				return nil, errors.New("bucket does not exist")
			case http.StatusPreconditionFailed:
				if conds.DoesNotExist {
					return nil, errSecretAlreadyExists
				}
				return nil, errSecretModified
			}
		}

		return nil, errors.Wrap(err, "failed to write to bucket")
	}

	return secretFromAttrs(iow.Attrs(), plaintext), nil
}
<|endoftext|>


<commit_before>// Copyright 2014 The Go 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 main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
	"time"
	"unicode/utf8"
)

func TestExactly16Bytes(t *testing.T) {
	var tests = []string{
		"",
		"a",
		"日本語",
		"1234567890123456",
		"12345678901234567890",
		"1234567890123本語4567890",
		"12345678901234日本語567890",
		"123456789012345日本語67890",
		"1234567890123456日本語7890",
		"1234567890123456日本語7日本語890",
	}
	for _, str := range tests {
		got := exactly16Bytes(str)
		if len(got) != 16 {
			t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got))
		}
		// Make sure it is full runes.
		for _, c := range got {
			if c == utf8.RuneError {
				t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got)
			}
		}
	}
}

// tmpDir creates a temporary directory and returns its name.
func tmpDir(t *testing.T) string {
	name, err := ioutil.TempDir("", "pack")
	if err != nil {
		t.Fatal(err)
	}
	return name
}

// Test that we can create an archive, write to it, and get the same contents back.
// Tests the rv and then the pv command on a new archive.
func TestCreate(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add an entry by hand.
	ar.addFile(helloFile.Reset())
	ar.fd.Close()
	// Now check it.
	ar = archive(name, os.O_RDONLY, []string{helloFile.name})
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.printContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose output plus file contents.
	expect := fmt.Sprintf("%s\n%s", helloFile.name, helloFile.contents)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a correct listing.
// Tests the tv command.
func TestTableOfContents(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()
	// Now print it.
	ar = archive(name, os.O_RDONLY, nil)
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose listing.
	expect := fmt.Sprintf("%s\n%s\n", helloFile.Entry(), goodbyeFile.Entry())
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
	// Do it again without verbose.
	verbose = false
	buf.Reset()
	ar = archive(name, os.O_RDONLY, nil)
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result = buf.String()
	// Expect non-verbose listing.
	expect = fmt.Sprintf("%s\n%s\n", helloFile.name, goodbyeFile.name)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a file.
// Tests the x command.
func TestExtract(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()
	// Now extract one file. We chdir to the directory of the archive for simplicity.
	pwd, err := os.Getwd()
	if err != nil {
		t.Fatal("os.Getwd: ", err)
	}
	err = os.Chdir(dir)
	if err != nil {
		t.Fatal("os.Chdir: ", err)
	}
	defer func() {
		err := os.Chdir(pwd)
		if err != nil {
			t.Fatal("os.Chdir: ", err)
		}
	}()
	ar = archive(name, os.O_RDONLY, []string{goodbyeFile.name})
	ar.scan(ar.extractContents)
	ar.fd.Close()
	data, err := ioutil.ReadFile(goodbyeFile.name)
	if err != nil {
		t.Fatal(err)
	}
	// Expect contents of file.
	result := string(data)
	expect := goodbyeFile.contents
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that pack-created archives can be understood by the tools.
func TestHello(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	hello := filepath.Join(dir, "hello.go")
	prog := `
		package main
		func main() {
			println("hello world")
		}
	`
	err := ioutil.WriteFile(hello, []byte(prog), 0666)
	if err != nil {
		t.Fatal(err)
	}

	run := func(args ...string) string {
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Dir = dir
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("%v: %v\n%s", args, err, string(out))
		}
		return string(out)
	}

	out := run("go", "env")
	i := strings.Index(out, "GOCHAR=\"")
	if i < 0 {
		t.Fatal("cannot find GOCHAR in 'go env' output")
	}
	char := out[i+8 : i+9]
	run("go", "build", "cmd/pack") // writes pack binary to dir
	run("go", "tool", char+"g", "hello.go")
	run("./pack", "grc", "hello.a", "hello."+char)
	run("go", "tool", char+"l", "-o", "a.out", "hello.a")
	out = run("./a.out")
	if out != "hello world\n" {
		t.Fatal("incorrect output: %q, want %q", out, "hello world\n")
	}
}

// Fake implementation of files.

var helloFile = &FakeFile{
	name:     "hello",
	contents: "hello world", // 11 bytes, an odd number.
	mode:     0644,
}

var goodbyeFile = &FakeFile{
	name:     "goodbye",
	contents: "Sayonara, Jim", // 13 bytes, another odd number.
	mode:     0644,
}

// FakeFile implements FileLike and also os.FileInfo.
type FakeFile struct {
	name     string
	contents string
	mode     os.FileMode
	offset   int
}

// Reset prepares a FakeFile for reuse.
func (f *FakeFile) Reset() *FakeFile {
	f.offset = 0
	return f
}

// FileLike methods.

func (f *FakeFile) Name() string {
	// A bit of a cheat: we only have a basename, so that's also ok for FileInfo.
	return f.name
}

func (f *FakeFile) Stat() (os.FileInfo, error) {
	return f, nil
}

func (f *FakeFile) Read(p []byte) (int, error) {
	if f.offset >= len(f.contents) {
		return 0, io.EOF
	}
	n := copy(p, f.contents[f.offset:])
	f.offset += n
	return n, nil
}

func (f *FakeFile) Close() error {
	return nil
}

// os.FileInfo methods.

func (f *FakeFile) Size() int64 {
	return int64(len(f.contents))
}

func (f *FakeFile) Mode() os.FileMode {
	return f.mode
}

func (f *FakeFile) ModTime() time.Time {
	return time.Time{}
}

func (f *FakeFile) IsDir() bool {
	return false
}

func (f *FakeFile) Sys() interface{} {
	return nil
}

// Special helpers.

func (f *FakeFile) Entry() *Entry {
	return &Entry{
		name:  f.name,
		mtime: 0, // Defined to be zero.
		uid:   0, // Ditto.
		gid:   0, // Ditto.
		mode:  f.mode,
		size:  int64(len(f.contents)),
	}
}
<commit_msg>cmd/pack: dump output of command of "go env" command in test Get more information to help understand build failure on Plan 9. Also Windows. (TestHello is failing because GOCHAR does not appear in output. What does?)<commit_after>// Copyright 2014 The Go 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 main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
	"time"
	"unicode/utf8"
)

func TestExactly16Bytes(t *testing.T) {
	var tests = []string{
		"",
		"a",
		"日本語",
		"1234567890123456",
		"12345678901234567890",
		"1234567890123本語4567890",
		"12345678901234日本語567890",
		"123456789012345日本語67890",
		"1234567890123456日本語7890",
		"1234567890123456日本語7日本語890",
	}
	for _, str := range tests {
		got := exactly16Bytes(str)
		if len(got) != 16 {
			t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got))
		}
		// Make sure it is full runes.
		for _, c := range got {
			if c == utf8.RuneError {
				t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got)
			}
		}
	}
}

// tmpDir creates a temporary directory and returns its name.
func tmpDir(t *testing.T) string {
	name, err := ioutil.TempDir("", "pack")
	if err != nil {
		t.Fatal(err)
	}
	return name
}

// Test that we can create an archive, write to it, and get the same contents back.
// Tests the rv and then the pv command on a new archive.
func TestCreate(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add an entry by hand.
	ar.addFile(helloFile.Reset())
	ar.fd.Close()
	// Now check it.
	ar = archive(name, os.O_RDONLY, []string{helloFile.name})
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.printContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose output plus file contents.
	expect := fmt.Sprintf("%s\n%s", helloFile.name, helloFile.contents)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a correct listing.
// Tests the tv command.
func TestTableOfContents(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()
	// Now print it.
	ar = archive(name, os.O_RDONLY, nil)
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose listing.
	expect := fmt.Sprintf("%s\n%s\n", helloFile.Entry(), goodbyeFile.Entry())
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
	// Do it again without verbose.
	verbose = false
	buf.Reset()
	ar = archive(name, os.O_RDONLY, nil)
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result = buf.String()
	// Expect non-verbose listing.
	expect = fmt.Sprintf("%s\n%s\n", helloFile.name, goodbyeFile.name)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a file.
// Tests the x command.
func TestExtract(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()
	// Now extract one file. We chdir to the directory of the archive for simplicity.
	pwd, err := os.Getwd()
	if err != nil {
		t.Fatal("os.Getwd: ", err)
	}
	err = os.Chdir(dir)
	if err != nil {
		t.Fatal("os.Chdir: ", err)
	}
	defer func() {
		err := os.Chdir(pwd)
		if err != nil {
			t.Fatal("os.Chdir: ", err)
		}
	}()
	ar = archive(name, os.O_RDONLY, []string{goodbyeFile.name})
	ar.scan(ar.extractContents)
	ar.fd.Close()
	data, err := ioutil.ReadFile(goodbyeFile.name)
	if err != nil {
		t.Fatal(err)
	}
	// Expect contents of file.
	result := string(data)
	expect := goodbyeFile.contents
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that pack-created archives can be understood by the tools.
func TestHello(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	hello := filepath.Join(dir, "hello.go")
	prog := `
		package main
		func main() {
			println("hello world")
		}
	`
	err := ioutil.WriteFile(hello, []byte(prog), 0666)
	if err != nil {
		t.Fatal(err)
	}

	run := func(args ...string) string {
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Dir = dir
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("%v: %v\n%s", args, err, string(out))
		}
		return string(out)
	}

	out := run("go", "env")
	i := strings.Index(out, "GOCHAR=\"")
	if i < 0 {
		t.Fatal("cannot find GOCHAR in 'go env' output:\n", out)
	}
	char := out[i+8 : i+9]
	run("go", "build", "cmd/pack") // writes pack binary to dir
	run("go", "tool", char+"g", "hello.go")
	run("./pack", "grc", "hello.a", "hello."+char)
	run("go", "tool", char+"l", "-o", "a.out", "hello.a")
	out = run("./a.out")
	if out != "hello world\n" {
		t.Fatal("incorrect output: %q, want %q", out, "hello world\n")
	}
}

// Fake implementation of files.

var helloFile = &FakeFile{
	name:     "hello",
	contents: "hello world", // 11 bytes, an odd number.
	mode:     0644,
}

var goodbyeFile = &FakeFile{
	name:     "goodbye",
	contents: "Sayonara, Jim", // 13 bytes, another odd number.
	mode:     0644,
}

// FakeFile implements FileLike and also os.FileInfo.
type FakeFile struct {
	name     string
	contents string
	mode     os.FileMode
	offset   int
}

// Reset prepares a FakeFile for reuse.
func (f *FakeFile) Reset() *FakeFile {
	f.offset = 0
	return f
}

// FileLike methods.

func (f *FakeFile) Name() string {
	// A bit of a cheat: we only have a basename, so that's also ok for FileInfo.
	return f.name
}

func (f *FakeFile) Stat() (os.FileInfo, error) {
	return f, nil
}

func (f *FakeFile) Read(p []byte) (int, error) {
	if f.offset >= len(f.contents) {
		return 0, io.EOF
	}
	n := copy(p, f.contents[f.offset:])
	f.offset += n
	return n, nil
}

func (f *FakeFile) Close() error {
	return nil
}

// os.FileInfo methods.

func (f *FakeFile) Size() int64 {
	return int64(len(f.contents))
}

func (f *FakeFile) Mode() os.FileMode {
	return f.mode
}

func (f *FakeFile) ModTime() time.Time {
	return time.Time{}
}

func (f *FakeFile) IsDir() bool {
	return false
}

func (f *FakeFile) Sys() interface{} {
	return nil
}

// Special helpers.

func (f *FakeFile) Entry() *Entry {
	return &Entry{
		name:  f.name,
		mtime: 0, // Defined to be zero.
		uid:   0, // Ditto.
		gid:   0, // Ditto.
		mode:  f.mode,
		size:  int64(len(f.contents)),
	}
}
<|endoftext|>


<commit_before>// Copyright 2016-2017 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 bpfdebug

import (
	"fmt"

	"github.com/cilium/cilium/common"
)

// Must be synchronized with <bpf/lib/common.h>
const (
	MessageTypeUnspec = iota
	MessageTypeDrop
	MessageTypeDebug
	MessageTypeCapture
)

// must be in sync with <bpf/lib/dbg.h>
const (
	DbgCaptureUnspec = iota
	DbgCaptureFromLxc
	DbgCaptureFromNetdev
	DbgCaptureFromOverlay
	DbgCaptureDelivery
	DbgCaptureFromLb
	DbgCaptureAfterV46
	DbgCaptureAfterV64
	DbgCaptureProxyPre
	DbgCaptureProxyPost
)

// must be in sync with <bpf/lib/dbg.h>
const (
	DbgUnspec = iota
	DbgGeneric
	DbgLocalDelivery
	DbgEncap
	DbgLxcFound
	DbgPolicyDenied
	DbgCtLookup
	DbgCtMatch
	DbgCtCreated
	DbgCtCreated2
	DbgIcmp6Handle
	DbgIcmp6Request
	DbgIcmp6Ns
	DbgIcmp6TimeExceeded
	DbgCtVerdict
	DbgDecap
	DbgPortMap
	DbgErrorRet
	DbgToHost
	DbgToStack
	DbgPktHash
	DbgLb6LookupMaster
	DbgLb6LookupMasterFail
	DbgLb6LookupSlave
	DbgLb6LookupSlaveSuccess
	DbgLb6ReverseNatLookup
	DbgLb6ReverseNat
	DbgLb4LookupMaster
	DbgLb4LookupMasterFail
	DbgLb4LookupSlave
	DbgLb4LookupSlaveSuccess
	DbgLb4ReverseNatLookup
	DbgLb4ReverseNat
	DbgLb4LoopbackSnat
	DbgLb4LoopbackSnatRev
	DbgCtLookup4
	DbgRRSlaveSel
	DbgRevProxyLookup
	DbgRevProxyFound
	DbgRevProxyUpdate
	DbgL4Policy
)

// must be in sync with <bpf/lib/conntrack.h>
const (
	CtNew uint32 = iota
	CtEstablished
	CtReply
	CtRelated
)

var ctStateText = map[uint32]string{
	CtNew:         "New",
	CtEstablished: "Established",
	CtReply:       "Reply",
	CtRelated:     "Related",
}

const (
	ctEgress  = 0
	ctIngress = 1
)

var ctDirection = map[int]string{
	ctEgress:  "egress",
	ctIngress: "ingress",
}

func ctState(state uint32) string {
	txt, ok := ctStateText[state]
	if ok {
		return txt
	}

	return dropReason(uint8(state))
}

func ctInfo(arg1 uint32, arg2 uint32) string {
	return fmt.Sprintf("sport=%d dport=%d nexthdr=%d flags=%d",
		arg1>>16, arg1&0xFFFF, arg2>>8, arg2&0xFF)
}

func proxyInfo(arg1 uint32, arg2 uint32) string {
	sport := common.Swab16(uint16(arg1 >> 16))
	dport := common.Swab16(uint16(arg1 & 0xFFFF))
	return fmt.Sprintf("sport=%d dport=%d saddr=%x", sport, dport, arg2)
}

// DebugMsg is the message format of the debug message found in the BPF ring buffer
type DebugMsg struct {
	Type    uint8
	SubType uint8
	Source  uint16
	Hash    uint32
	Arg1    uint32
	Arg2    uint32
}

// Dump prints the debug message in a human readable format.
func (n *DebugMsg) Dump(data []byte, prefix string) {
	fmt.Printf("%s MARK %#x FROM %d DEBUG: ", prefix, n.Hash, n.Source)
	switch n.SubType {
	case DbgGeneric:
		fmt.Printf("No message, arg1=%d (%#x) arg2=%d (%#x)\n", n.Arg1, n.Arg1, n.Arg2, n.Arg2)
	case DbgLocalDelivery:
		fmt.Printf("Attempting local delivery for container id %d from seclabel %d\n", n.Arg1, n.Arg2)
	case DbgEncap:
		fmt.Printf("Encapsulating to node %d (%#x) from seclabel %d\n", n.Arg1, n.Arg1, n.Arg2)
	case DbgLxcFound:
		fmt.Printf("Local container found ifindex %d seclabel %d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgPolicyDenied:
		fmt.Printf("Policy evaluation would deny packet from %d to %d\n", n.Arg1, n.Arg2)
	case DbgCtLookup:
		fmt.Printf("CT lookup: %s\n", ctInfo(n.Arg1, n.Arg2))
	case DbgCtLookup4:
		fmt.Printf("CT lookup address: %x\n", n.Arg1)
	case DbgCtMatch:
		fmt.Printf("CT entry found lifetime=%d, revnat=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgCtCreated:
		fmt.Printf("CT created 1/2: %s\n", ctInfo(n.Arg1, n.Arg2))
	case DbgCtCreated2:
		fmt.Printf("CT created 2/2: %x revnat=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgCtVerdict:
		fmt.Printf("CT verdict: %s\n", ctState(n.Arg1))
	case DbgIcmp6Handle:
		fmt.Printf("Handling ICMPv6 type=%d\n", n.Arg1)
	case DbgIcmp6Request:
		fmt.Printf("ICMPv6 echo request for router offset=%d\n", n.Arg1)
	case DbgIcmp6Ns:
		fmt.Printf("ICMPv6 neighbour soliciation for address %x:%x\n", n.Arg1, n.Arg2)
	case DbgIcmp6TimeExceeded:
		fmt.Printf("Sending ICMPv6 time exceeded\n")
	case DbgDecap:
		fmt.Printf("Tunnel decap: id=%d flowlabel=%x\n", n.Arg1, n.Arg2)
	case DbgPortMap:
		fmt.Printf("Mapping port from=%d to=%d\n", n.Arg1, n.Arg2)
	case DbgErrorRet:
		fmt.Printf("BPF function %d returned error %d\n", n.Arg1, n.Arg2)
	case DbgToHost:
		fmt.Printf("Going to host, policy-skip=%d\n", n.Arg1)
	case DbgToStack:
		fmt.Printf("Going to the stack, policy-skip=%d\n", n.Arg1)
	case DbgPktHash:
		fmt.Printf("Packet hash=%d (%#x), selected_service=%d\n", n.Arg1, n.Arg1, n.Arg2)
	case DbgRRSlaveSel:
		fmt.Printf("RR slave selection hash=%d (%#x), selected_service=%d\n", n.Arg1, n.Arg1, n.Arg2)
	case DbgLb6LookupMaster:
		fmt.Printf("Master service lookup, addr.p4=%x key.dport=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgLb6LookupMasterFail:
		fmt.Printf("Master service lookup failed, addr.p2=%x addr.p3=%x\n", n.Arg1, n.Arg2)
	case DbgLb6LookupSlave, DbgLb4LookupSlave:
		fmt.Printf("Slave service lookup: slave=%d, dport=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgLb6LookupSlaveSuccess:
		fmt.Printf("Slave service lookup result: target.p4=%x port=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgLb6ReverseNatLookup, DbgLb4ReverseNatLookup:
		fmt.Printf("Reverse NAT lookup, index=%d\n", common.Swab16(uint16(n.Arg1)))
	case DbgLb6ReverseNat:
		fmt.Printf("Performing reverse NAT, address.p4=%x port=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgLb4LookupMaster:
		fmt.Printf("Master service lookup, addr=%x key.dport=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgLb4LookupMasterFail:
		fmt.Printf("Master service lookup failed\n")
	case DbgLb4LookupSlaveSuccess:
		fmt.Printf("Slave service lookup result: target=%x port=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgLb4ReverseNat:
		fmt.Printf("Performing reverse NAT, address=%x port=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgLb4LoopbackSnat:
		fmt.Printf("Loopback SNAT from=%x to=%x\n", n.Arg1, n.Arg2)
	case DbgLb4LoopbackSnatRev:
		fmt.Printf("Loopback reverse SNAT from=%x to=%x\n", n.Arg1, n.Arg2)
	case DbgRevProxyLookup:
		fmt.Printf("Reverse proxy lookup, %s\n", proxyInfo(n.Arg1, n.Arg2))
	case DbgRevProxyFound:
		fmt.Printf("Reverse proxy entry found, orig-daddr=%x orig-dport=%d\n", n.Arg1, n.Arg2)
	case DbgRevProxyUpdate:
		fmt.Printf("Reverse proxy updated, %s\n", proxyInfo(n.Arg1, n.Arg2))
	case DbgL4Policy:
		fmt.Printf("Resolved L4 policy to: %d / %s\n",
			common.Swab16(uint16(n.Arg1)), ctDirection[int(n.Arg2)])
	default:
		fmt.Printf("Unknown message type=%d arg1=%d arg2=%d\n", n.SubType, n.Arg1, n.Arg2)
	}
}

const (
	// DebugCaptureLen is the amount of packet data in a packet capture message
	DebugCaptureLen = 20
)

// DebugCapture is the metadata sent along with a captured packet frame
type DebugCapture struct {
	Type    uint8
	SubType uint8
	Source  uint16
	Hash    uint32
	Len     uint32
	OrigLen uint32
	Arg1    uint32
	// data
}

// Dump prints the captured packet in human readable format
func (n *DebugCapture) Dump(dissect bool, data []byte, prefix string) {
	fmt.Printf("%s MARK %#x FROM %d DEBUG: %d bytes ", prefix, n.Hash, n.Source, n.Len)
	switch n.SubType {
	case DbgCaptureFromLxc:
		fmt.Printf("Incoming packet from container ifindex %d\n", n.Arg1)
	case DbgCaptureFromNetdev:
		fmt.Printf("Incoming packet from netdev ifindex %d\n", n.Arg1)
	case DbgCaptureFromOverlay:
		fmt.Printf("Incoming packet from overlay ifindex %d\n", n.Arg1)
	case DbgCaptureDelivery:
		fmt.Printf("Delivery to ifindex %d\n", n.Arg1)
	case DbgCaptureFromLb:
		fmt.Printf("Incoming packet to load balancer on ifindex %d\n", n.Arg1)
	case DbgCaptureAfterV46:
		fmt.Printf("Packet after nat46 ifindex %d\n", n.Arg1)
	case DbgCaptureAfterV64:
		fmt.Printf("Packet after nat64 ifindex %d\n", n.Arg1)
	case DbgCaptureProxyPre:
		fmt.Printf("Packet to proxy port %d (Pre)\n", common.Swab16(uint16(n.Arg1)))
	case DbgCaptureProxyPost:
		fmt.Printf("Packet to proxy port %d (Post)\n", common.Swab16(uint16(n.Arg1)))
	default:
		fmt.Printf("Unknown message type=%d arg1=%d\n", n.SubType, n.Arg1)
	}

	if n.Len > 0 && len(data) > DebugCaptureLen {
		Dissect(dissect, data[DebugCaptureLen:])
	}
}
<commit_msg>pkg/bpfdebug: Pretty print IP addresses<commit_after>// Copyright 2016-2017 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 bpfdebug

import (
	"encoding/binary"
	"fmt"
	"net"

	"github.com/cilium/cilium/common"
)

// Must be synchronized with <bpf/lib/common.h>
const (
	MessageTypeUnspec = iota
	MessageTypeDrop
	MessageTypeDebug
	MessageTypeCapture
)

// must be in sync with <bpf/lib/dbg.h>
const (
	DbgCaptureUnspec = iota
	DbgCaptureFromLxc
	DbgCaptureFromNetdev
	DbgCaptureFromOverlay
	DbgCaptureDelivery
	DbgCaptureFromLb
	DbgCaptureAfterV46
	DbgCaptureAfterV64
	DbgCaptureProxyPre
	DbgCaptureProxyPost
)

// must be in sync with <bpf/lib/dbg.h>
const (
	DbgUnspec = iota
	DbgGeneric
	DbgLocalDelivery
	DbgEncap
	DbgLxcFound
	DbgPolicyDenied
	DbgCtLookup
	DbgCtMatch
	DbgCtCreated
	DbgCtCreated2
	DbgIcmp6Handle
	DbgIcmp6Request
	DbgIcmp6Ns
	DbgIcmp6TimeExceeded
	DbgCtVerdict
	DbgDecap
	DbgPortMap
	DbgErrorRet
	DbgToHost
	DbgToStack
	DbgPktHash
	DbgLb6LookupMaster
	DbgLb6LookupMasterFail
	DbgLb6LookupSlave
	DbgLb6LookupSlaveSuccess
	DbgLb6ReverseNatLookup
	DbgLb6ReverseNat
	DbgLb4LookupMaster
	DbgLb4LookupMasterFail
	DbgLb4LookupSlave
	DbgLb4LookupSlaveSuccess
	DbgLb4ReverseNatLookup
	DbgLb4ReverseNat
	DbgLb4LoopbackSnat
	DbgLb4LoopbackSnatRev
	DbgCtLookup4
	DbgRRSlaveSel
	DbgRevProxyLookup
	DbgRevProxyFound
	DbgRevProxyUpdate
	DbgL4Policy
)

// must be in sync with <bpf/lib/conntrack.h>
const (
	CtNew uint32 = iota
	CtEstablished
	CtReply
	CtRelated
)

var ctStateText = map[uint32]string{
	CtNew:         "New",
	CtEstablished: "Established",
	CtReply:       "Reply",
	CtRelated:     "Related",
}

const (
	ctEgress  = 0
	ctIngress = 1
)

var ctDirection = map[int]string{
	ctEgress:  "egress",
	ctIngress: "ingress",
}

func ctState(state uint32) string {
	txt, ok := ctStateText[state]
	if ok {
		return txt
	}

	return dropReason(uint8(state))
}

func ctInfo(arg1 uint32, arg2 uint32) string {
	return fmt.Sprintf("sport=%d dport=%d nexthdr=%d flags=%d",
		arg1>>16, arg1&0xFFFF, arg2>>8, arg2&0xFF)
}

func proxyInfo(arg1 uint32, arg2 uint32) string {
	sport := common.Swab16(uint16(arg1 >> 16))
	dport := common.Swab16(uint16(arg1 & 0xFFFF))
	return fmt.Sprintf("sport=%d dport=%d saddr=%s", sport, dport, ip4Str(arg2))
}

func ip4Str(arg1 uint32) string {
	ip := make(net.IP, 4)
	binary.LittleEndian.PutUint32(ip, arg1)
	return ip.String()

}

// DebugMsg is the message format of the debug message found in the BPF ring buffer
type DebugMsg struct {
	Type    uint8
	SubType uint8
	Source  uint16
	Hash    uint32
	Arg1    uint32
	Arg2    uint32
}

// Dump prints the debug message in a human readable format.
func (n *DebugMsg) Dump(data []byte, prefix string) {
	fmt.Printf("%s MARK %#x FROM %d DEBUG: ", prefix, n.Hash, n.Source)
	switch n.SubType {
	case DbgGeneric:
		fmt.Printf("No message, arg1=%d (%#x) arg2=%d (%#x)\n", n.Arg1, n.Arg1, n.Arg2, n.Arg2)
	case DbgLocalDelivery:
		fmt.Printf("Attempting local delivery for container id %d from seclabel %d\n", n.Arg1, n.Arg2)
	case DbgEncap:
		fmt.Printf("Encapsulating to node %d (%#x) from seclabel %d\n", n.Arg1, n.Arg1, n.Arg2)
	case DbgLxcFound:
		fmt.Printf("Local container found ifindex %d seclabel %d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgPolicyDenied:
		fmt.Printf("Policy evaluation would deny packet from %d to %d\n", n.Arg1, n.Arg2)
	case DbgCtLookup:
		fmt.Printf("CT lookup: %s\n", ctInfo(n.Arg1, n.Arg2))
	case DbgCtLookup4:
		fmt.Printf("CT lookup address: %s\n", ip4Str(n.Arg1))
	case DbgCtMatch:
		fmt.Printf("CT entry found lifetime=%d, revnat=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgCtCreated:
		fmt.Printf("CT created 1/2: %s\n", ctInfo(n.Arg1, n.Arg2))
	case DbgCtCreated2:
		fmt.Printf("CT created 2/2: %s revnat=%d\n", ip4Str(n.Arg1), common.Swab16(uint16(n.Arg2)))
	case DbgCtVerdict:
		fmt.Printf("CT verdict: %s\n", ctState(n.Arg1))
	case DbgIcmp6Handle:
		fmt.Printf("Handling ICMPv6 type=%d\n", n.Arg1)
	case DbgIcmp6Request:
		fmt.Printf("ICMPv6 echo request for router offset=%d\n", n.Arg1)
	case DbgIcmp6Ns:
		fmt.Printf("ICMPv6 neighbour soliciation for address %x:%x\n", n.Arg1, n.Arg2)
	case DbgIcmp6TimeExceeded:
		fmt.Printf("Sending ICMPv6 time exceeded\n")
	case DbgDecap:
		fmt.Printf("Tunnel decap: id=%d flowlabel=%x\n", n.Arg1, n.Arg2)
	case DbgPortMap:
		fmt.Printf("Mapping port from=%d to=%d\n", n.Arg1, n.Arg2)
	case DbgErrorRet:
		fmt.Printf("BPF function %d returned error %d\n", n.Arg1, n.Arg2)
	case DbgToHost:
		fmt.Printf("Going to host, policy-skip=%d\n", n.Arg1)
	case DbgToStack:
		fmt.Printf("Going to the stack, policy-skip=%d\n", n.Arg1)
	case DbgPktHash:
		fmt.Printf("Packet hash=%d (%#x), selected_service=%d\n", n.Arg1, n.Arg1, n.Arg2)
	case DbgRRSlaveSel:
		fmt.Printf("RR slave selection hash=%d (%#x), selected_service=%d\n", n.Arg1, n.Arg1, n.Arg2)
	case DbgLb6LookupMaster:
		fmt.Printf("Master service lookup, addr.p4=%x key.dport=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgLb6LookupMasterFail:
		fmt.Printf("Master service lookup failed, addr.p2=%x addr.p3=%x\n", n.Arg1, n.Arg2)
	case DbgLb6LookupSlave, DbgLb4LookupSlave:
		fmt.Printf("Slave service lookup: slave=%d, dport=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgLb6LookupSlaveSuccess:
		fmt.Printf("Slave service lookup result: target.p4=%x port=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgLb6ReverseNatLookup, DbgLb4ReverseNatLookup:
		fmt.Printf("Reverse NAT lookup, index=%d\n", common.Swab16(uint16(n.Arg1)))
	case DbgLb6ReverseNat:
		fmt.Printf("Performing reverse NAT, address.p4=%x port=%d\n", n.Arg1, common.Swab16(uint16(n.Arg2)))
	case DbgLb4LookupMaster:
		fmt.Printf("Master service lookup, addr=%s key.dport=%d\n", ip4Str(n.Arg1), common.Swab16(uint16(n.Arg2)))
	case DbgLb4LookupMasterFail:
		fmt.Printf("Master service lookup failed\n")
	case DbgLb4LookupSlaveSuccess:
		fmt.Printf("Slave service lookup result: target=%s port=%d\n", ip4Str(n.Arg1), common.Swab16(uint16(n.Arg2)))
	case DbgLb4ReverseNat:
		fmt.Printf("Performing reverse NAT, address=%s port=%d\n", ip4Str(n.Arg1), common.Swab16(uint16(n.Arg2)))
	case DbgLb4LoopbackSnat:
		fmt.Printf("Loopback SNAT from=%s to=%s\n", ip4Str(n.Arg1), ip4Str(n.Arg2))
	case DbgLb4LoopbackSnatRev:
		fmt.Printf("Loopback reverse SNAT from=%s to=%s\n", ip4Str(n.Arg1), ip4Str(n.Arg2))
	case DbgRevProxyLookup:
		fmt.Printf("Reverse proxy lookup, %s\n", proxyInfo(n.Arg1, n.Arg2))
	case DbgRevProxyFound:
		fmt.Printf("Reverse proxy entry found, orig-daddr=%s orig-dport=%d\n", ip4Str(n.Arg1), n.Arg2)
	case DbgRevProxyUpdate:
		fmt.Printf("Reverse proxy updated, %s\n", proxyInfo(n.Arg1, n.Arg2))
	case DbgL4Policy:
		fmt.Printf("Resolved L4 policy to: %d / %s\n",
			common.Swab16(uint16(n.Arg1)), ctDirection[int(n.Arg2)])
	default:
		fmt.Printf("Unknown message type=%d arg1=%d arg2=%d\n", n.SubType, n.Arg1, n.Arg2)
	}
}

const (
	// DebugCaptureLen is the amount of packet data in a packet capture message
	DebugCaptureLen = 20
)

// DebugCapture is the metadata sent along with a captured packet frame
type DebugCapture struct {
	Type    uint8
	SubType uint8
	Source  uint16
	Hash    uint32
	Len     uint32
	OrigLen uint32
	Arg1    uint32
	// data
}

// Dump prints the captured packet in human readable format
func (n *DebugCapture) Dump(dissect bool, data []byte, prefix string) {
	fmt.Printf("%s MARK %#x FROM %d DEBUG: %d bytes ", prefix, n.Hash, n.Source, n.Len)
	switch n.SubType {
	case DbgCaptureFromLxc:
		fmt.Printf("Incoming packet from container ifindex %d\n", n.Arg1)
	case DbgCaptureFromNetdev:
		fmt.Printf("Incoming packet from netdev ifindex %d\n", n.Arg1)
	case DbgCaptureFromOverlay:
		fmt.Printf("Incoming packet from overlay ifindex %d\n", n.Arg1)
	case DbgCaptureDelivery:
		fmt.Printf("Delivery to ifindex %d\n", n.Arg1)
	case DbgCaptureFromLb:
		fmt.Printf("Incoming packet to load balancer on ifindex %d\n", n.Arg1)
	case DbgCaptureAfterV46:
		fmt.Printf("Packet after nat46 ifindex %d\n", n.Arg1)
	case DbgCaptureAfterV64:
		fmt.Printf("Packet after nat64 ifindex %d\n", n.Arg1)
	case DbgCaptureProxyPre:
		fmt.Printf("Packet to proxy port %d (Pre)\n", common.Swab16(uint16(n.Arg1)))
	case DbgCaptureProxyPost:
		fmt.Printf("Packet to proxy port %d (Post)\n", common.Swab16(uint16(n.Arg1)))
	default:
		fmt.Printf("Unknown message type=%d arg1=%d\n", n.SubType, n.Arg1)
	}

	if n.Len > 0 && len(data) > DebugCaptureLen {
		Dissect(dissect, data[DebugCaptureLen:])
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os/exec"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"syscall"
	"strings"
	//"user"

	_ "pty"
)

type cgroup string

func (c cgroup) apply(s string, f func(s string)) {
	for _,g := range strings.Split(s, ",") {
		p := path.Join(string(c), g)
		f(p)
	}
}

func (c cgroup) Validate(s string) {
	c.apply(s, func(s string) {
		if st, err := os.Stat(path.Join(string(c), s)); err != nil {
			log.Fatal("%v", err)
		} else if ! st.IsDir() {
				log.Fatal("%s: not a directory", s)
		}})
}

func (c cgroup) Create(s, name string) {
	if err := os.MkdirAll(path.Join(string(c), s, name), 0755); err != nil {
			log.Fatal(err)
	}
}

func (c cgroup) Attach(s, name string, pid int) {
	t := path.Join(string(c), s, name, "tasks")
	b := []byte(fmt.Sprintf("%v", pid))
	if err := ioutil.WriteFile(t, b, 0600); err != nil {
		log.Fatal(err)
	}
}

func (c cgroup) Destroy(s, n string) {
	if err := os.RemoveAll(path.Join(string(c), s, n)); err != nil {
			log.Fatal(err)
	}
}

func (c cgroup) Do(pid int) {
	cgn := fmt.Sprintf("pflask.%d", pid)
	c.apply(string(c), func(s string) {
		c.Create(s, cgn)
		c.Attach(s, cgn, 1)
	})
}

type mount struct {
	src, dst, mtype, opts string
	flags uintptr
	dir bool
	mounted bool
}

type mlist struct {
	mounts[]*mount
}

func NewMlist(base string) (*mlist, error){
	m := &mlist{}
	if err := syscall.Mount("", "/", "", syscall.MS_SLAVE|syscall.MS_REC, ""); err != nil {
		err := fmt.Errorf("Mount :%s: on :%s: type :%s: flags %x: opts :%v: %v\n",
			"", "/", "", syscall.MS_SLAVE|syscall.MS_REC, "", err)
		return nil, err
	}

	return m, nil
}

func (m *mlist) Add(base, src, dst, mtype, opts string, flags uintptr, dir bool) {
	m.mounts = append(m.mounts, &mount{src: src, dst: path.Join(base, dst), mtype: mtype, flags: flags, opts: opts, dir: dir})

}

func (m* mount) One() error {
	if m.dir {
		if err := os.MkdirAll(m.dst, 0755); err != nil {
			return fmt.Errorf("One: mkdirall %v: %v", m.dst, err)
		}
	}
	if err := syscall.Mount(m.src, m.dst, m.mtype, m.flags, m.opts); err != nil {
		return fmt.Errorf("Mount :%s: on :%s: type :%s: flags %x: opts :%v: %v\n",
			m.src, m.dst, m.mtype, m.flags, m.opts, err)
	}
	m.mounted = true
	return nil
}
func (m *mlist) Do(base, console string) {
	ok := true
	if base != "" {
		m.Add(base, "proc", "/proc", "proc", "",
			syscall.MS_NOSUID | syscall.MS_NOEXEC | syscall.MS_NODEV, true)

		m.Add(base, "/proc/sys", "/proc/sys", "", "",
			syscall.MS_BIND, true)

		m.Add(base, "", "/proc/sys", "", "",
			syscall.MS_BIND | syscall.MS_RDONLY | syscall.MS_REMOUNT, true)

		m.Add(base, "sysfs", "/sys", "sysfs", "",
			syscall.MS_NOSUID | syscall.MS_NOEXEC | syscall.MS_NODEV | syscall.MS_RDONLY, true)

		m.Add(base, "tmpfs", "/dev", "tmpfs", "mode=755",
			syscall.MS_NOSUID | syscall.MS_STRICTATIME, true)

		m.Add(base, "devpts", "/dev/pts", "devpts","newinstance,ptmxmode=000,mode=620,gid=5",
			syscall.MS_NOSUID | syscall.MS_NOEXEC, true)

		// OOPS! have to mknod first. Now I see why they did it the way they
		// did.
		//m.Add(base, console, "/dev/console", "", "", syscall.MS_BIND, false)

		m.Add(base, "tmpfs", "/dev/shm", "tmpfs", "mode=1777",
			syscall.MS_NOSUID | syscall.MS_STRICTATIME | syscall.MS_NODEV, true)

		m.Add(base, "tmpfs", "/run", "tmpfs", "mode=755",
			syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_STRICTATIME, true)

	}

	for _, m := range m.mounts[1:] {
		err := m.One()
		if err != nil {
			log.Printf(err.Error())
			ok = false
		}
	}
	if ! ok {
		m.Undo()
		log.Fatal("Not all mounts succeeded.")
	}
}

func (m *mlist) Undo() {
	for i := range m.mounts {
		m := m.mounts[len(m.mounts)-i-1]
		if ! m.mounted {
			continue
		}
		if err := syscall.Unmount(m.dst, 0); err != nil {
			log.Printf("Unmounting %v: %v", m, err)
		}
		m.mounted = false
	}
}

func copy_nodes(base, console string) {
	nodes := []string {
		console,
		"/dev/tty",
		"/dev/full",
		"/dev/null",
		"/dev/zero",
		"/dev/random",
		"/dev/urandom", }

	if err := os.Chmod(console, 0600); err != nil {
		log.Printf("%v", err)
	}
	if err := os.Chown(console, 0, 0); err != nil {
		log.Printf("%v", err)
	}

	for _, n := range nodes {
		st, err := os.Stat(n)
		if err != nil {
			log.Printf("%v", err)
		}
		nn := path.Join(base, n)
		if err := syscall.Mknod(nn, uint32(st.Mode()), int(st.Sys().(*syscall.Stat_t).Dev)); err != nil {
			log.Printf("%v", err)
		}

	}
}

func make_symlinks(base string) {
	linkit := []struct {
		src, dst string
	} {
		{"/dev/pts/ptmx", "/dev/ptmx"},
		{"/proc/kcore",	"/dev/core"},
		{"/proc/self/fd", "/dev/fd"},
		{"/proc/self/fd/0", "/dev/stdin"},
		{"/proc/self/fd/1","/dev/stdout"},
		{"/proc/self/fd/2", "/dev/stderr"},

	}

	for i := range linkit {
		dst := path.Join(base, linkit[i].dst)

		if err := os.Symlink(linkit[i].src, dst); err != nil {
			log.Printf("%v", err)
		}
	}
}

func do_chroot(dest string) {

	if err := os.Chdir(dest); err != nil {
		log.Fatalf("%v", err)
	}
	if err := syscall.Chroot(dest); err != nil {
		log.Fatalf("%v", err)
	}
	if err := os.Chdir("/"); err != nil {
		log.Fatalf("%v", err)
	}
}

var (
	cg = flag.String("cgroup", "/sys/fs/cgroup", "set the cgroups")
	mnt = flag.String("mount", "", "define mounts")
	chroot = flag.String("chroot", "", "where to chroot to")
	chdir = flag.String("chdir", "", "where to chrdir to")
	dest = flag.String("dest", "", "where the root is")
	console = flag.String("console", "/dev/console", "where the root is")
	keepenv = flag.Bool("keepenv", false, "Keep the environment")
	env = flag.String("env", "", "other environment variables")
	user = flag.String("user", "root"/*user.User.Username*/, "User name")
)

func main() {
	// note the unshare system call worketh not for Go. You have to run
	// this under the unshare command. Good times.
	flag.Parse()
	if *dest == "" {
		log.Fatalf("you are required to set the dest via --dest")
	}
	fmt.Printf("greetings\n")
	a := flag.Args()

	// Just create the container and run with it for now.
	c := cgroup(*cg)
	ppid := 1048576
	m, err := NewMlist(*dest)
	if err != nil {
		log.Fatalf("%v", err)
	}
	// child code. Not really. What really happens here is we set
	// ourselves into the container, and spawn the child. It's a bit odd
	// but we're the master, but we'll run in the container? I don't know
	// how else to do it. This may require we set some things up first,
	// esp. the network. But, it's all fun and games until someone loses
	// an eye.
	if true {
		c.Do(ppid)

		m.Do(*dest, *console)

			copy_nodes(*dest, *console)

			//make_ptmx(dest);

			make_symlinks(*dest)

			//make_console(dest, master_name);

			do_chroot(*dest)

		//umask(0022);

		/* TODO: drop capabilities */

		//do_user(user);
/*
		if (change != NULL) {
			rc = chdir(change);
			if (rc < 0) sysf_printf("chdir()");
*/

		e := make(map[string]string)
		if *keepenv {
			for _, v := range os.Environ() {
				k := strings.SplitN(v, "=", 2)
				e[k[0]] = k[1]
			}
		}

			term := os.Getenv("TERM")
			e["TERM"] = term
			e["PATH"] =  "/usr/sbin:/usr/bin:/sbin:/bin"
			e["USER"] = *user
			e["LOGNAME"] = *user

		if *env != "" {
			for _, c := range strings.Split(*env, ",") {
				k := strings.SplitN(c, "=", 2)
				if len(k) != 2 {
					log.Printf("Bogus environment string %v", c)
					continue
				}
				e[k[0]] = k[1]
			}
		}
		e["container"] = "pflask"

		if len(a) == 0 {
			a = []string{"/bin/bash", "bash"}
		}
		c := exec.Command(a[0]) // , a[1:]...)
		c.Env = nil
		for k, v := range e {
			c.Env = append(c.Env, k + "=" + v)
		}

		if err := c.Run(); err != nil {
			m.Undo()
			m.Undo()
			log.Fatalf("Run failed")
		}
	}
	// end child code.
	m.Undo()
}
<commit_msg>Ran date in a container.<commit_after>package main

import (
	"os/exec"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"syscall"
	"strings"
	//"user"

	"pty"
)

type cgroup string

func (c cgroup) apply(s string, f func(s string)) {
	for _,g := range strings.Split(s, ",") {
		p := path.Join(string(c), g)
		f(p)
	}
}

func (c cgroup) Validate(s string) {
	c.apply(s, func(s string) {
		if st, err := os.Stat(path.Join(string(c), s)); err != nil {
			log.Fatal("%v", err)
		} else if ! st.IsDir() {
				log.Fatal("%s: not a directory", s)
		}})
}

func (c cgroup) Create(s, name string) {
	if err := os.MkdirAll(path.Join(string(c), s, name), 0755); err != nil {
			log.Fatal(err)
	}
}

func (c cgroup) Attach(s, name string, pid int) {
	t := path.Join(string(c), s, name, "tasks")
	b := []byte(fmt.Sprintf("%v", pid))
	if err := ioutil.WriteFile(t, b, 0600); err != nil {
		log.Fatal(err)
	}
}

func (c cgroup) Destroy(s, n string) {
	if err := os.RemoveAll(path.Join(string(c), s, n)); err != nil {
			log.Fatal(err)
	}
}

func (c cgroup) Do(pid int) {
	cgn := fmt.Sprintf("pflask.%d", pid)
	c.apply(string(c), func(s string) {
		c.Create(s, cgn)
		c.Attach(s, cgn, 1)
	})
}

type mount struct {
	src, dst, mtype, opts string
	flags uintptr
	dir bool
	mounted bool
}

type mlist struct {
	mounts[]*mount
}

func NewMlist(base string) (*mlist, error){
	m := &mlist{}
	if err := syscall.Mount("", "/", "", syscall.MS_SLAVE|syscall.MS_REC, ""); err != nil {
		err := fmt.Errorf("Mount :%s: on :%s: type :%s: flags %x: opts :%v: %v\n",
			"", "/", "", syscall.MS_SLAVE|syscall.MS_REC, "", err)
		return nil, err
	}

	return m, nil
}

func (m *mlist) Add(base, src, dst, mtype, opts string, flags uintptr, dir bool) {
	m.mounts = append(m.mounts, &mount{src: src, dst: path.Join(base, dst), mtype: mtype, flags: flags, opts: opts, dir: dir})

}

func (m* mount) One() error {
	if m.dir {
		if err := os.MkdirAll(m.dst, 0755); err != nil {
			return fmt.Errorf("One: mkdirall %v: %v", m.dst, err)
		}
	}
	if err := syscall.Mount(m.src, m.dst, m.mtype, m.flags, m.opts); err != nil {
		return fmt.Errorf("Mount :%s: on :%s: type :%s: flags %x: opts :%v: %v\n",
			m.src, m.dst, m.mtype, m.flags, m.opts, err)
	}
	m.mounted = true
	return nil
}
func (m *mlist) Do(base, console string) {
	ok := true
	if base != "" {
		m.Add(base, "proc", "/proc", "proc", "",
			syscall.MS_NOSUID | syscall.MS_NOEXEC | syscall.MS_NODEV, true)

		m.Add(base, "/proc/sys", "/proc/sys", "", "",
			syscall.MS_BIND, true)

		m.Add(base, "", "/proc/sys", "", "",
			syscall.MS_BIND | syscall.MS_RDONLY | syscall.MS_REMOUNT, true)

		m.Add(base, "sysfs", "/sys", "sysfs", "",
			syscall.MS_NOSUID | syscall.MS_NOEXEC | syscall.MS_NODEV | syscall.MS_RDONLY, true)

		m.Add(base, "tmpfs", "/dev", "tmpfs", "mode=755",
			syscall.MS_NOSUID | syscall.MS_STRICTATIME, true)

		m.Add(base, "devpts", "/dev/pts", "devpts","newinstance,ptmxmode=000,mode=620,gid=5",
			syscall.MS_NOSUID | syscall.MS_NOEXEC, true)

		// OOPS! have to mknod first. Now I see why they did it the way they
		// did.
		//m.Add(base, console, "/dev/console", "", "", syscall.MS_BIND, false)

		m.Add(base, "tmpfs", "/dev/shm", "tmpfs", "mode=1777",
			syscall.MS_NOSUID | syscall.MS_STRICTATIME | syscall.MS_NODEV, true)

		m.Add(base, "tmpfs", "/run", "tmpfs", "mode=755",
			syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_STRICTATIME, true)

	}

	for _, m := range m.mounts[1:] {
		err := m.One()
		if err != nil {
			log.Printf(err.Error())
			ok = false
		}
	}
	if ! ok {
		m.Undo()
		log.Fatal("Not all mounts succeeded.")
	}
}

func (m *mlist) Undo() {
	for i := range m.mounts {
		m := m.mounts[len(m.mounts)-i-1]
		if ! m.mounted {
			continue
		}
		if err := syscall.Unmount(m.dst, 0); err != nil {
			log.Printf("Unmounting %v: %v", m, err)
		}
		m.mounted = false
	}
}

func copy_nodes(base, console string) {
	nodes := []string {
		console,
		"/dev/tty",
		"/dev/full",
		"/dev/null",
		"/dev/zero",
		"/dev/random",
		"/dev/urandom", }

	if err := os.Chmod(console, 0600); err != nil {
		log.Printf("%v", err)
	}
	if err := os.Chown(console, 0, 0); err != nil {
		log.Printf("%v", err)
	}

	for _, n := range nodes {
		st, err := os.Stat(n)
		if err != nil {
			log.Printf("%v", err)
		}
		nn := path.Join(base, n)
		if err := syscall.Mknod(nn, uint32(st.Mode()), int(st.Sys().(*syscall.Stat_t).Dev)); err != nil {
			log.Printf("%v", err)
		}

	}
}

func make_symlinks(base string) {
	linkit := []struct {
		src, dst string
	} {
		{"/dev/pts/ptmx", "/dev/ptmx"},
		{"/proc/kcore",	"/dev/core"},
		{"/proc/self/fd", "/dev/fd"},
		{"/proc/self/fd/0", "/dev/stdin"},
		{"/proc/self/fd/1","/dev/stdout"},
		{"/proc/self/fd/2", "/dev/stderr"},

	}

	for i := range linkit {
		dst := path.Join(base, linkit[i].dst)

		if err := os.Symlink(linkit[i].src, dst); err != nil {
			log.Printf("%v", err)
		}
	}
}

func do_chroot(dest string) {

	if err := os.Chdir(dest); err != nil {
		log.Fatalf("%v", err)
	}
	if err := syscall.Chroot(dest); err != nil {
		log.Fatalf("%v", err)
	}
	if err := os.Chdir("/"); err != nil {
		log.Fatalf("%v", err)
	}
}

var (
	cg = flag.String("cgroup", "/sys/fs/cgroup", "set the cgroups")
	mnt = flag.String("mount", "", "define mounts")
	chroot = flag.String("chroot", "", "where to chroot to")
	chdir = flag.String("chdir", "", "where to chrdir to")
	dest = flag.String("dest", "", "where the root is")
	console = flag.String("console", "/dev/console", "where the root is")
	keepenv = flag.Bool("keepenv", false, "Keep the environment")
	env = flag.String("env", "", "other environment variables")
	user = flag.String("user", "root"/*user.User.Username*/, "User name")
)

func main() {
	// note the unshare system call worketh not for Go. You have to run
	// this under the unshare command. Good times.
	flag.Parse()
	if *dest == "" {
		log.Fatalf("you are required to set the dest via --dest")
	}
	fmt.Printf("greetings\n")
	a := flag.Args()

	// Just create the container and run with it for now.
	c := cgroup(*cg)
	ppid := 1048576
	m, err := NewMlist(*dest)
	if err != nil {
		log.Fatalf("%v", err)
	}
	// child code. Not really. What really happens here is we set
	// ourselves into the container, and spawn the child. It's a bit odd
	// but we're the master, but we'll run in the container? I don't know
	// how else to do it. This may require we set some things up first,
	// esp. the network. But, it's all fun and games until someone loses
	// an eye.
	if true {
		c.Do(ppid)

		m.Do(*dest, *console)

			copy_nodes(*dest, *console)

			//make_ptmx(dest);

			make_symlinks(*dest)

			//make_console(dest, master_name);

			do_chroot(*dest)

		//umask(0022);

		/* TODO: drop capabilities */

		//do_user(user);
/*
		if (change != NULL) {
			rc = chdir(change);
			if (rc < 0) sysf_printf("chdir()");
*/

		e := make(map[string]string)
		if *keepenv {
			for _, v := range os.Environ() {
				k := strings.SplitN(v, "=", 2)
				e[k[0]] = k[1]
			}
		}

			term := os.Getenv("TERM")
			e["TERM"] = term
			e["PATH"] =  "/usr/sbin:/usr/bin:/sbin:/bin"
			e["USER"] = *user
			e["LOGNAME"] = *user

		if *env != "" {
			for _, c := range strings.Split(*env, ",") {
				k := strings.SplitN(c, "=", 2)
				if len(k) != 2 {
					log.Printf("Bogus environment string %v", c)
					continue
				}
				e[k[0]] = k[1]
			}
		}
		e["container"] = "pflask"

		if len(a) == 0 {
			a = []string{"/bin/bash", "bash"}
		}
		c := exec.Command(a[0]) // , a[1:]...)
		c.Env = nil
		for k, v := range e {
			c.Env = append(c.Env, k + "=" + v)
		}

		f, err := pty.Start(c)
		if err != nil {
			panic(err)
		}
		io.Copy(os.Stdout, f)
	}
	// end child code.
	m.Undo()
}
<|endoftext|>


<commit_before>package cli

import (
	"errors"
	"fmt"
	"io"
	"os"

	"github.com/apprenda/kismatic-platform/pkg/install"
	"github.com/apprenda/kismatic-platform/pkg/util"
	"github.com/spf13/cobra"
)

type addWorkerOpts struct {
	CAConfigFile             string
	CASigningProfile         string
	GeneratedAssetsDirectory string
	RestartServices          bool
	OutputFormat             string
	Verbose                  bool
	SkipPreFlight            bool
	WorkerInternalIP         string
}

// NewCmdAddWorker returns the command for adding workers to the cluster
func NewCmdAddWorker(out io.Writer, installOpts *installOpts) *cobra.Command {
	opts := &addWorkerOpts{}
	cmd := &cobra.Command{
		Use:   "add-worker WORKER_NAME WORKER_IP [WORKER_INTERNAL_IP]",
		Short: "add a Worker node to an existing Kismatic cluster",
		RunE: func(cmd *cobra.Command, args []string) error {
			if len(args) < 2 || len(args) > 3 {
				return cmd.Usage()
			}
			newWorker := install.Node{
				Host: args[0],
				IP:   args[1],
			}
			if len(args) == 3 {
				newWorker.InternalIP = args[2]
			}
			return doAddWorker(out, installOpts.planFilename, opts, newWorker)
		},
	}
	cmd.Flags().StringVar(&opts.CAConfigFile, "ca-config", "ansible/playbooks/tls/ca-config.json", "path to the Certificate Authority configuration file")
	cmd.Flags().StringVar(&opts.CASigningProfile, "ca-signing-profile", "kubernetes", "name of the profile to be used for signing certificates")
	cmd.Flags().StringVar(&opts.GeneratedAssetsDirectory, "generated-assets-dir", "generated", "path to the directory where assets generated during the installation process are to be stored")
	cmd.Flags().BoolVar(&opts.RestartServices, "restart-services", false, "force restart clusters services (Use with care)")
	cmd.Flags().BoolVar(&opts.Verbose, "verbose", false, "enable verbose logging from the installation")
	cmd.Flags().StringVarP(&opts.OutputFormat, "output", "o", "simple", "installation output format. Supported options: simple|raw")
	cmd.Flags().BoolVar(&opts.SkipPreFlight, "skip-preflight", false, "skip pre-flight checks")
	cmd.Flags().StringVar(&opts.WorkerInternalIP, "internal-ip", "", "the internal IP of the worker, if different than the IP.")
	return cmd
}

func doAddWorker(out io.Writer, planFile string, opts *addWorkerOpts, newWorker install.Node) error {
	planner := &install.FilePlanner{File: planFile}
	if !planner.PlanExists() {
		return errors.New("add-worker can only be used with an existin plan file")
	}
	execOpts := install.ExecutorOptions{
		CAConfigFile:             opts.CAConfigFile,
		CASigningProfile:         opts.CASigningProfile,
		GeneratedAssetsDirectory: opts.GeneratedAssetsDirectory,
		RestartServices:          opts.RestartServices,
		OutputFormat:             opts.OutputFormat,
		Verbose:                  opts.Verbose,
		SkipCAGeneration:         true,
	}
	executor, err := install.NewExecutor(out, os.Stderr, execOpts)
	if err != nil {
		return err
	}
	plan, err := planner.Read()
	if err != nil {
		return fmt.Errorf("failed to read plan file: %v", err)
	}
	if _, errs := install.ValidateNode(&newWorker); errs != nil {
		printValidationErrors(out, errs)
		return errors.New("information provided about the new worker node is invalid")
	}
	if _, errs := install.ValidatePlan(plan); errs != nil {
		printValidationErrors(out, errs)
		return errors.New("the plan file failed validation")
	}
	if err := ensureNodeIsNew(*plan, newWorker); err != nil {
		return err
	}
	if !opts.SkipPreFlight {
		util.PrintHeader(out, "Running Pre-Flight Checks On New Worker", '=')
		if err := runPreFlightOnWorker(executor, *plan, newWorker); err != nil {
			return err
		}
	}
	updatedPlan, err := executor.AddWorker(plan, newWorker)
	if err != nil {
		return err
	}
	if err := planner.Write(updatedPlan); err != nil {
		return fmt.Errorf("error updating plan file to inlcude new worker node: %v", err)
	}
	return nil
}

// returns an error if the plan contains a worker that is "equivalent"
// to the new worker that is being added
func ensureNodeIsNew(plan install.Plan, newWorker install.Node) error {
	for _, n := range plan.Worker.Nodes {
		if n.Host == newWorker.Host {
			return fmt.Errorf("according to the plan file, the host name of the new node is already being used by another worker node")
		}
		if n.IP == newWorker.IP {
			return fmt.Errorf("according to the plan file, the IP of the new node is already being used by another worker node")
		}
		if newWorker.InternalIP != "" && n.InternalIP == newWorker.InternalIP {
			return fmt.Errorf("according to the plan file, the internal IP of the new node is already being used by another worker node")
		}
	}
	return nil
}

func runPreFlightOnWorker(executor install.Executor, plan install.Plan, workerNode install.Node) error {
	// use the original plan, but only run against the new worker
	preFlightPlan := plan
	preFlightPlan.Master.Nodes = []install.Node{}
	preFlightPlan.Etcd.Nodes = []install.Node{}
	preFlightPlan.Worker.Nodes = []install.Node{workerNode}
	return executor.RunPreFlightCheck(&preFlightPlan)
}
<commit_msg>KIS-83: Remove unused flag<commit_after>package cli

import (
	"errors"
	"fmt"
	"io"
	"os"

	"github.com/apprenda/kismatic-platform/pkg/install"
	"github.com/apprenda/kismatic-platform/pkg/util"
	"github.com/spf13/cobra"
)

type addWorkerOpts struct {
	CAConfigFile             string
	CASigningProfile         string
	GeneratedAssetsDirectory string
	RestartServices          bool
	OutputFormat             string
	Verbose                  bool
	SkipPreFlight            bool
}

// NewCmdAddWorker returns the command for adding workers to the cluster
func NewCmdAddWorker(out io.Writer, installOpts *installOpts) *cobra.Command {
	opts := &addWorkerOpts{}
	cmd := &cobra.Command{
		Use:   "add-worker WORKER_NAME WORKER_IP [WORKER_INTERNAL_IP]",
		Short: "add a Worker node to an existing Kismatic cluster",
		RunE: func(cmd *cobra.Command, args []string) error {
			if len(args) < 2 || len(args) > 3 {
				return cmd.Usage()
			}
			newWorker := install.Node{
				Host: args[0],
				IP:   args[1],
			}
			if len(args) == 3 {
				newWorker.InternalIP = args[2]
			}
			return doAddWorker(out, installOpts.planFilename, opts, newWorker)
		},
	}
	cmd.Flags().StringVar(&opts.CAConfigFile, "ca-config", "ansible/playbooks/tls/ca-config.json", "path to the Certificate Authority configuration file")
	cmd.Flags().StringVar(&opts.CASigningProfile, "ca-signing-profile", "kubernetes", "name of the profile to be used for signing certificates")
	cmd.Flags().StringVar(&opts.GeneratedAssetsDirectory, "generated-assets-dir", "generated", "path to the directory where assets generated during the installation process are to be stored")
	cmd.Flags().BoolVar(&opts.RestartServices, "restart-services", false, "force restart clusters services (Use with care)")
	cmd.Flags().BoolVar(&opts.Verbose, "verbose", false, "enable verbose logging from the installation")
	cmd.Flags().StringVarP(&opts.OutputFormat, "output", "o", "simple", "installation output format. Supported options: simple|raw")
	cmd.Flags().BoolVar(&opts.SkipPreFlight, "skip-preflight", false, "skip pre-flight checks")
	return cmd
}

func doAddWorker(out io.Writer, planFile string, opts *addWorkerOpts, newWorker install.Node) error {
	planner := &install.FilePlanner{File: planFile}
	if !planner.PlanExists() {
		return errors.New("add-worker can only be used with an existin plan file")
	}
	execOpts := install.ExecutorOptions{
		CAConfigFile:             opts.CAConfigFile,
		CASigningProfile:         opts.CASigningProfile,
		GeneratedAssetsDirectory: opts.GeneratedAssetsDirectory,
		RestartServices:          opts.RestartServices,
		OutputFormat:             opts.OutputFormat,
		Verbose:                  opts.Verbose,
		SkipCAGeneration:         true,
	}
	executor, err := install.NewExecutor(out, os.Stderr, execOpts)
	if err != nil {
		return err
	}
	plan, err := planner.Read()
	if err != nil {
		return fmt.Errorf("failed to read plan file: %v", err)
	}
	if _, errs := install.ValidateNode(&newWorker); errs != nil {
		printValidationErrors(out, errs)
		return errors.New("information provided about the new worker node is invalid")
	}
	if _, errs := install.ValidatePlan(plan); errs != nil {
		printValidationErrors(out, errs)
		return errors.New("the plan file failed validation")
	}
	if err := ensureNodeIsNew(*plan, newWorker); err != nil {
		return err
	}
	if !opts.SkipPreFlight {
		util.PrintHeader(out, "Running Pre-Flight Checks On New Worker", '=')
		if err := runPreFlightOnWorker(executor, *plan, newWorker); err != nil {
			return err
		}
	}
	updatedPlan, err := executor.AddWorker(plan, newWorker)
	if err != nil {
		return err
	}
	if err := planner.Write(updatedPlan); err != nil {
		return fmt.Errorf("error updating plan file to inlcude new worker node: %v", err)
	}
	return nil
}

// returns an error if the plan contains a worker that is "equivalent"
// to the new worker that is being added
func ensureNodeIsNew(plan install.Plan, newWorker install.Node) error {
	for _, n := range plan.Worker.Nodes {
		if n.Host == newWorker.Host {
			return fmt.Errorf("according to the plan file, the host name of the new node is already being used by another worker node")
		}
		if n.IP == newWorker.IP {
			return fmt.Errorf("according to the plan file, the IP of the new node is already being used by another worker node")
		}
		if newWorker.InternalIP != "" && n.InternalIP == newWorker.InternalIP {
			return fmt.Errorf("according to the plan file, the internal IP of the new node is already being used by another worker node")
		}
	}
	return nil
}

func runPreFlightOnWorker(executor install.Executor, plan install.Plan, workerNode install.Node) error {
	// use the original plan, but only run against the new worker
	preFlightPlan := plan
	preFlightPlan.Master.Nodes = []install.Node{}
	preFlightPlan.Etcd.Nodes = []install.Node{}
	preFlightPlan.Worker.Nodes = []install.Node{workerNode}
	return executor.RunPreFlightCheck(&preFlightPlan)
}
<|endoftext|>


<commit_before>// Copyright 2016 The nvim-go 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 config

import (
	"github.com/neovim/go-client/nvim"
)

// Config represents a config variable for nvim-go.
// Each type must be exported for plugin.HandleAutocmd Eval option.
// Also it does not support embeded type.
type Config struct {
	Global *Global

	Build    *build
	Cover    *cover
	Fmt      *fmt
	Generate *generate
	Guru     *guru
	Iferr    *iferr
	Lint     *lint
	Rename   *rename
	Terminal *terminal
	Test     *test

	Debug *debug
}

// Global represents a global config variable.
type Global struct {
	ChannelID     int
	ServerName    string `eval:"v:servername"`
	ErrorListType string `eval:"get(g:, 'go#global#errorlisttype', 'locationlist')"`
}

// build GoBuild command config variable.
type build struct {
	Appengine int64    `eval:"get(g:, 'go#build#appengine', 0)"`
	Autosave  int64    `eval:"get(g:, 'go#build#autosave', 0)"`
	Force     int64    `eval:"get(g:, 'go#build#force', 0)"`
	Flags     []string `eval:"get(g:, 'go#build#flags', [])"`
	IsNotGb   int64    `eval:"get(g:, 'go#build#is_not_gb', 0)"`
}

type cover struct {
	Flags []string `eval:"get(g:, 'go#cover#flags', [])"`
	Mode  string   `eval:"get(g:, 'go#cover#mode', '')"`
}

// fmt represents a GoFmt command config variable.
type fmt struct {
	Autosave int64  `eval:"get(g:, 'go#fmt#autosave', 0)"`
	Mode     string `eval:"get(g:, 'go#fmt#mode', 'goimports')"`
}

// generate represents a GoGenerate command config variables.
type generate struct {
	TestAllFuncs      int64  `eval:"get(g:, 'go#generate#test#allfuncs', 1)"`
	TestExclFuncs     string `eval:"get(g:, 'go#generate#test#exclude', '')"`
	TestExportedFuncs int64  `eval:"get(g:, 'go#generate#test#exportedfuncs', 0)"`
	TestSubTest       int64  `eval:"get(g:, 'go#generate#test#subtest', 1)"`
}

// guru represents a GoGuru command config variable.
type guru struct {
	Reflection int64            `eval:"get(g:, 'go#guru#reflection', 0)"`
	KeepCursor map[string]int64 `eval:"get(g:, 'go#guru#keep_cursor', {'callees':0,'callers':0,'callstack':0,'definition':0,'describe':0,'freevars':0,'implements':0,'peers':0,'pointsto':0,'referrers':0,'whicherrs':0})"`
	JumpFirst  int64            `eval:"get(g:, 'go#guru#jump_first', 0)"`
}

// iferr represents a GoIferr command config variable.
type iferr struct {
	Autosave int64 `eval:"get(g:, 'go#iferr#autosave', 0)"`
}

// lint represents a code lint commands config variable.
type lint struct {
	GolintAutosave          int64    `eval:"get(g:, 'go#lint#golint#autosave', 0)"`
	GolintIgnore            []string `eval:"get(g:, 'go#lint#golint#ignore', [])"`
	GolintMinConfidence     float64  `eval:"get(g:, 'go#lint#golint#min_confidence', 0.8)"`
	GolintMode              string   `eval:"get(g:, 'go#lint#golint#mode', 'current')"`
	GoVetAutosave           int64    `eval:"get(g:, 'go#lint#govet#autosave', 0)"`
	GoVetFlags              []string `eval:"get(g:, 'go#lint#govet#flags', [])"`
	GoVetIgnore             []string `eval:"get(g:, 'go#lint#govet#ignore', [])"`
	MetalinterAutosave      int64    `eval:"get(g:, 'go#lint#metalinter#autosave', 0)"`
	MetalinterAutosaveTools []string `eval:"get(g:, 'go#lint#metalinter#autosave#tools', ['vet', 'golint'])"`
	MetalinterTools         []string `eval:"get(g:, 'go#lint#metalinter#tools', ['vet', 'golint'])"`
	MetalinterDeadline      string   `eval:"get(g:, 'go#lint#metalinter#deadline', '5s')"`
	MetalinterSkipDir       []string `eval:"get(g:, 'go#lint#metalinter#skip_dir', [])"`
}

// rename represents a GoRename command config variable.
type rename struct {
	Prefill int64 `eval:"get(g:, 'go#rename#prefill', 0)"`
}

// terminal represents a configure of Neovim terminal buffer.
type terminal struct {
	Mode       string `eval:"get(g:, 'go#terminal#mode', 'vsplit')"`
	Position   string `eval:"get(g:, 'go#terminal#position', 'belowright')"`
	Height     int64  `eval:"get(g:, 'go#terminal#height', 0)"`
	Width      int64  `eval:"get(g:, 'go#terminal#width', 0)"`
	StopInsert int64  `eval:"get(g:, 'go#terminal#stop_insert', 1)"`
}

// Test represents a GoTest command config variables.
type test struct {
	AllPackage int64    `eval:"get(g:, 'go#test#all_package', 0)"`
	Autosave   int64    `eval:"get(g:, 'go#test#autosave', 0)"`
	Flags      []string `eval:"get(g:, 'go#test#flags', [])"`
}

// Debug represents a debug of nvim-go config variable.
type debug struct {
	Enable int64 `eval:"get(g:, 'go#debug', 0)"`
	Pprof  int64 `eval:"get(g:, 'go#debug#pprof', 0)"`
}

var (
	// ChannelID remote plugins channel id.
	ChannelID int
	// ServerName Neovim socket listen location.
	ServerName string
	// ErrorListType type of error list window.
	ErrorListType string

	// BuildAppengine enable appengine bulid.
	BuildAppengine bool
	// BuildAutosave call the GoBuild command automatically at during the BufWritePost.
	BuildAutosave bool
	// BuildForce builds the binary instead of fake(use ioutil.TempFiile) build.
	BuildForce bool
	// BuildFlags flag of compile tools build command.
	BuildFlags []string

	// BuildIsNotGb workaround for not ues gb compiler.
	BuildIsNotGb bool

	// CoverFlags flags for cover command.
	CoverFlags []string
	// CoverMode mode of cover command.
	CoverMode string

	// FmtAutosave call the GoFmt command automatically at during the BufWritePre.
	FmtAutosave bool
	// FmtMode formatting mode of Fmt command.
	FmtMode string

	// GenerateTestAllFuncs accept all functions to the GenerateTest.
	GenerateTestAllFuncs bool
	// GenerateTestExclFuncs exclude function of GenerateTest.
	GenerateTestExclFuncs string
	// GenerateTestExportedFuncs accept exported functions to the GenerateTest.
	GenerateTestExportedFuncs bool
	// GenerateTestSubTest whether the use Go subtest idiom or not.
	GenerateTestSubTest bool

	// GuruReflection use the type reflection on GoGuru commmands.
	GuruReflection bool
	// GuruKeepCursor keep the cursor focus to source buffer instead of quickfix or locationlist.
	GuruKeepCursor map[string]int64
	// GuruJumpFirst jump the first error position on GoGuru commands.
	GuruJumpFirst bool

	// IferrAutosave call the GoIferr command automatically at during the BufWritePre.
	IferrAutosave bool

	// GolintAutosave call the GoLint command automatically at during the BufWritePost.
	GolintAutosave bool
	// GolintIgnore ignore file for lint command.
	GolintIgnore []string
	// GolintMinConfidence minimum confidence of a problem to print it
	GolintMinConfidence float64
	// GolintMode mode of golint. available value are "root", "current" and "recursive".
	GolintMode string
	// GoVetAutosave call the GoVet command automatically at during the BufWritePost.
	GoVetAutosave bool
	// GoVetFlags default flags for GoVet commands
	GoVetFlags []string
	// GoVetIgnore ignore directories for go vet command.
	GoVetIgnore []string
	// MetalinterAutosave call the GoMetaLinter command automatically at during the BufWritePre.
	MetalinterAutosave bool
	// MetalinterAutosaveTools lint tool list for MetalinterAutosave.
	MetalinterAutosaveTools []string
	// MetalinterTools lint tool list for GoMetaLinter command.
	MetalinterTools []string
	// MetalinterDeadline deadline of GoMetaLinter command timeout.
	MetalinterDeadline string
	// MetalinterSkipDir skips of lint of the directory.
	MetalinterSkipDir []string

	// RenamePrefill Enable naming prefill.
	RenamePrefill bool

	// TerminalMode open the terminal window mode.
	TerminalMode string
	// TerminalPosition open the terminal window position.
	TerminalPosition string
	// TerminalHeight open the terminal window height.
	TerminalHeight int64
	// TerminalWidth open the terminal window width.
	TerminalWidth int64
	// TerminalStopInsert workaround if users set "autocmd BufEnter term://* startinsert".
	TerminalStopInsert bool

	// TestAutosave call the GoBuild command automatically at during the BufWritePost.
	TestAutosave bool
	// TestAll enable all package test on GoTest. similar "go test ./...", but ignored vendor and testdata.
	TestAll bool
	// TestFlags test command default flags.
	TestFlags []string

	// DebugEnable Enable debugging.
	DebugEnable bool
	// DebugPprof Enable net/http/pprof debugging.
	DebugPprof bool
)

// Get gets the user config variables and convert to global varialble.
func Get(v *nvim.Nvim, cfg *Config) {
	// Client
	ChannelID = cfg.Global.ChannelID
	ServerName = cfg.Global.ServerName
	ErrorListType = cfg.Global.ErrorListType

	// Build
	BuildAppengine = itob(cfg.Build.Appengine)
	BuildAutosave = itob(cfg.Build.Autosave)
	BuildForce = itob(cfg.Build.Force)
	BuildFlags = cfg.Build.Flags
	BuildIsNotGb = itob(cfg.Build.IsNotGb)

	// Cover
	CoverFlags = cfg.Cover.Flags
	CoverMode = cfg.Cover.Mode

	// Fmt
	FmtAutosave = itob(cfg.Fmt.Autosave)
	FmtMode = cfg.Fmt.Mode

	// Generate
	GenerateTestAllFuncs = itob(cfg.Generate.TestAllFuncs)
	GenerateTestExclFuncs = cfg.Generate.TestExclFuncs
	GenerateTestExportedFuncs = itob(cfg.Generate.TestExportedFuncs)
	GenerateTestSubTest = itob(cfg.Generate.TestSubTest)

	// Guru
	GuruReflection = itob(cfg.Guru.Reflection)
	GuruKeepCursor = cfg.Guru.KeepCursor
	GuruJumpFirst = itob(cfg.Guru.JumpFirst)

	// Iferr
	IferrAutosave = itob(cfg.Iferr.Autosave)

	// Lint
	GolintAutosave = itob(cfg.Lint.GolintAutosave)
	GolintIgnore = cfg.Lint.GolintIgnore
	GolintMinConfidence = cfg.Lint.GolintMinConfidence
	GolintMode = cfg.Lint.GolintMode
	GoVetAutosave = itob(cfg.Lint.GoVetAutosave)
	GoVetFlags = cfg.Lint.GoVetFlags
	GoVetIgnore = cfg.Lint.GoVetIgnore
	MetalinterAutosave = itob(cfg.Lint.MetalinterAutosave)
	MetalinterAutosaveTools = cfg.Lint.MetalinterAutosaveTools
	MetalinterTools = cfg.Lint.MetalinterTools
	MetalinterDeadline = cfg.Lint.MetalinterDeadline
	MetalinterSkipDir = cfg.Lint.MetalinterSkipDir

	// Rename
	RenamePrefill = itob(cfg.Rename.Prefill)

	// Terminal
	TerminalMode = cfg.Terminal.Mode
	TerminalPosition = cfg.Terminal.Position
	TerminalHeight = cfg.Terminal.Height
	TerminalWidth = cfg.Terminal.Width
	TerminalStopInsert = itob(cfg.Terminal.StopInsert)

	// Test
	TestAutosave = itob(cfg.Test.Autosave)
	TestAll = itob(cfg.Test.AllPackage)
	TestFlags = cfg.Test.Flags

	// Debug
	DebugEnable = itob(cfg.Debug.Enable)
	DebugPprof = itob(cfg.Debug.Pprof)
}

func itob(i int64) bool { return i != int64(0) }
<commit_msg>pkg/config: remove unused source<commit_after><|endoftext|>


<commit_before>package kato

//---------------------------------------------------------------------------
// Package factored import statement:
//---------------------------------------------------------------------------

import (

	// Stdlib:
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
)

//-----------------------------------------------------------------------------
// func: CountNodes
//-----------------------------------------------------------------------------

func CountNodes(quads []string, role string) (count int) {

	// Default to zero:
	count = 0

	// Get the role count:
	for _, q := range quads {
		if strings.Contains(q, role) {
			s := strings.Split(q, ":")
			count, _ = strconv.Atoi(s[0])
			break
		}
	}

	return
}

//-----------------------------------------------------------------------------
// func: CreateDNSZones
//-----------------------------------------------------------------------------

func CreateDNSZones(wg *sync.WaitGroup, provider, apiKey, domain string) error {

	// Decrement:
	if wg != nil {
		defer wg.Done()
	}

	// Forge the zone command:
	cmd := exec.Command("katoctl", provider,
		"--api-key", apiKey, "zone", "add",
		domain, "int."+domain, "ext."+domain)

	// Execute the zone command:
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return err
	}

	return nil
}

//-----------------------------------------------------------------------------
// func: ExecutePipeline
//-----------------------------------------------------------------------------

// ExecutePipeline takes two commands and pipes the stdout of the first one
// into the stdin of the second one. Returns the output as []byte.
func ExecutePipeline(cmd1, cmd2 *exec.Cmd) ([]byte, error) {

	var err error

	// Adjust the stderr:
	cmd1.Stderr = os.Stderr
	cmd2.Stderr = os.Stderr

	// Connect both commands:
	cmd2.Stdin, err = cmd1.StdoutPipe()
	if err != nil {
		return nil, err
	}

	// Get cmd2 stdout:
	stdout, err := cmd2.StdoutPipe()
	if err != nil {
		return nil, err
	}

	// Execute the pipeline:
	if err = cmd2.Start(); err != nil {
		return nil, err
	}
	if err = cmd1.Run(); err != nil {
		return nil, err
	}

	// Read the cmd2 output:
	out, err := ioutil.ReadAll(stdout)
	if err != nil {
		return nil, err
	}

	// Wait and return:
	if err = cmd2.Wait(); err != nil {
		return nil, err
	}

	return out, nil
}

//-----------------------------------------------------------------------------
// func: NewEtcdToken
//-----------------------------------------------------------------------------

// NewEtcdToken takes quorumCount and returns a valid etcd bootstrap token:
func NewEtcdToken(wg *sync.WaitGroup, quorumCount int, token *string) error {

	// Decrement:
	if wg != nil {
		defer wg.Done()
	}

	// Request an etcd bootstrap token:
	res, err := http.Get("https://discovery.etcd.io/new?size=" + strconv.Itoa(quorumCount))
	if err != nil {
		return err
	}

	// Retrieve the token URL:
	tokenURL, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}

	// Call the close method:
	_ = res.Body.Close()

	// Return the token ID:
	slice := strings.Split(string(tokenURL), "/")
	*token = slice[len(slice)-1]
	return nil
}

//-----------------------------------------------------------------------------
// func: LoadState
//-----------------------------------------------------------------------------

// LoadState reads the current ClusterID state file and decodes its content
// into a data structure:
func LoadState(clusterID string) ([]byte, error) {

	// Load data from state file:
	stateFile := os.Getenv("HOME") + "/.kato/" + clusterID + ".json"
	raw, err := ioutil.ReadFile(stateFile)
	if err != nil {
		return nil, err
	}

	return raw, nil
}

//-----------------------------------------------------------------------------
// func: OffsetIP
//-----------------------------------------------------------------------------

// OffsetIP takes a CIDR and an offset and returns the IP address at the offset
// position starting at the beginning of the CIDR's subnet:
func OffsetIP(cidr string, offset int) string {

	// Parse the CIDR:
	ip1, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		return ""
	}

	// Compute the IP:
	ip2 := ip1.Mask(ipnet.Mask)
	a := int(ipToI32(ip2[len(ip2)-4:]))

	// Return:
	return i32ToIP(int32(a + offset)).String()
}

func ipToI32(ip net.IP) int32 {
	ip = ip.To4()
	return int32(ip[0])<<24 | int32(ip[1])<<16 | int32(ip[2])<<8 | int32(ip[3])
}

func i32ToIP(a int32) net.IP {
	return net.IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
}
<commit_msg>Add comments to keep golint happy<commit_after>package kato

//---------------------------------------------------------------------------
// Package factored import statement:
//---------------------------------------------------------------------------

import (

	// Stdlib:
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
)

//-----------------------------------------------------------------------------
// func: CountNodes
//-----------------------------------------------------------------------------

// CountNodes returns the count of <role> nodes defined in <quads>.
func CountNodes(quads []string, role string) (count int) {

	// Default to zero:
	count = 0

	// Get the role count:
	for _, q := range quads {
		if strings.Contains(q, role) {
			s := strings.Split(q, ":")
			count, _ = strconv.Atoi(s[0])
			break
		}
	}

	return
}

//-----------------------------------------------------------------------------
// func: CreateDNSZones
//-----------------------------------------------------------------------------

// CreateDNSZones creates (int|ext).<domain> zones using <provider>.
func CreateDNSZones(wg *sync.WaitGroup, provider, apiKey, domain string) error {

	// Decrement:
	if wg != nil {
		defer wg.Done()
	}

	// Forge the zone command:
	cmd := exec.Command("katoctl", provider,
		"--api-key", apiKey, "zone", "add",
		domain, "int."+domain, "ext."+domain)

	// Execute the zone command:
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return err
	}

	return nil
}

//-----------------------------------------------------------------------------
// func: ExecutePipeline
//-----------------------------------------------------------------------------

// ExecutePipeline takes two commands and pipes the stdout of the first one
// into the stdin of the second one. Returns the output as []byte.
func ExecutePipeline(cmd1, cmd2 *exec.Cmd) ([]byte, error) {

	var err error

	// Adjust the stderr:
	cmd1.Stderr = os.Stderr
	cmd2.Stderr = os.Stderr

	// Connect both commands:
	cmd2.Stdin, err = cmd1.StdoutPipe()
	if err != nil {
		return nil, err
	}

	// Get cmd2 stdout:
	stdout, err := cmd2.StdoutPipe()
	if err != nil {
		return nil, err
	}

	// Execute the pipeline:
	if err = cmd2.Start(); err != nil {
		return nil, err
	}
	if err = cmd1.Run(); err != nil {
		return nil, err
	}

	// Read the cmd2 output:
	out, err := ioutil.ReadAll(stdout)
	if err != nil {
		return nil, err
	}

	// Wait and return:
	if err = cmd2.Wait(); err != nil {
		return nil, err
	}

	return out, nil
}

//-----------------------------------------------------------------------------
// func: NewEtcdToken
//-----------------------------------------------------------------------------

// NewEtcdToken takes quorumCount and returns a valid etcd bootstrap token:
func NewEtcdToken(wg *sync.WaitGroup, quorumCount int, token *string) error {

	// Decrement:
	if wg != nil {
		defer wg.Done()
	}

	// Request an etcd bootstrap token:
	res, err := http.Get("https://discovery.etcd.io/new?size=" + strconv.Itoa(quorumCount))
	if err != nil {
		return err
	}

	// Retrieve the token URL:
	tokenURL, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}

	// Call the close method:
	_ = res.Body.Close()

	// Return the token ID:
	slice := strings.Split(string(tokenURL), "/")
	*token = slice[len(slice)-1]
	return nil
}

//-----------------------------------------------------------------------------
// func: LoadState
//-----------------------------------------------------------------------------

// LoadState reads the current ClusterID state file and decodes its content
// into a data structure:
func LoadState(clusterID string) ([]byte, error) {

	// Load data from state file:
	stateFile := os.Getenv("HOME") + "/.kato/" + clusterID + ".json"
	raw, err := ioutil.ReadFile(stateFile)
	if err != nil {
		return nil, err
	}

	return raw, nil
}

//-----------------------------------------------------------------------------
// func: OffsetIP
//-----------------------------------------------------------------------------

// OffsetIP takes a CIDR and an offset and returns the IP address at the offset
// position starting at the beginning of the CIDR's subnet:
func OffsetIP(cidr string, offset int) string {

	// Parse the CIDR:
	ip1, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		return ""
	}

	// Compute the IP:
	ip2 := ip1.Mask(ipnet.Mask)
	a := int(ipToI32(ip2[len(ip2)-4:]))

	// Return:
	return i32ToIP(int32(a + offset)).String()
}

func ipToI32(ip net.IP) int32 {
	ip = ip.To4()
	return int32(ip[0])<<24 | int32(ip[1])<<16 | int32(ip[2])<<8 | int32(ip[3])
}

func i32ToIP(a int32) net.IP {
	return net.IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
}
<|endoftext|>


<commit_before>package command

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"reflect"
	"strings"
	"sync"
)

type CommandsInfo struct {
	Port int
}

type CommandInfo map[string]*struct {
	Token   string
	Options []string
}

type CommandRuntimeInfo struct {
	Token   string
	Handler interface{}
	Options map[string]string
}

type CommandServer struct {
	Common   CommandsInfo
	Command  CommandInfo
	Handlers map[string]*CommandRuntimeInfo
}

func NewServer(commands CommandsInfo, command CommandInfo) *CommandServer {
	server := &CommandServer{commands, command, map[string]*CommandRuntimeInfo{}}

	for k, v := range command {
		var parsed_options map[string]string
		for _, val := range v.Options {
			vals := strings.Split(val, ":")
			parsed_options[vals[0]] = vals[1]
		}
		server.Handlers[k] = &CommandRuntimeInfo{v.Token, nil, parsed_options}
	}

	server.registHandler("/echo", EchoCommand, nil)
	server.registHandler("/namu", NamuCommand, nil)
	server.registHandler("/zzal", ZzalCommand, nil)
	server.registHandler("/currency", CurrencyCommand, nil)

	return server
}

type HandlerInitializer func(*map[string]string)

func (server *CommandServer) registHandler(key string, handler interface{}, initializer HandlerInitializer) {
	if val, ok := server.Handlers[key]; ok {
		val.Handler = handler
	} else {
		log.Println("Warning : config not found for ", key)
		server.Handlers[key] = &CommandRuntimeInfo{"", handler, nil}
	}
	if initializer != nil {
		initializer(&server.Handlers[key].Options)
	}
}

func requestFormToRequestObj(r *http.Request) *Request {
	ret := new(Request)

	val := reflect.Indirect(reflect.ValueOf(ret))
	typ := reflect.TypeOf(*ret)

	for i := 0; i < typ.NumField(); i++ {
		field := val.Field(i)
		field_info := typ.Field(i)
		field_name := field_info.Tag.Get("param")
		field.Set(reflect.ValueOf(r.FormValue(field_name)))
	}

	return ret
}

func (server *CommandServer) commandHandler(w http.ResponseWriter, r *http.Request) {
	req := requestFormToRequestObj(r)
	handlerInfo := server.Handlers[req.Command]

	if handlerInfo != nil {
		if handlerInfo.Token == "" || handlerInfo.Token == req.Token {
			fun := reflect.ValueOf(handlerInfo.Handler)
			in := make([]reflect.Value, 1)
			in[0] = reflect.ValueOf(*req)
			response := fun.Call(in)[0].Interface().(*Response)

			var e error
			w.Header().Set("Content-Type", "application/json")
			if response.ResponseType != deffered_in_channel {
				encoder := json.NewEncoder(w)
				e = encoder.Encode(response)
			} else {
				var buf []byte
				buf, e = json.Marshal(response)
				http.Post(req.ResponseUrl, "application/json", bytes.NewBuffer(buf))
				log.Println("Deffered : ", string(buf))
			}

			if e != nil {
				log.Println("Error occured : ", req, e)
			}
		}
	}
}

func (server *CommandServer) Start(wg *sync.WaitGroup) {
	http.HandleFunc("/", server.commandHandler)
	http.ListenAndServe(fmt.Sprintf(":%d", server.Common.Port), nil)

	wg.Done()
}
<commit_msg>Comment command.go<commit_after>package command

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"reflect"
	"strings"
	"sync"
)

// Command server info
//
// use with gcfg
type CommandsInfo struct {
	Port int
}

// Each command processor info
//
// user with gcfg
type CommandInfo map[string]*struct {
	Token   string
	// "key=val" list
	Options []string
}

// Command processor info using at runtime
type CommandRuntimeInfo struct {
	Token   string
	Handler interface{}
	Options map[string]string
}

// Command server object
type CommandServer struct {
	Common   CommandsInfo
	Command  CommandInfo
	Handlers map[string]*CommandRuntimeInfo
}

// Create new server
func NewServer(commands CommandsInfo, command CommandInfo) *CommandServer {
	server := &CommandServer{commands, command, map[string]*CommandRuntimeInfo{}}

	// set config from config file.
	for k, v := range command {
		var parsed_options map[string]string
		for _, val := range v.Options {
			vals := strings.Split(val, ":")
			parsed_options[vals[0]] = vals[1]
		}
		server.Handlers[k] = &CommandRuntimeInfo{v.Token, nil, parsed_options}
	}

	// Regist processor
	//
	// Reflection of golang can't find function with name.
	// manually regist all available handler here.
	server.registHandler("/echo", EchoCommand, nil)
	server.registHandler("/namu", NamuCommand, nil)
	server.registHandler("/zzal", ZzalCommand, nil)
	server.registHandler("/currency", CurrencyCommand, nil)

	return server
}

type HandlerInitializer func(*map[string]string)

func (server *CommandServer) registHandler(key string, handler interface{}, initializer HandlerInitializer) {
	if val, ok := server.Handlers[key]; ok {
		val.Handler = handler
	} else {
		log.Println("Warning : config not found for ", key)
		server.Handlers[key] = &CommandRuntimeInfo{"", handler, nil}
	}
	if initializer != nil {
		initializer(&server.Handlers[key].Options)
	}
}

func requestFormToRequestObj(r *http.Request) *Request {
	ret := new(Request)

	val := reflect.Indirect(reflect.ValueOf(ret))
	typ := reflect.TypeOf(*ret)

	for i := 0; i < typ.NumField(); i++ {
		field := val.Field(i)
		field_info := typ.Field(i)
		field_name := field_info.Tag.Get("param")
		field.Set(reflect.ValueOf(r.FormValue(field_name)))
	}

	return ret
}

// Comman request handler
func (server *CommandServer) commandHandler(w http.ResponseWriter, r *http.Request) {
	// Parse request
	req := requestFormToRequestObj(r)
	// retrieve command handler for request
	handlerInfo := server.Handlers[req.Command]

	// exist handler
	if handlerInfo != nil {
		// token check
		if handlerInfo.Token == "" || handlerInfo.Token == req.Token {
			// invoke handler
			fun := reflect.ValueOf(handlerInfo.Handler)
			in := make([]reflect.Value, 1)
			in[0] = reflect.ValueOf(*req)
			response := fun.Call(in)[0].Interface().(*Response)

			// create response
			var e error
			w.Header().Set("Content-Type", "application/json")
			if response.ResponseType != deffered_in_channel {
				encoder := json.NewEncoder(w)
				e = encoder.Encode(response)
			} else {
				// special case - deffered_in_channel
				var buf []byte
				buf, e = json.Marshal(response)
				http.Post(req.ResponseUrl, "application/json", bytes.NewBuffer(buf))
				log.Println("Deffered : ", string(buf))
			}

			if e != nil {
				log.Println("Error occured : ", req, e)
			}
		}
	}
}

// start server
func (server *CommandServer) Start(wg *sync.WaitGroup) {
	http.HandleFunc("/", server.commandHandler)
	http.ListenAndServe(fmt.Sprintf(":%d", server.Common.Port), nil)

	wg.Done()
}
<|endoftext|>