<commit_before>// +build integration

package tests

import (
	"io/ioutil"
	"testing"

	"github.com/deis/deis/tests/utils"
)

var (
	configListCmd         = "config:list --app={{.AppName}}"
	configSetCmd          = "config:set FOO=讲台 --app={{.AppName}}"
	configSet2Cmd         = "config:set FOO=10 --app={{.AppName}}"
	configSet3Cmd         = "config:set POWERED_BY=\"the Deis team\" --app={{.AppName}}"
	configSetBuildpackCmd = "config:set BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-go#98f37cc --app={{.AppName}}"
	configUnsetCmd        = "config:unset FOO --app={{.AppName}}"
)

func TestConfig(t *testing.T) {
	params := configSetup(t)
	configSetTest(t, params)
	configPushTest(t, params)
	configListTest(t, params, false)
	appsOpenTest(t, params)
	configUnsetTest(t, params)
	configListTest(t, params, true)
	limitsSetTest(t, params, 4)
	appsOpenTest(t, params)
	limitsUnsetTest(t, params, 6)
	appsOpenTest(t, params)
	//tagsTest(t, params, 8)
	appsOpenTest(t, params)
	utils.AppsDestroyTest(t, params)
}

func configSetup(t *testing.T) *utils.DeisTestConfig {
	cfg := utils.GetGlobalConfig()
	cfg.AppName = "configsample"
	utils.Execute(t, authLoginCmd, cfg, false, "")
	utils.Execute(t, gitCloneCmd, cfg, false, "")
	if err := utils.Chdir(cfg.ExampleApp); err != nil {
		t.Fatal(err)
	}
	utils.Execute(t, appsCreateCmd, cfg, false, "")
	// ensure envvars with spaces work fine on `git push`
	// https://github.com/deis/deis/issues/2477
	utils.Execute(t, configSet3Cmd, cfg, false, "the Deis team")
	// ensure custom buildpack URLS are in order
	utils.Execute(t, configSetBuildpackCmd, cfg, false, "https://github.com/heroku/heroku-buildpack-go#98f37cc")
	utils.Execute(t, gitPushCmd, cfg, false, "")
	utils.CurlApp(t, *cfg)
	utils.CheckList(t, "run env --app={{.AppName}}", cfg, "DEIS_APP", false)
	utils.CheckList(t, "run env --app={{.AppName}}", cfg, "DEIS_RELEASE", false)
	if err := utils.Chdir(".."); err != nil {
		t.Fatal(err)
	}
	return cfg
}

func configListTest(
	t *testing.T, params *utils.DeisTestConfig, notflag bool) {
	utils.CheckList(t, configListCmd, params, "FOO", notflag)
}

func configSetTest(t *testing.T, params *utils.DeisTestConfig) {
	utils.Execute(t, configSetCmd, params, false, "讲台")
	utils.CheckList(t, appsInfoCmd, params, "(v5)", false)
	utils.Execute(t, configSet2Cmd, params, false, "10")
	utils.CheckList(t, appsInfoCmd, params, "(v6)", false)
}

func configPushTest(t *testing.T, params *utils.DeisTestConfig) {
	if err := utils.Chdir(params.ExampleApp); err != nil {
		t.Fatal(err)
	}
	// create a .env in the project root
	if err := ioutil.WriteFile(".env", []byte("POWERED_BY=Deis"), 0664); err != nil {
		t.Fatal(err)
	}
	utils.Execute(t, "config:push --app {{.AppName}}", params, false, "Deis")
	utils.CheckList(t, appsInfoCmd, params, "(v7)", false)
	if err := utils.Chdir(".."); err != nil {
		t.Fatal(err)
	}
}

func configUnsetTest(t *testing.T, params *utils.DeisTestConfig) {
	utils.Execute(t, configUnsetCmd, params, false, "")
	utils.CheckList(t, appsInfoCmd, params, "(v8)", false)
	utils.CheckList(t, "run env --app={{.AppName}}", params, "FOO", true)
}
<commit_msg>fix(tests): test custom buildpacks for example Procfile apps<commit_after>// +build integration

package tests

import (
	"io/ioutil"
	"strings"
	"testing"

	"github.com/deis/deis/tests/utils"
)

var (
	configListCmd         = "config:list --app={{.AppName}}"
	configSetCmd          = "config:set FOO=讲台 --app={{.AppName}}"
	configSet2Cmd         = "config:set FOO=10 --app={{.AppName}}"
	configSet3Cmd         = "config:set POWERED_BY=\"the Deis team\" --app={{.AppName}}"
	configSetBuildpackCmd = "config:set BUILDPACK_URL=$BUILDPACK_URL --app={{.AppName}}"
	configUnsetCmd        = "config:unset FOO --app={{.AppName}}"
)

var buildpacks = map[string]string{
	"example-clojure-ring":   "https://github.com/heroku/heroku-buildpack-clojure#v66",
	"example-go":             "https://github.com/heroku/heroku-buildpack-go#6eeb09f",
	"example-java-jetty":     "https://github.com/heroku/heroku-buildpack-java#v38",
	"example-nodejs-express": "https://github.com/heroku/heroku-buildpack-nodejs#v75",
	"example-perl":           "https://github.com/miyagawa/heroku-buildpack-perl#2da7480",
	"example-php":            "https://github.com/heroku/heroku-buildpack-php#v67",
	"example-play":           "https://github.com/heroku/heroku-buildpack-play#v23",
	"example-python-django":  "https://github.com/heroku/heroku-buildpack-python#v58",
	"example-python-flask":   "https://github.com/heroku/heroku-buildpack-python#v58",
	"example-ruby-sinatra":   "https://github.com/heroku/heroku-buildpack-ruby#v137",
	"example-scala":          "https://github.com/heroku/heroku-buildpack-scala#v55",
}

func TestConfig(t *testing.T) {
	params := configSetup(t)
	configSetTest(t, params)
	configPushTest(t, params)
	configListTest(t, params, false)
	appsOpenTest(t, params)
	configUnsetTest(t, params)
	configListTest(t, params, true)
	limitsSetTest(t, params, 4)
	appsOpenTest(t, params)
	limitsUnsetTest(t, params, 6)
	appsOpenTest(t, params)
	//tagsTest(t, params, 8)
	appsOpenTest(t, params)
	utils.AppsDestroyTest(t, params)
}

func configSetup(t *testing.T) *utils.DeisTestConfig {
	cfg := utils.GetGlobalConfig()
	cfg.AppName = "configsample"
	utils.Execute(t, authLoginCmd, cfg, false, "")
	utils.Execute(t, gitCloneCmd, cfg, false, "")
	if err := utils.Chdir(cfg.ExampleApp); err != nil {
		t.Fatal(err)
	}
	utils.Execute(t, appsCreateCmd, cfg, false, "")
	// ensure envvars with spaces work fine on `git push`
	// https://github.com/deis/deis/issues/2477
	utils.Execute(t, configSet3Cmd, cfg, false, "the Deis team")
	// ensure custom buildpack URLs are in order
	url := buildpacks[cfg.ExampleApp]
	if url == "" {
		// set url anyway so example-dockerfile apps create a build
		url = buildpacks["example-go"]
	}
	cmd := strings.Replace(configSetBuildpackCmd, "$BUILDPACK_URL", url, 1)
	utils.Execute(t, cmd, cfg, false, url)
	utils.Execute(t, gitPushCmd, cfg, false, "")
	utils.CurlApp(t, *cfg)
	utils.CheckList(t, "run env --app={{.AppName}}", cfg, "DEIS_APP", false)
	utils.CheckList(t, "run env --app={{.AppName}}", cfg, "DEIS_RELEASE", false)
	if err := utils.Chdir(".."); err != nil {
		t.Fatal(err)
	}
	return cfg
}

func configListTest(
	t *testing.T, params *utils.DeisTestConfig, notflag bool) {
	utils.CheckList(t, configListCmd, params, "FOO", notflag)
}

func configSetTest(t *testing.T, params *utils.DeisTestConfig) {
	utils.Execute(t, configSetCmd, params, false, "讲台")
	utils.CheckList(t, appsInfoCmd, params, "(v5)", false)
	utils.Execute(t, configSet2Cmd, params, false, "10")
	utils.CheckList(t, appsInfoCmd, params, "(v6)", false)
}

func configPushTest(t *testing.T, params *utils.DeisTestConfig) {
	if err := utils.Chdir(params.ExampleApp); err != nil {
		t.Fatal(err)
	}
	// create a .env in the project root
	if err := ioutil.WriteFile(".env", []byte("POWERED_BY=Deis"), 0664); err != nil {
		t.Fatal(err)
	}
	utils.Execute(t, "config:push --app {{.AppName}}", params, false, "Deis")
	utils.CheckList(t, appsInfoCmd, params, "(v7)", false)
	if err := utils.Chdir(".."); err != nil {
		t.Fatal(err)
	}
}

func configUnsetTest(t *testing.T, params *utils.DeisTestConfig) {
	utils.Execute(t, configUnsetCmd, params, false, "")
	utils.CheckList(t, appsInfoCmd, params, "(v8)", false)
	utils.CheckList(t, "run env --app={{.AppName}}", params, "FOO", true)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package diskmanagers

import (
	"context"
	"time"

	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/vim25/types"

	"k8s.io/klog/v2"
	"k8s.io/legacy-cloud-providers/vsphere/vclib"
)

// virtualDiskManager implements VirtualDiskProvider Interface for creating and deleting volume using VirtualDiskManager
type virtualDiskManager struct {
	diskPath      string
	volumeOptions *vclib.VolumeOptions
}

// Create implements Disk's Create interface
// Contains implementation of virtualDiskManager based Provisioning
func (diskManager virtualDiskManager) Create(ctx context.Context, datastore *vclib.Datastore) (canonicalDiskPath string, err error) {
	if diskManager.volumeOptions.SCSIControllerType == "" {
		diskManager.volumeOptions.SCSIControllerType = vclib.LSILogicControllerType
	}

	// Check for existing VMDK before attempting create. Because a name collision
	// is unlikely, "VMDK already exists" is likely from a previous attempt to
	// create this volume.
	if dsPath := vclib.GetPathFromVMDiskPath(diskManager.diskPath); datastore.Exists(ctx, dsPath) {
		klog.V(2).Infof("Create: VirtualDisk already exists, returning success. Name=%q", diskManager.diskPath)
		return diskManager.diskPath, nil
	}

	// Create specification for new virtual disk
	diskFormat := vclib.DiskFormatValidType[diskManager.volumeOptions.DiskFormat]
	vmDiskSpec := &types.FileBackedVirtualDiskSpec{
		VirtualDiskSpec: types.VirtualDiskSpec{
			AdapterType: diskManager.volumeOptions.SCSIControllerType,
			DiskType:    diskFormat,
		},
		CapacityKb: int64(diskManager.volumeOptions.CapacityKB),
	}

	vdm := object.NewVirtualDiskManager(datastore.Client())
	requestTime := time.Now()
	// Create virtual disk
	task, err := vdm.CreateVirtualDisk(ctx, diskManager.diskPath, datastore.Datacenter.Datacenter, vmDiskSpec)
	if err != nil {
		vclib.RecordvSphereMetric(vclib.APICreateVolume, requestTime, err)
		klog.Errorf("Failed to create virtual disk: %s. err: %+v", diskManager.diskPath, err)
		return "", err
	}
	taskInfo, err := task.WaitForResult(ctx, nil)
	vclib.RecordvSphereMetric(vclib.APICreateVolume, requestTime, err)
	if err != nil {
		klog.Errorf("Failed to complete virtual disk creation: %s. err: %+v", diskManager.diskPath, err)
		return "", err
	}
	canonicalDiskPath = taskInfo.Result.(string)
	return canonicalDiskPath, nil
}

// Delete implements Disk's Delete interface
func (diskManager virtualDiskManager) Delete(ctx context.Context, datacenter *vclib.Datacenter) error {
	// Create a virtual disk manager
	virtualDiskManager := object.NewVirtualDiskManager(datacenter.Client())
	diskPath := vclib.RemoveStorageClusterORFolderNameFromVDiskPath(diskManager.diskPath)
	requestTime := time.Now()
	// Delete virtual disk
	task, err := virtualDiskManager.DeleteVirtualDisk(ctx, diskPath, datacenter.Datacenter)
	if err != nil {
		klog.Errorf("Failed to delete virtual disk. err: %v", err)
		vclib.RecordvSphereMetric(vclib.APIDeleteVolume, requestTime, err)
		return err
	}
	err = task.Wait(ctx)
	vclib.RecordvSphereMetric(vclib.APIDeleteVolume, requestTime, err)
	if err != nil {
		klog.Errorf("Failed to delete virtual disk. err: %v", err)
		return err
	}
	return nil
}
<commit_msg>VSphere cloud provider: Fix race in disk provisioning<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package diskmanagers

import (
	"context"
	"time"

	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/vim25/types"

	"k8s.io/klog/v2"
	"k8s.io/legacy-cloud-providers/vsphere/vclib"
)

// virtualDiskManager implements VirtualDiskProvider Interface for creating and deleting volume using VirtualDiskManager
type virtualDiskManager struct {
	diskPath      string
	volumeOptions *vclib.VolumeOptions
}

// Create implements Disk's Create interface
// Contains implementation of virtualDiskManager based Provisioning
func (diskManager virtualDiskManager) Create(ctx context.Context, datastore *vclib.Datastore) (canonicalDiskPath string, err error) {
	if diskManager.volumeOptions.SCSIControllerType == "" {
		diskManager.volumeOptions.SCSIControllerType = vclib.LSILogicControllerType
	}

	// Check for existing VMDK before attempting create. Because a name collision
	// is unlikely, "VMDK already exists" is likely from a previous attempt to
	// create this volume.
	if dsPath := vclib.GetPathFromVMDiskPath(diskManager.diskPath); datastore.Exists(ctx, dsPath) {
		klog.V(2).Infof("Create: VirtualDisk already exists, returning success. Name=%q", diskManager.diskPath)
		return diskManager.diskPath, nil
	}

	// Create specification for new virtual disk
	diskFormat := vclib.DiskFormatValidType[diskManager.volumeOptions.DiskFormat]
	vmDiskSpec := &types.FileBackedVirtualDiskSpec{
		VirtualDiskSpec: types.VirtualDiskSpec{
			AdapterType: diskManager.volumeOptions.SCSIControllerType,
			DiskType:    diskFormat,
		},
		CapacityKb: int64(diskManager.volumeOptions.CapacityKB),
	}

	vdm := object.NewVirtualDiskManager(datastore.Client())
	requestTime := time.Now()
	// Create virtual disk
	task, err := vdm.CreateVirtualDisk(ctx, diskManager.diskPath, datastore.Datacenter.Datacenter, vmDiskSpec)
	if err != nil {
		vclib.RecordvSphereMetric(vclib.APICreateVolume, requestTime, err)
		klog.Errorf("Failed to create virtual disk: %s. err: %+v", diskManager.diskPath, err)
		return "", err
	}
	taskInfo, err := task.WaitForResult(ctx, nil)
	vclib.RecordvSphereMetric(vclib.APICreateVolume, requestTime, err)
	if err != nil {
		if isAlreadyExists(diskManager.diskPath, err) {
			// The disk already exists, log info message and return success
			klog.V(vclib.LogLevel).Infof("File: %v already exists", diskManager.diskPath)
			return diskManager.diskPath, nil
		}
		klog.Errorf("Failed to complete virtual disk creation: %s. err: %+v", diskManager.diskPath, err)
		return "", err
	}
	canonicalDiskPath = taskInfo.Result.(string)
	return canonicalDiskPath, nil
}

// Delete implements Disk's Delete interface
func (diskManager virtualDiskManager) Delete(ctx context.Context, datacenter *vclib.Datacenter) error {
	// Create a virtual disk manager
	virtualDiskManager := object.NewVirtualDiskManager(datacenter.Client())
	diskPath := vclib.RemoveStorageClusterORFolderNameFromVDiskPath(diskManager.diskPath)
	requestTime := time.Now()
	// Delete virtual disk
	task, err := virtualDiskManager.DeleteVirtualDisk(ctx, diskPath, datacenter.Datacenter)
	if err != nil {
		klog.Errorf("Failed to delete virtual disk. err: %v", err)
		vclib.RecordvSphereMetric(vclib.APIDeleteVolume, requestTime, err)
		return err
	}
	err = task.Wait(ctx)
	vclib.RecordvSphereMetric(vclib.APIDeleteVolume, requestTime, err)
	if err != nil {
		klog.Errorf("Failed to delete virtual disk. err: %v", err)
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// +build linux

package selinux

import (
	"bufio"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"syscall"

	"github.com/docker/docker/pkg/mount"
	"github.com/docker/libcontainer/system"
)

const (
	Enforcing        = 1
	Permissive       = 0
	Disabled         = -1
	selinuxDir       = "/etc/selinux/"
	selinuxConfig    = selinuxDir + "config"
	selinuxTypeTag   = "SELINUXTYPE"
	selinuxTag       = "SELINUX"
	selinuxPath      = "/sys/fs/selinux"
	xattrNameSelinux = "security.selinux"
	stRdOnly         = 0x01
)

var (
	assignRegex           = regexp.MustCompile(`^([^=]+)=(.*)$`)
	spaceRegex            = regexp.MustCompile(`^([^=]+) (.*)$`)
	mcsList               = make(map[string]bool)
	selinuxfs             = "unknown"
	selinuxEnabled        = false // Stores whether selinux is currently enabled
	selinuxEnabledChecked = false // Stores whether selinux enablement has been checked or established yet
)

type SELinuxContext map[string]string

// SetDisabled disables selinux support for the package
func SetDisabled() {
	selinuxEnabled, selinuxEnabledChecked = false, true
}

// getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs
// filesystem or an empty string if no mountpoint is found.  Selinuxfs is
// a proc-like pseudo-filesystem that exposes the selinux policy API to
// processes.  The existence of an selinuxfs mount is used to determine
// whether selinux is currently enabled or not.
func getSelinuxMountPoint() string {
	if selinuxfs != "unknown" {
		return selinuxfs
	}
	selinuxfs = ""

	mounts, err := mount.GetMounts()
	if err != nil {
		return selinuxfs
	}
	for _, mount := range mounts {
		if mount.Fstype == "selinuxfs" {
			selinuxfs = mount.Mountpoint
			break
		}
	}
	if selinuxfs != "" {
		var buf syscall.Statfs_t
		syscall.Statfs(selinuxfs, &buf)
		if (buf.Flags & stRdOnly) == 1 {
			selinuxfs = ""
		}
	}
	return selinuxfs
}

// SelinuxEnabled returns whether selinux is currently enabled.
func SelinuxEnabled() bool {
	if selinuxEnabledChecked {
		return selinuxEnabled
	}
	selinuxEnabledChecked = true
	if fs := getSelinuxMountPoint(); fs != "" {
		if con, _ := Getcon(); con != "kernel" {
			selinuxEnabled = true
		}
	}
	return selinuxEnabled
}

func readConfig(target string) (value string) {
	var (
		val, key string
		bufin    *bufio.Reader
	)

	in, err := os.Open(selinuxConfig)
	if err != nil {
		return ""
	}
	defer in.Close()

	bufin = bufio.NewReader(in)

	for done := false; !done; {
		var line string
		if line, err = bufin.ReadString('\n'); err != nil {
			if err != io.EOF {
				return ""
			}
			done = true
		}
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			// Skip blank lines
			continue
		}
		if line[0] == ';' || line[0] == '#' {
			// Skip comments
			continue
		}
		if groups := assignRegex.FindStringSubmatch(line); groups != nil {
			key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
			if key == target {
				return strings.Trim(val, "\"")
			}
		}
	}
	return ""
}

func getSELinuxPolicyRoot() string {
	return selinuxDir + readConfig(selinuxTypeTag)
}

func readCon(name string) (string, error) {
	var val string

	in, err := os.Open(name)
	if err != nil {
		return "", err
	}
	defer in.Close()

	_, err = fmt.Fscanf(in, "%s", &val)
	return val, err
}

// Setfilecon sets the SELinux label for this path or returns an error.
func Setfilecon(path string, scon string) error {
	return system.Lsetxattr(path, xattrNameSelinux, []byte(scon), 0)
}

// Getfilecon returns the SELinux label for this path or returns an error.
func Getfilecon(path string) (string, error) {
	con, err := system.Lgetxattr(path, xattrNameSelinux)
	return string(con), err
}

func Setfscreatecon(scon string) error {
	return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), scon)
}

func Getfscreatecon() (string, error) {
	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()))
}

// Getcon returns the SELinux label of the current process thread, or an error.
func Getcon() (string, error) {
	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()))
}

// Getpidcon returns the SELinux label of the given pid, or an error.
func Getpidcon(pid int) (string, error) {
	return readCon(fmt.Sprintf("/proc/%d/attr/current", pid))
}

func Getexeccon() (string, error) {
	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()))
}

func writeCon(name string, val string) error {
	out, err := os.OpenFile(name, os.O_WRONLY, 0)
	if err != nil {
		return err
	}
	defer out.Close()

	if val != "" {
		_, err = out.Write([]byte(val))
	} else {
		_, err = out.Write(nil)
	}
	return err
}

func Setexeccon(scon string) error {
	return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), scon)
}

func (c SELinuxContext) Get() string {
	return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
}

func NewContext(scon string) SELinuxContext {
	c := make(SELinuxContext)

	if len(scon) != 0 {
		con := strings.SplitN(scon, ":", 4)
		c["user"] = con[0]
		c["role"] = con[1]
		c["type"] = con[2]
		c["level"] = con[3]
	}
	return c
}

func ReserveLabel(scon string) {
	if len(scon) != 0 {
		con := strings.SplitN(scon, ":", 4)
		mcsAdd(con[3])
	}
}

func SelinuxGetEnforce() int {
	var enforce int

	enforceS, err := readCon(fmt.Sprintf("%s/enforce", selinuxPath))
	if err != nil {
		return -1
	}

	enforce, err = strconv.Atoi(string(enforceS))
	if err != nil {
		return -1
	}
	return enforce
}

func SelinuxGetEnforceMode() int {
	switch readConfig(selinuxTag) {
	case "enforcing":
		return Enforcing
	case "permissive":
		return Permissive
	}
	return Disabled
}

func mcsAdd(mcs string) error {
	if mcsList[mcs] {
		return fmt.Errorf("MCS Label already exists")
	}
	mcsList[mcs] = true
	return nil
}

func mcsDelete(mcs string) {
	mcsList[mcs] = false
}

func mcsExists(mcs string) bool {
	return mcsList[mcs]
}

func IntToMcs(id int, catRange uint32) string {
	var (
		SETSIZE = int(catRange)
		TIER    = SETSIZE
		ORD     = id
	)

	if id < 1 || id > 523776 {
		return ""
	}

	for ORD > TIER {
		ORD = ORD - TIER
		TIER -= 1
	}
	TIER = SETSIZE - TIER
	ORD = ORD + TIER
	return fmt.Sprintf("s0:c%d,c%d", TIER, ORD)
}

func uniqMcs(catRange uint32) string {
	var (
		n      uint32
		c1, c2 uint32
		mcs    string
	)

	for {
		binary.Read(rand.Reader, binary.LittleEndian, &n)
		c1 = n % catRange
		binary.Read(rand.Reader, binary.LittleEndian, &n)
		c2 = n % catRange
		if c1 == c2 {
			continue
		} else {
			if c1 > c2 {
				t := c1
				c1 = c2
				c2 = t
			}
		}
		mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
		if err := mcsAdd(mcs); err != nil {
			continue
		}
		break
	}
	return mcs
}

func FreeLxcContexts(scon string) {
	if len(scon) != 0 {
		con := strings.SplitN(scon, ":", 4)
		mcsDelete(con[3])
	}
}

func GetLxcContexts() (processLabel string, fileLabel string) {
	var (
		val, key string
		bufin    *bufio.Reader
	)

	if !SelinuxEnabled() {
		return "", ""
	}
	lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot())
	in, err := os.Open(lxcPath)
	if err != nil {
		return "", ""
	}
	defer in.Close()

	bufin = bufio.NewReader(in)

	for done := false; !done; {
		var line string
		if line, err = bufin.ReadString('\n'); err != nil {
			if err == io.EOF {
				done = true
			} else {
				goto exit
			}
		}
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			// Skip blank lines
			continue
		}
		if line[0] == ';' || line[0] == '#' {
			// Skip comments
			continue
		}
		if groups := assignRegex.FindStringSubmatch(line); groups != nil {
			key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
			if key == "process" {
				processLabel = strings.Trim(val, "\"")
			}
			if key == "file" {
				fileLabel = strings.Trim(val, "\"")
			}
		}
	}

	if processLabel == "" || fileLabel == "" {
		return "", ""
	}

exit:
	//	mcs := IntToMcs(os.Getpid(), 1024)
	mcs := uniqMcs(1024)
	scon := NewContext(processLabel)
	scon["level"] = mcs
	processLabel = scon.Get()
	scon = NewContext(fileLabel)
	scon["level"] = mcs
	fileLabel = scon.Get()
	return processLabel, fileLabel
}

func SecurityCheckContext(val string) error {
	return writeCon(fmt.Sprintf("%s.context", selinuxPath), val)
}

func CopyLevel(src, dest string) (string, error) {
	if src == "" {
		return "", nil
	}
	if err := SecurityCheckContext(src); err != nil {
		return "", err
	}
	if err := SecurityCheckContext(dest); err != nil {
		return "", err
	}
	scon := NewContext(src)
	tcon := NewContext(dest)
	mcsDelete(tcon["level"])
	mcsAdd(scon["level"])
	tcon["level"] = scon["level"]
	return tcon.Get(), nil
}

// Prevent users from relabing system files
func badPrefix(fpath string) error {
	var badprefixes = []string{"/usr"}

	for _, prefix := range badprefixes {
		if fpath == prefix || strings.HasPrefix(fpath, fmt.Sprintf("%s/", prefix)) {
			return fmt.Errorf("Relabeling content in %s is not allowed.", prefix)
		}
	}
	return nil
}

// Change the fpath file object to the SELinux label scon.
// If the fpath is a directory and recurse is true Chcon will walk the
// directory tree setting the label
func Chcon(fpath string, scon string, recurse bool) error {
	if scon == "" {
		return nil
	}
	if err := badPrefix(fpath); err != nil {
		return err
	}
	callback := func(p string, info os.FileInfo, err error) error {
		return Setfilecon(p, scon)
	}

	if recurse {
		return filepath.Walk(fpath, callback)
	}

	return Setfilecon(fpath, scon)
}

// DupSecOpt takes an SELinux process label and returns security options that
// can will set the SELinux Type and Level for future container processes
func DupSecOpt(src string) []string {
	if src == "" {
		return nil
	}
	con := NewContext(src)
	if con["user"] == "" ||
		con["role"] == "" ||
		con["type"] == "" ||
		con["level"] == "" {
		return nil
	}
	return []string{"label:user:" + con["user"],
		"label:role:" + con["role"],
		"label:type:" + con["type"],
		"label:level:" + con["level"]}
}

// DisableSecOpt returns a security opt that can be used to disabling SELinux
// labeling support for future container processes
func DisableSecOpt() []string {
	return []string{"label:disable"}
}
<commit_msg>selinux: Adds a check for a NUL byte at the end of the string and removes it.<commit_after>// +build linux

package selinux

import (
	"bufio"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"syscall"

	"github.com/docker/docker/pkg/mount"
	"github.com/docker/libcontainer/system"
)

const (
	Enforcing        = 1
	Permissive       = 0
	Disabled         = -1
	selinuxDir       = "/etc/selinux/"
	selinuxConfig    = selinuxDir + "config"
	selinuxTypeTag   = "SELINUXTYPE"
	selinuxTag       = "SELINUX"
	selinuxPath      = "/sys/fs/selinux"
	xattrNameSelinux = "security.selinux"
	stRdOnly         = 0x01
)

var (
	assignRegex           = regexp.MustCompile(`^([^=]+)=(.*)$`)
	spaceRegex            = regexp.MustCompile(`^([^=]+) (.*)$`)
	mcsList               = make(map[string]bool)
	selinuxfs             = "unknown"
	selinuxEnabled        = false // Stores whether selinux is currently enabled
	selinuxEnabledChecked = false // Stores whether selinux enablement has been checked or established yet
)

type SELinuxContext map[string]string

// SetDisabled disables selinux support for the package
func SetDisabled() {
	selinuxEnabled, selinuxEnabledChecked = false, true
}

// getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs
// filesystem or an empty string if no mountpoint is found.  Selinuxfs is
// a proc-like pseudo-filesystem that exposes the selinux policy API to
// processes.  The existence of an selinuxfs mount is used to determine
// whether selinux is currently enabled or not.
func getSelinuxMountPoint() string {
	if selinuxfs != "unknown" {
		return selinuxfs
	}
	selinuxfs = ""

	mounts, err := mount.GetMounts()
	if err != nil {
		return selinuxfs
	}
	for _, mount := range mounts {
		if mount.Fstype == "selinuxfs" {
			selinuxfs = mount.Mountpoint
			break
		}
	}
	if selinuxfs != "" {
		var buf syscall.Statfs_t
		syscall.Statfs(selinuxfs, &buf)
		if (buf.Flags & stRdOnly) == 1 {
			selinuxfs = ""
		}
	}
	return selinuxfs
}

// SelinuxEnabled returns whether selinux is currently enabled.
func SelinuxEnabled() bool {
	if selinuxEnabledChecked {
		return selinuxEnabled
	}
	selinuxEnabledChecked = true
	if fs := getSelinuxMountPoint(); fs != "" {
		if con, _ := Getcon(); con != "kernel" {
			selinuxEnabled = true
		}
	}
	return selinuxEnabled
}

func readConfig(target string) (value string) {
	var (
		val, key string
		bufin    *bufio.Reader
	)

	in, err := os.Open(selinuxConfig)
	if err != nil {
		return ""
	}
	defer in.Close()

	bufin = bufio.NewReader(in)

	for done := false; !done; {
		var line string
		if line, err = bufin.ReadString('\n'); err != nil {
			if err != io.EOF {
				return ""
			}
			done = true
		}
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			// Skip blank lines
			continue
		}
		if line[0] == ';' || line[0] == '#' {
			// Skip comments
			continue
		}
		if groups := assignRegex.FindStringSubmatch(line); groups != nil {
			key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
			if key == target {
				return strings.Trim(val, "\"")
			}
		}
	}
	return ""
}

func getSELinuxPolicyRoot() string {
	return selinuxDir + readConfig(selinuxTypeTag)
}

func readCon(name string) (string, error) {
	var val string

	in, err := os.Open(name)
	if err != nil {
		return "", err
	}
	defer in.Close()

	_, err = fmt.Fscanf(in, "%s", &val)
	return val, err
}

// Setfilecon sets the SELinux label for this path or returns an error.
func Setfilecon(path string, scon string) error {
	return system.Lsetxattr(path, xattrNameSelinux, []byte(scon), 0)
}

// Getfilecon returns the SELinux label for this path or returns an error.
func Getfilecon(path string) (string, error) {
	con, err := system.Lgetxattr(path, xattrNameSelinux)

	// Trim the NUL byte at the end of the byte buffer, if present.
	if con[len(con)-1] == '\x00' {
		con = con[:len(con)-1]
	}
	return string(con), err
}

func Setfscreatecon(scon string) error {
	return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), scon)
}

func Getfscreatecon() (string, error) {
	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()))
}

// Getcon returns the SELinux label of the current process thread, or an error.
func Getcon() (string, error) {
	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()))
}

// Getpidcon returns the SELinux label of the given pid, or an error.
func Getpidcon(pid int) (string, error) {
	return readCon(fmt.Sprintf("/proc/%d/attr/current", pid))
}

func Getexeccon() (string, error) {
	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()))
}

func writeCon(name string, val string) error {
	out, err := os.OpenFile(name, os.O_WRONLY, 0)
	if err != nil {
		return err
	}
	defer out.Close()

	if val != "" {
		_, err = out.Write([]byte(val))
	} else {
		_, err = out.Write(nil)
	}
	return err
}

func Setexeccon(scon string) error {
	return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), scon)
}

func (c SELinuxContext) Get() string {
	return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
}

func NewContext(scon string) SELinuxContext {
	c := make(SELinuxContext)

	if len(scon) != 0 {
		con := strings.SplitN(scon, ":", 4)
		c["user"] = con[0]
		c["role"] = con[1]
		c["type"] = con[2]
		c["level"] = con[3]
	}
	return c
}

func ReserveLabel(scon string) {
	if len(scon) != 0 {
		con := strings.SplitN(scon, ":", 4)
		mcsAdd(con[3])
	}
}

func SelinuxGetEnforce() int {
	var enforce int

	enforceS, err := readCon(fmt.Sprintf("%s/enforce", selinuxPath))
	if err != nil {
		return -1
	}

	enforce, err = strconv.Atoi(string(enforceS))
	if err != nil {
		return -1
	}
	return enforce
}

func SelinuxGetEnforceMode() int {
	switch readConfig(selinuxTag) {
	case "enforcing":
		return Enforcing
	case "permissive":
		return Permissive
	}
	return Disabled
}

func mcsAdd(mcs string) error {
	if mcsList[mcs] {
		return fmt.Errorf("MCS Label already exists")
	}
	mcsList[mcs] = true
	return nil
}

func mcsDelete(mcs string) {
	mcsList[mcs] = false
}

func mcsExists(mcs string) bool {
	return mcsList[mcs]
}

func IntToMcs(id int, catRange uint32) string {
	var (
		SETSIZE = int(catRange)
		TIER    = SETSIZE
		ORD     = id
	)

	if id < 1 || id > 523776 {
		return ""
	}

	for ORD > TIER {
		ORD = ORD - TIER
		TIER -= 1
	}
	TIER = SETSIZE - TIER
	ORD = ORD + TIER
	return fmt.Sprintf("s0:c%d,c%d", TIER, ORD)
}

func uniqMcs(catRange uint32) string {
	var (
		n      uint32
		c1, c2 uint32
		mcs    string
	)

	for {
		binary.Read(rand.Reader, binary.LittleEndian, &n)
		c1 = n % catRange
		binary.Read(rand.Reader, binary.LittleEndian, &n)
		c2 = n % catRange
		if c1 == c2 {
			continue
		} else {
			if c1 > c2 {
				t := c1
				c1 = c2
				c2 = t
			}
		}
		mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
		if err := mcsAdd(mcs); err != nil {
			continue
		}
		break
	}
	return mcs
}

func FreeLxcContexts(scon string) {
	if len(scon) != 0 {
		con := strings.SplitN(scon, ":", 4)
		mcsDelete(con[3])
	}
}

func GetLxcContexts() (processLabel string, fileLabel string) {
	var (
		val, key string
		bufin    *bufio.Reader
	)

	if !SelinuxEnabled() {
		return "", ""
	}
	lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot())
	in, err := os.Open(lxcPath)
	if err != nil {
		return "", ""
	}
	defer in.Close()

	bufin = bufio.NewReader(in)

	for done := false; !done; {
		var line string
		if line, err = bufin.ReadString('\n'); err != nil {
			if err == io.EOF {
				done = true
			} else {
				goto exit
			}
		}
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			// Skip blank lines
			continue
		}
		if line[0] == ';' || line[0] == '#' {
			// Skip comments
			continue
		}
		if groups := assignRegex.FindStringSubmatch(line); groups != nil {
			key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
			if key == "process" {
				processLabel = strings.Trim(val, "\"")
			}
			if key == "file" {
				fileLabel = strings.Trim(val, "\"")
			}
		}
	}

	if processLabel == "" || fileLabel == "" {
		return "", ""
	}

exit:
	//	mcs := IntToMcs(os.Getpid(), 1024)
	mcs := uniqMcs(1024)
	scon := NewContext(processLabel)
	scon["level"] = mcs
	processLabel = scon.Get()
	scon = NewContext(fileLabel)
	scon["level"] = mcs
	fileLabel = scon.Get()
	return processLabel, fileLabel
}

func SecurityCheckContext(val string) error {
	return writeCon(fmt.Sprintf("%s.context", selinuxPath), val)
}

func CopyLevel(src, dest string) (string, error) {
	if src == "" {
		return "", nil
	}
	if err := SecurityCheckContext(src); err != nil {
		return "", err
	}
	if err := SecurityCheckContext(dest); err != nil {
		return "", err
	}
	scon := NewContext(src)
	tcon := NewContext(dest)
	mcsDelete(tcon["level"])
	mcsAdd(scon["level"])
	tcon["level"] = scon["level"]
	return tcon.Get(), nil
}

// Prevent users from relabing system files
func badPrefix(fpath string) error {
	var badprefixes = []string{"/usr"}

	for _, prefix := range badprefixes {
		if fpath == prefix || strings.HasPrefix(fpath, fmt.Sprintf("%s/", prefix)) {
			return fmt.Errorf("Relabeling content in %s is not allowed.", prefix)
		}
	}
	return nil
}

// Change the fpath file object to the SELinux label scon.
// If the fpath is a directory and recurse is true Chcon will walk the
// directory tree setting the label
func Chcon(fpath string, scon string, recurse bool) error {
	if scon == "" {
		return nil
	}
	if err := badPrefix(fpath); err != nil {
		return err
	}
	callback := func(p string, info os.FileInfo, err error) error {
		return Setfilecon(p, scon)
	}

	if recurse {
		return filepath.Walk(fpath, callback)
	}

	return Setfilecon(fpath, scon)
}

// DupSecOpt takes an SELinux process label and returns security options that
// can will set the SELinux Type and Level for future container processes
func DupSecOpt(src string) []string {
	if src == "" {
		return nil
	}
	con := NewContext(src)
	if con["user"] == "" ||
		con["role"] == "" ||
		con["type"] == "" ||
		con["level"] == "" {
		return nil
	}
	return []string{"label:user:" + con["user"],
		"label:role:" + con["role"],
		"label:type:" + con["type"],
		"label:level:" + con["level"]}
}

// DisableSecOpt returns a security opt that can be used to disabling SELinux
// labeling support for future container processes
func DisableSecOpt() []string {
	return []string{"label:disable"}
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"

	"github.com/gogo/protobuf/proto"
	log "github.com/golang/glog"
	"github.com/mesos/mesos-go/auth"
	"github.com/mesos/mesos-go/auth/sasl"
	"github.com/mesos/mesos-go/auth/sasl/mech"
	mesos "github.com/mesos/mesos-go/mesosproto"
	"github.com/mesos/mesos-go/scheduler"
	"github.com/samuel/go-zookeeper/zk"
	"golang.org/x/net/context"

	"github.com/mesosphere/etcd-mesos/rpc"
	etcdscheduler "github.com/mesosphere/etcd-mesos/scheduler"
)

func parseIP(address string) net.IP {
	addr, err := net.LookupIP(address)
	if err != nil {
		log.Fatal(err)
	}
	if len(addr) < 1 {
		log.Fatalf("failed to parse IP from address '%v'", address)
	}
	return addr[0]
}

func main() {
	singleInstancePerSlave :=
		flag.Bool("single-instance-per-slave", true, "Only allow one etcd instance to be started per slave")
	taskCount :=
		flag.Int("task-count", 5, "Total task count to run")
	artifactPort :=
		flag.Int("artifactPort", 12300, "Binding port for artifact server")
	failoverTimeoutSeconds :=
		flag.Float64("failover-timeout-seconds", 60*60*24*7, "Mesos framework failover timeout in seconds")
	executorPath :=
		flag.String("executor", "./bin/etcd_executor", "Path to test executor")
	etcdPath :=
		flag.String("etcd", "./bin/etcd", "Path to test executor")
	address :=
		flag.String("address", "127.0.0.1", "Binding address for artifact server")
	restorePath :=
		flag.String("restore", "", "Local path or URI for an etcd backup to restore as a new cluster")
	master :=
		flag.String("master", "127.0.0.1:5050", "Master address <ip:port>")
	clusterName :=
		flag.String("clusterName", "default", "Unique name of this etcd cluster")
	mesosAuthPrincipal :=
		flag.String("mesos_authentication_principal", "", "Mesos authentication principal")
	mesosAuthSecretFile :=
		flag.String("mesos_authentication_secret_file", "", "Mesos authentication secret file")
	zkConnect :=
		flag.String("zk-framework-persist", "", "Zookeeper URI of the form zk://host1:port1,host2:port2/chroot/path")
	authProvider :=
		flag.String("mesos_authentication_provider", sasl.ProviderName,
			fmt.Sprintf("Authentication provider to use, default is SASL that supports mechanisms: %+v", mech.ListSupported()))
	flag.Parse()

	executorUris := []*mesos.CommandInfo_URI{}
	execUri, err := etcdscheduler.ServeExecutorArtifact(*executorPath, *address, *artifactPort)
	if err != nil {
		log.Errorf("Could not stat executor binary: %v", err)
		return
	}
	executorUris = append(executorUris, &mesos.CommandInfo_URI{
		Value:      execUri,
		Executable: proto.Bool(true),
	})
	etcdUri, err := etcdscheduler.ServeExecutorArtifact(*etcdPath, *address, *artifactPort)
	if err != nil {
		log.Errorf("Could not stat etcd binary: %v", err)
		return
	}
	executorUris = append(executorUris, &mesos.CommandInfo_URI{
		Value:      etcdUri,
		Executable: proto.Bool(true),
	})

	go http.ListenAndServe(fmt.Sprintf("%s:%d", *address, *artifactPort), nil)
	log.V(2).Info("Serving executor artifacts...")

	bindingAddress := parseIP(*address)

	// chillFactor is the number of seconds that are slept for to allow for
	// convergence across the cluster during mutations.
	chillFactor := 10
	etcdScheduler := etcdscheduler.NewEtcdScheduler(
		*taskCount,
		chillFactor,
		executorUris,
		*singleInstancePerSlave,
	)
	etcdScheduler.ExecutorPath = *executorPath
	etcdScheduler.RestorePath = *restorePath
	etcdScheduler.Master = *master
	etcdScheduler.ClusterName = *clusterName
	etcdScheduler.ZkConnect = *zkConnect

	fwinfo := &mesos.FrameworkInfo{
		User:            proto.String(""), // Mesos-go will fill in user.
		Name:            proto.String("etcd-" + etcdScheduler.ClusterName),
		Checkpoint:      proto.Bool(true),
		FailoverTimeout: proto.Float64(*failoverTimeoutSeconds),
	}

	cred := (*mesos.Credential)(nil)
	if *mesosAuthPrincipal != "" {
		fwinfo.Principal = proto.String(*mesosAuthPrincipal)
		secret, err := ioutil.ReadFile(*mesosAuthSecretFile)
		if err != nil {
			log.Fatal(err)
		}
		cred = &mesos.Credential{
			Principal: proto.String(*mesosAuthPrincipal),
			Secret:    secret,
		}
	}

	zkServers, zkChroot, err := rpc.ParseZKURI(*zkConnect)
	etcdScheduler.ZkServers = zkServers
	etcdScheduler.ZkChroot = zkChroot
	if err != nil && *zkConnect != "" {
		log.Fatalf("Error parsing zookeeper URI of %s: %s", *zkConnect, err)
	} else if *zkConnect != "" {
		previous, err := rpc.GetPreviousFrameworkID(
			zkServers,
			zkChroot,
			etcdScheduler.ClusterName,
		)
		if err != nil && err != zk.ErrNoNode {
			log.Fatalf("Could not retrieve previous framework ID: %s", err)
		} else if err == zk.ErrNoNode {
			log.Info("No previous persisted framework ID exists in zookeeper.")
		} else {
			log.Infof("Found stored framework ID in Zookeeper, "+
				"attempting to re-use: %s", previous)
			fwinfo.Id = &mesos.FrameworkID{
				Value: proto.String(previous),
			}
		}
	}

	config := scheduler.DriverConfig{
		Scheduler:      etcdScheduler,
		Framework:      fwinfo,
		Master:         etcdScheduler.Master,
		Credential:     cred,
		BindingAddress: bindingAddress,
		WithAuthContext: func(ctx context.Context) context.Context {
			ctx = auth.WithLoginProvider(ctx, *authProvider)
			ctx = sasl.WithBindingAddress(ctx, bindingAddress)
			return ctx
		},
	}

	driver, err := scheduler.NewMesosSchedulerDriver(config)

	if err != nil {
		log.Errorln("Unable to create a SchedulerDriver ", err.Error())
	}

	go etcdScheduler.SerialLauncher(driver)
	go etcdScheduler.PeriodicLaunchRequestor()
	go etcdScheduler.PeriodicPruner()

	if stat, err := driver.Run(); err != nil {
		log.Infof("Framework stopped with status %s and error: %s",
			stat.String(),
			err.Error())
	}
}
<commit_msg>Replace camelCase command-line opts with dash-separated replacements.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"

	"github.com/gogo/protobuf/proto"
	log "github.com/golang/glog"
	"github.com/mesos/mesos-go/auth"
	"github.com/mesos/mesos-go/auth/sasl"
	"github.com/mesos/mesos-go/auth/sasl/mech"
	mesos "github.com/mesos/mesos-go/mesosproto"
	"github.com/mesos/mesos-go/scheduler"
	"github.com/samuel/go-zookeeper/zk"
	"golang.org/x/net/context"

	"github.com/mesosphere/etcd-mesos/rpc"
	etcdscheduler "github.com/mesosphere/etcd-mesos/scheduler"
)

func parseIP(address string) net.IP {
	addr, err := net.LookupIP(address)
	if err != nil {
		log.Fatal(err)
	}
	if len(addr) < 1 {
		log.Fatalf("failed to parse IP from address '%v'", address)
	}
	return addr[0]
}

func main() {
	singleInstancePerSlave :=
		flag.Bool("single-instance-per-slave", true, "Only allow one etcd instance to be started per slave")
	taskCount :=
		flag.Int("task-count", 5, "Total task count to run")
	artifactPort :=
		flag.Int("artifact-port", 12300, "Binding port for artifact server")
	failoverTimeoutSeconds :=
		flag.Float64("failover-timeout-seconds", 60*60*24*7, "Mesos framework failover timeout in seconds")
	executorPath :=
		flag.String("executor", "./bin/etcd_executor", "Path to test executor")
	etcdPath :=
		flag.String("etcd", "./bin/etcd", "Path to test executor")
	address :=
		flag.String("address", "127.0.0.1", "Binding address for artifact server")
	restorePath :=
		flag.String("restore", "", "Local path or URI for an etcd backup to restore as a new cluster")
	master :=
		flag.String("master", "127.0.0.1:5050", "Master address <ip:port>")
	clusterName :=
		flag.String("cluster-name", "default", "Unique name of this etcd cluster")
	mesosAuthPrincipal :=
		flag.String("mesos-authentication-principal", "", "Mesos authentication principal")
	mesosAuthSecretFile :=
		flag.String("mesos-authentication-secret-file", "", "Mesos authentication secret file")
	zkConnect :=
		flag.String("zk-framework-persist", "", "Zookeeper URI of the form zk://host1:port1,host2:port2/chroot/path")
	authProvider :=
		flag.String("mesos-authentication-provider", sasl.ProviderName,
			fmt.Sprintf("Authentication provider to use, default is SASL that supports mechanisms: %+v", mech.ListSupported()))
	flag.Parse()

	executorUris := []*mesos.CommandInfo_URI{}
	execUri, err := etcdscheduler.ServeExecutorArtifact(*executorPath, *address, *artifactPort)
	if err != nil {
		log.Errorf("Could not stat executor binary: %v", err)
		return
	}
	executorUris = append(executorUris, &mesos.CommandInfo_URI{
		Value:      execUri,
		Executable: proto.Bool(true),
	})
	etcdUri, err := etcdscheduler.ServeExecutorArtifact(*etcdPath, *address, *artifactPort)
	if err != nil {
		log.Errorf("Could not stat etcd binary: %v", err)
		return
	}
	executorUris = append(executorUris, &mesos.CommandInfo_URI{
		Value:      etcdUri,
		Executable: proto.Bool(true),
	})

	go http.ListenAndServe(fmt.Sprintf("%s:%d", *address, *artifactPort), nil)
	log.V(2).Info("Serving executor artifacts...")

	bindingAddress := parseIP(*address)

	// chillFactor is the number of seconds that are slept for to allow for
	// convergence across the cluster during mutations.
	chillFactor := 10
	etcdScheduler := etcdscheduler.NewEtcdScheduler(
		*taskCount,
		chillFactor,
		executorUris,
		*singleInstancePerSlave,
	)
	etcdScheduler.ExecutorPath = *executorPath
	etcdScheduler.RestorePath = *restorePath
	etcdScheduler.Master = *master
	etcdScheduler.ClusterName = *clusterName
	etcdScheduler.ZkConnect = *zkConnect

	fwinfo := &mesos.FrameworkInfo{
		User:            proto.String(""), // Mesos-go will fill in user.
		Name:            proto.String("etcd-" + etcdScheduler.ClusterName),
		Checkpoint:      proto.Bool(true),
		FailoverTimeout: proto.Float64(*failoverTimeoutSeconds),
	}

	cred := (*mesos.Credential)(nil)
	if *mesosAuthPrincipal != "" {
		fwinfo.Principal = proto.String(*mesosAuthPrincipal)
		secret, err := ioutil.ReadFile(*mesosAuthSecretFile)
		if err != nil {
			log.Fatal(err)
		}
		cred = &mesos.Credential{
			Principal: proto.String(*mesosAuthPrincipal),
			Secret:    secret,
		}
	}

	zkServers, zkChroot, err := rpc.ParseZKURI(*zkConnect)
	etcdScheduler.ZkServers = zkServers
	etcdScheduler.ZkChroot = zkChroot
	if err != nil && *zkConnect != "" {
		log.Fatalf("Error parsing zookeeper URI of %s: %s", *zkConnect, err)
	} else if *zkConnect != "" {
		previous, err := rpc.GetPreviousFrameworkID(
			zkServers,
			zkChroot,
			etcdScheduler.ClusterName,
		)
		if err != nil && err != zk.ErrNoNode {
			log.Fatalf("Could not retrieve previous framework ID: %s", err)
		} else if err == zk.ErrNoNode {
			log.Info("No previous persisted framework ID exists in zookeeper.")
		} else {
			log.Infof("Found stored framework ID in Zookeeper, "+
				"attempting to re-use: %s", previous)
			fwinfo.Id = &mesos.FrameworkID{
				Value: proto.String(previous),
			}
		}
	}

	config := scheduler.DriverConfig{
		Scheduler:      etcdScheduler,
		Framework:      fwinfo,
		Master:         etcdScheduler.Master,
		Credential:     cred,
		BindingAddress: bindingAddress,
		WithAuthContext: func(ctx context.Context) context.Context {
			ctx = auth.WithLoginProvider(ctx, *authProvider)
			ctx = sasl.WithBindingAddress(ctx, bindingAddress)
			return ctx
		},
	}

	driver, err := scheduler.NewMesosSchedulerDriver(config)

	if err != nil {
		log.Errorln("Unable to create a SchedulerDriver ", err.Error())
	}

	go etcdScheduler.SerialLauncher(driver)
	go etcdScheduler.PeriodicLaunchRequestor()
	go etcdScheduler.PeriodicPruner()

	if stat, err := driver.Run(); err != nil {
		log.Infof("Framework stopped with status %s and error: %s",
			stat.String(),
			err.Error())
	}
}
<|endoftext|>


<commit_before>// Copyright 2015-2020 Ray Holder
//
// 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 main

import (
	"io/ioutil"
	"strings"
	"testing"
)

// Return true if any pattern in the rollup matches the string.
func matches(rollup *Rollup, value string) bool {
	for _, pc := range rollup.patterns {
		if pc.regex.MatchString(value) {
			return true
		}
	}
	return false
}

func TestRollup(t *testing.T) {
	acceptable := []string{
		"abc", "def", "ghi", "abcdef", "ghiblahblah", "defabc",
	}
	unacceptable := []string{
		"foo", "bar", "foobar",
	}

	args := []string{"abc", "def", "ghi"}
	config, err := newConfig(args, ioutil.Discard, ioutil.Discard)
	rollup, err := newRollup(config)
	if err != nil {
		t.Fatal("Unxpected rollup error:", err)
	}

	if rollup.total != 0 {
		t.Fatal("Expected rollup total to start at 0:", rollup.total)
	}

	for _, expected := range acceptable {
		if !matches(rollup, expected) {
			t.Fatal("Expected to match:", expected)
		}
	}

	for _, unexpected := range unacceptable {
		if matches(rollup, unexpected) {
			t.Fatal("Not expected to match:", unexpected)
		}
	}
}

func TestRollupBadRegex(t *testing.T) {
	args := []string{".**", "def", "ghi"}
	config, err := newConfig(args, ioutil.Discard, ioutil.Discard)
	rollup, err := newRollup(config)
	if err == nil {
		t.Fatal("Expected rollup error:", rollup)
	} else {
		if !strings.HasPrefix(err.Error(), "error parsing regexp") {
			t.Fatal("Expected a regexp error:", rollup)
		}
	}
}
<commit_msg>Add check for an Error that could occur<commit_after>// Copyright 2015-2020 Ray Holder
//
// 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 main

import (
	"io/ioutil"
	"strings"
	"testing"
)

// Return true if any pattern in the rollup matches the string.
func matches(rollup *Rollup, value string) bool {
	for _, pc := range rollup.patterns {
		if pc.regex.MatchString(value) {
			return true
		}
	}
	return false
}

func TestRollup(t *testing.T) {
	acceptable := []string{
		"abc", "def", "ghi", "abcdef", "ghiblahblah", "defabc",
	}
	unacceptable := []string{
		"foo", "bar", "foobar",
	}

	args := []string{"abc", "def", "ghi"}
	config, err := newConfig(args, ioutil.Discard, ioutil.Discard)
	if err != nil {
		t.Fatal("Unxpected rollup error:", err)
	}
	rollup, err := newRollup(config)
	if err != nil {
		t.Fatal("Unxpected rollup error:", err)
	}

	if rollup.total != 0 {
		t.Fatal("Expected rollup total to start at 0:", rollup.total)
	}

	for _, expected := range acceptable {
		if !matches(rollup, expected) {
			t.Fatal("Expected to match:", expected)
		}
	}

	for _, unexpected := range unacceptable {
		if matches(rollup, unexpected) {
			t.Fatal("Not expected to match:", unexpected)
		}
	}
}

func TestRollupBadRegex(t *testing.T) {
	args := []string{".**", "def", "ghi"}
	config, err := newConfig(args, ioutil.Discard, ioutil.Discard)
	rollup, err := newRollup(config)
	if err == nil {
		t.Fatal("Expected rollup error:", rollup)
	} else {
		if !strings.HasPrefix(err.Error(), "error parsing regexp") {
			t.Fatal("Expected a regexp error:", rollup)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 Istio 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.

// Defines the redirect object and operations.
package main

import (
	"fmt"
	"net"
	"os/exec"
	"strconv"
	"strings"

	"github.com/sirupsen/logrus"
)

const (
	redirectModeREDIRECT         = "REDIRECT"
	redirectModeTPROXY           = "TPROXY"
	defaultProxyStatusPort       = "15020"
	defaultRedirectToPort        = "15001"
	defaultNoRedirectUID         = "1337"
	defaultRedirectMode          = redirectModeREDIRECT
	defaultRedirectIPCidr        = "*"
	defaultRedirectExcludeIPCidr = ""
	defaultRedirectExcludePort   = defaultProxyStatusPort
	defaultKubevirtInterfaces    = ""

	includeIPCidrsKey = "traffic.sidecar.istio.io/includeOutboundIPRanges"
	excludeIPCidrsKey = "traffic.sidecar.istio.io/excludeOutboundIPRanges"
	includePortsKey   = "traffic.sidecar.istio.io/includeInboundPorts"
	excludePortsKey   = "traffic.sidecar.istio.io/excludeInboundPorts"

	sidecarInterceptModeKey = "sidecar.istio.io/interceptionMode"
	sidecarPortListKey      = "status.sidecar.istio.io/port"

	kubevirtInterfacesKey = "traffic.sidecar.istio.io/kubevirtInterfaces"
)

var (
	annotationRegistry = map[string]*annotationParam{
		"inject":             {injectAnnotationKey, "", alwaysValidFunc},
		"status":             {sidecarStatusKey, "", alwaysValidFunc},
		"redirectMode":       {sidecarInterceptModeKey, defaultRedirectMode, validateInterceptionMode},
		"ports":              {sidecarPortListKey, "", validatePortList},
		"includeIPCidrs":     {includeIPCidrsKey, defaultRedirectIPCidr, validateCIDRListWithWildcard},
		"excludeIPCidrs":     {excludeIPCidrsKey, defaultRedirectExcludeIPCidr, validateCIDRList},
		"includePorts":       {includePortsKey, "", validatePortListWithWildcard},
		"excludePorts":       {excludePortsKey, defaultRedirectExcludePort, validatePortList},
		"kubevirtInterfaces": {kubevirtInterfacesKey, defaultKubevirtInterfaces, alwaysValidFunc},
	}
)

// Redirect -- the istio-cni redirect object
type Redirect struct {
	targetPort         string
	redirectMode       string
	noRedirectUID      string
	includeIPCidrs     string
	includePorts       string
	excludeIPCidrs     string
	excludePorts       string
	kubevirtInterfaces string

	logger *logrus.Entry
}

type annotationValidationFunc func(value string) error

type annotationParam struct {
	key        string
	defaultVal string
	validator  annotationValidationFunc
}

func alwaysValidFunc(value string) error {
	return nil
}

// validateInterceptionMode validates the interceptionMode annotation
func validateInterceptionMode(mode string) error {
	switch mode {
	case redirectModeREDIRECT:
	case redirectModeTPROXY:
	default:
		return fmt.Errorf("interceptionMode invalid: %v", mode)
	}
	return nil
}

func validateCIDRList(cidrs string) error {
	if len(cidrs) > 0 {
		for _, cidr := range strings.Split(cidrs, ",") {
			if _, _, err := net.ParseCIDR(cidr); err != nil {
				return fmt.Errorf("failed parsing cidr '%s': %v", cidr, err)
			}
		}
	}
	return nil
}

func splitPorts(portsString string) []string {
	return strings.Split(portsString, ",")
}

func parsePort(portStr string) (uint16, error) {
	port, err := strconv.ParseUint(strings.TrimSpace(portStr), 10, 16)
	if err != nil {
		return 0, fmt.Errorf("failed parsing port %q: %v", portStr, err)
	}
	return uint16(port), nil
}

func parsePorts(portsString string) ([]int, error) {
	portsString = strings.TrimSpace(portsString)
	ports := make([]int, 0)
	if len(portsString) > 0 {
		for _, portStr := range splitPorts(portsString) {
			port, err := parsePort(portStr)
			if err != nil {
				return nil, err
			}
			ports = append(ports, int(port))
		}
	}
	return ports, nil
}

func validatePortList(ports string) error {
	if _, err := parsePorts(ports); err != nil {
		return fmt.Errorf("portList %q invalid: %v", ports, err)
	}
	return nil
}

func validatePortListWithWildcard(ports string) error {
	if ports != "*" {
		return validatePortList(ports)
	}
	return nil
}

// ValidateIncludeIPRanges validates the includeIPRanges parameter
func validateCIDRListWithWildcard(ipRanges string) error {
	if ipRanges != "*" {
		if e := validateCIDRList(ipRanges); e != nil {
			return fmt.Errorf("IPRanges invalid: %v", e)
		}
	}
	return nil
}

func getAnnotationOrDefault(name string, annotations map[string]string) (isFound bool, val string, err error) {
	if _, ok := annotationRegistry[name]; !ok {
		return false, "", fmt.Errorf("no registered annotation with name=%s", name)
	}
	// use annotation value if present
	if val, found := annotations[annotationRegistry[name].key]; found {
		if err := annotationRegistry[name].validator(val); err != nil {
			return true, annotationRegistry[name].defaultVal, err
		}
		return true, val, nil
	}
	// no annotation found so use default value
	return false, annotationRegistry[name].defaultVal, nil
}

// NewRedirect returns a new Redirect Object constructed from a list of ports and annotations
func NewRedirect(ports []string, annotations map[string]string, logger *logrus.Entry) (*Redirect, error) {
	var isFound bool
	var valErr error

	redir := &Redirect{}
	redir.logger = logger
	redir.targetPort = defaultRedirectToPort
	isFound, redir.redirectMode, valErr = getAnnotationOrDefault("redirectMode", annotations)
	if valErr != nil {
		logger.Errorf("Annotation value error for value %s; annotationFound = %t: %v",
			"redirectMode", isFound, valErr)
		return nil, valErr
	}
	redir.noRedirectUID = defaultNoRedirectUID
	isFound, redir.includeIPCidrs, valErr = getAnnotationOrDefault("includeIPCidrs", annotations)
	if valErr != nil {
		logger.Errorf("Annotation value error for value %s; annotationFound = %t: %v",
			"includeIPCidrs", isFound, valErr)
		return nil, valErr
	}
	isFound, redir.includePorts, valErr = getAnnotationOrDefault("includePorts", annotations)
	if !isFound || valErr != nil {
		redir.includePorts = strings.Join(ports, ",")
		if valErr != nil {
			logger.Errorf("Annotation value error for redirect ports, using ContainerPorts=\"%s\": %v",
				redir.includePorts, valErr)
			return nil, valErr
		}
	}
	isFound, redir.excludeIPCidrs, valErr = getAnnotationOrDefault("excludeIPCidrs", annotations)
	if valErr != nil {
		logger.Errorf("Annotation value error for value %s; annotationFound = %t: %v",
			"excludeIPCidrs", isFound, valErr)
		return nil, valErr
	}
	isFound, redir.excludePorts, valErr = getAnnotationOrDefault("excludePorts", annotations)
	if valErr != nil {
		logger.Errorf("Annotation value error for value %s; annotationFound = %t: %v",
			"excludePorts", isFound, valErr)
		return nil, valErr
	}
	isFound, redir.kubevirtInterfaces, valErr = getAnnotationOrDefault("kubevirtInterfaces", annotations)
	if valErr != nil {
		logger.Errorf("Annotation value error for value %s; annotationFound = %t: %v",
			"kubevirtInterfaces", isFound, valErr)
		return nil, valErr
	}

	return redir, nil
}

func (rdrct *Redirect) doRedirect(netns string) error {
	netnsArg := fmt.Sprintf("--net=%s", netns)
	nsSetupExecutable := fmt.Sprintf("%s/%s", nsSetupBinDir, nsSetupProg)
	nsenterArgs := []string{
		netnsArg,
		nsSetupExecutable,
		"-p", rdrct.targetPort,
		"-u", rdrct.noRedirectUID,
		"-m", rdrct.redirectMode,
		"-i", rdrct.includeIPCidrs,
		"-b", rdrct.includePorts,
		"-d", rdrct.excludePorts,
		"-x", rdrct.excludeIPCidrs,
		"-k", rdrct.kubevirtInterfaces,
	}
	logrus.WithFields(logrus.Fields{
		"nsenterArgs": nsenterArgs,
	}).Infof("nsenter args")
	out, err := exec.Command("nsenter", nsenterArgs...).CombinedOutput()
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"out": string(out),
			"err": err,
		}).Errorf("nsenter failed: %v", err)
		rdrct.logger.Infof("nsenter out: %s", out)
	} else {
		rdrct.logger.Infof("nsenter done: %s", out)
	}
	return err
}
<commit_msg>excludeOutboundPorts to facilitate headless data stores (#117)<commit_after>// Copyright 2018 Istio 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.

// Defines the redirect object and operations.
package main

import (
	"fmt"
	"net"
	"os/exec"
	"strconv"
	"strings"

	"github.com/sirupsen/logrus"
)

const (
	redirectModeREDIRECT         = "REDIRECT"
	redirectModeTPROXY           = "TPROXY"
	defaultProxyStatusPort       = "15020"
	defaultRedirectToPort        = "15001"
	defaultNoRedirectUID         = "1337"
	defaultRedirectMode          = redirectModeREDIRECT
	defaultRedirectIPCidr        = "*"
	defaultRedirectExcludeIPCidr = ""
	defaultRedirectExcludePort   = defaultProxyStatusPort
	defaultKubevirtInterfaces    = ""

	includeIPCidrsKey       = "traffic.sidecar.istio.io/includeOutboundIPRanges"
	excludeIPCidrsKey       = "traffic.sidecar.istio.io/excludeOutboundIPRanges"
	includePortsKey         = "traffic.sidecar.istio.io/includeInboundPorts"
	excludeInboundPortsKey  = "traffic.sidecar.istio.io/excludeInboundPorts"
	excludeOutboundPortsKey = "traffic.sidecar.istio.io/excludeOutboundPorts"

	sidecarInterceptModeKey = "sidecar.istio.io/interceptionMode"
	sidecarPortListKey      = "status.sidecar.istio.io/port"

	kubevirtInterfacesKey = "traffic.sidecar.istio.io/kubevirtInterfaces"
)

var (
	annotationRegistry = map[string]*annotationParam{
		"inject":               {injectAnnotationKey, "", alwaysValidFunc},
		"status":               {sidecarStatusKey, "", alwaysValidFunc},
		"redirectMode":         {sidecarInterceptModeKey, defaultRedirectMode, validateInterceptionMode},
		"ports":                {sidecarPortListKey, "", validatePortList},
		"includeIPCidrs":       {includeIPCidrsKey, defaultRedirectIPCidr, validateCIDRListWithWildcard},
		"excludeIPCidrs":       {excludeIPCidrsKey, defaultRedirectExcludeIPCidr, validateCIDRList},
		"includePorts":         {includePortsKey, "", validatePortListWithWildcard},
		"excludeInboundPorts":  {excludeInboundPortsKey, defaultRedirectExcludePort, validatePortList},
		"excludeOutboundPorts": {excludeOutboundPortsKey, defaultRedirectExcludePort, validatePortList},
		"kubevirtInterfaces":   {kubevirtInterfacesKey, defaultKubevirtInterfaces, alwaysValidFunc},
	}
)

// Redirect -- the istio-cni redirect object
type Redirect struct {
	targetPort           string
	redirectMode         string
	noRedirectUID        string
	includeIPCidrs       string
	includePorts         string
	excludeIPCidrs       string
	excludeInboundPorts  string
	excludeOutboundPorts string
	kubevirtInterfaces   string

	logger *logrus.Entry
}

type annotationValidationFunc func(value string) error

type annotationParam struct {
	key        string
	defaultVal string
	validator  annotationValidationFunc
}

func alwaysValidFunc(value string) error {
	return nil
}

// validateInterceptionMode validates the interceptionMode annotation
func validateInterceptionMode(mode string) error {
	switch mode {
	case redirectModeREDIRECT:
	case redirectModeTPROXY:
	default:
		return fmt.Errorf("interceptionMode invalid: %v", mode)
	}
	return nil
}

func validateCIDRList(cidrs string) error {
	if len(cidrs) > 0 {
		for _, cidr := range strings.Split(cidrs, ",") {
			if _, _, err := net.ParseCIDR(cidr); err != nil {
				return fmt.Errorf("failed parsing cidr '%s': %v", cidr, err)
			}
		}
	}
	return nil
}

func splitPorts(portsString string) []string {
	return strings.Split(portsString, ",")
}

func parsePort(portStr string) (uint16, error) {
	port, err := strconv.ParseUint(strings.TrimSpace(portStr), 10, 16)
	if err != nil {
		return 0, fmt.Errorf("failed parsing port %q: %v", portStr, err)
	}
	return uint16(port), nil
}

func parsePorts(portsString string) ([]int, error) {
	portsString = strings.TrimSpace(portsString)
	ports := make([]int, 0)
	if len(portsString) > 0 {
		for _, portStr := range splitPorts(portsString) {
			port, err := parsePort(portStr)
			if err != nil {
				return nil, err
			}
			ports = append(ports, int(port))
		}
	}
	return ports, nil
}

func validatePortList(ports string) error {
	if _, err := parsePorts(ports); err != nil {
		return fmt.Errorf("portList %q invalid: %v", ports, err)
	}
	return nil
}

func validatePortListWithWildcard(ports string) error {
	if ports != "*" {
		return validatePortList(ports)
	}
	return nil
}

// ValidateIncludeIPRanges validates the includeIPRanges parameter
func validateCIDRListWithWildcard(ipRanges string) error {
	if ipRanges != "*" {
		if e := validateCIDRList(ipRanges); e != nil {
			return fmt.Errorf("IPRanges invalid: %v", e)
		}
	}
	return nil
}

func getAnnotationOrDefault(name string, annotations map[string]string) (isFound bool, val string, err error) {
	if _, ok := annotationRegistry[name]; !ok {
		return false, "", fmt.Errorf("no registered annotation with name=%s", name)
	}
	// use annotation value if present
	if val, found := annotations[annotationRegistry[name].key]; found {
		if err := annotationRegistry[name].validator(val); err != nil {
			return true, annotationRegistry[name].defaultVal, err
		}
		return true, val, nil
	}
	// no annotation found so use default value
	return false, annotationRegistry[name].defaultVal, nil
}

// NewRedirect returns a new Redirect Object constructed from a list of ports and annotations
func NewRedirect(ports []string, annotations map[string]string, logger *logrus.Entry) (*Redirect, error) {
	var isFound bool
	var valErr error

	redir := &Redirect{}
	redir.logger = logger
	redir.targetPort = defaultRedirectToPort
	isFound, redir.redirectMode, valErr = getAnnotationOrDefault("redirectMode", annotations)
	if valErr != nil {
		logger.Errorf("Annotation value error for value %s; annotationFound = %t: %v",
			"redirectMode", isFound, valErr)
		return nil, valErr
	}
	redir.noRedirectUID = defaultNoRedirectUID
	isFound, redir.includeIPCidrs, valErr = getAnnotationOrDefault("includeIPCidrs", annotations)
	if valErr != nil {
		logger.Errorf("Annotation value error for value %s; annotationFound = %t: %v",
			"includeIPCidrs", isFound, valErr)
		return nil, valErr
	}
	isFound, redir.includePorts, valErr = getAnnotationOrDefault("includePorts", annotations)
	if !isFound || valErr != nil {
		redir.includePorts = strings.Join(ports, ",")
		if valErr != nil {
			logger.Errorf("Annotation value error for redirect ports, using ContainerPorts=\"%s\": %v",
				redir.includePorts, valErr)
			return nil, valErr
		}
	}
	isFound, redir.excludeIPCidrs, valErr = getAnnotationOrDefault("excludeIPCidrs", annotations)
	if valErr != nil {
		logger.Errorf("Annotation value error for value %s; annotationFound = %t: %v",
			"excludeIPCidrs", isFound, valErr)
		return nil, valErr
	}
	isFound, redir.excludeInboundPorts, valErr = getAnnotationOrDefault("excludeInboundPorts", annotations)
	if valErr != nil {
		logger.Errorf("Annotation value error for value %s; annotationFound = %t: %v",
			"excludeInboundPorts", isFound, valErr)
		return nil, valErr
	}
	isFound, redir.excludeOutboundPorts, valErr = getAnnotationOrDefault("excludeOutboundPorts", annotations)
	if valErr != nil {
		logger.Errorf("Annotation value error for value %s; annotationFound = %t: %v",
			"excludeOutboundPorts", isFound, valErr)
		return nil, valErr
	}
	isFound, redir.kubevirtInterfaces, valErr = getAnnotationOrDefault("kubevirtInterfaces", annotations)
	if valErr != nil {
		logger.Errorf("Annotation value error for value %s; annotationFound = %t: %v",
			"kubevirtInterfaces", isFound, valErr)
		return nil, valErr
	}

	return redir, nil
}

func (rdrct *Redirect) doRedirect(netns string) error {
	netnsArg := fmt.Sprintf("--net=%s", netns)
	nsSetupExecutable := fmt.Sprintf("%s/%s", nsSetupBinDir, nsSetupProg)
	nsenterArgs := []string{
		netnsArg,
		nsSetupExecutable,
		"-p", rdrct.targetPort,
		"-u", rdrct.noRedirectUID,
		"-m", rdrct.redirectMode,
		"-i", rdrct.includeIPCidrs,
		"-b", rdrct.includePorts,
		"-d", rdrct.excludeInboundPorts,
		"-o", rdrct.excludeOutboundPorts,
		"-x", rdrct.excludeIPCidrs,
		"-k", rdrct.kubevirtInterfaces,
	}
	logrus.WithFields(logrus.Fields{
		"nsenterArgs": nsenterArgs,
	}).Infof("nsenter args")
	out, err := exec.Command("nsenter", nsenterArgs...).CombinedOutput()
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"out": string(out),
			"err": err,
		}).Errorf("nsenter failed: %v", err)
		rdrct.logger.Infof("nsenter out: %s", out)
	} else {
		rdrct.logger.Infof("nsenter done: %s", out)
	}
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"

	"launchpad.net/gnuflag"
	"launchpad.net/juju-core/juju/cmd"
	"launchpad.net/juju-core/juju/environs"
	"launchpad.net/juju-core/juju/log"
	"launchpad.net/juju-core/juju/state"
	"launchpad.net/tomb"

	// register providers
	_ "launchpad.net/juju-core/juju/environs/dummy"
	_ "launchpad.net/juju-core/juju/environs/ec2"
)

var errInstanceNotFound = errors.New("instance not found")

// ProvisioningAgent is a cmd.Command responsible for running a provisioning agent.
type ProvisioningAgent struct {
	Conf AgentConf
}

// Info returns usage information for the command.
func (a *ProvisioningAgent) Info() *cmd.Info {
	return &cmd.Info{"provisioning", "", "run a juju provisioning agent", ""}
}

// Init initializes the command for running.
func (a *ProvisioningAgent) Init(f *gnuflag.FlagSet, args []string) error {
	a.Conf.addFlags(f)
	if err := f.Parse(true, args); err != nil {
		return err
	}
	return a.Conf.checkArgs(f.Args())
}

// Run runs a provisioning agent.
func (a *ProvisioningAgent) Run(_ *cmd.Context) error {
	// TODO(dfc) place the logic in a loop with a suitable delay
	p, err := NewProvisioner(&a.Conf.StateInfo)
	if err != nil {
		return err
	}
	return p.Wait()
}

type Provisioner struct {
	st      *state.State
	info    *state.Info
	environ environs.Environ
	tomb    tomb.Tomb

	environWatcher  *state.ConfigWatcher
	machinesWatcher *state.MachinesWatcher

	providerIdToInstance  map[string]environs.Instance
	machineIdToProviderId map[int]string
}

// NewProvisioner returns a Provisioner.
func NewProvisioner(info *state.Info) (*Provisioner, error) {
	st, err := state.Open(info)
	if err != nil {
		return nil, err
	}
	p := &Provisioner{
		st:                    st,
		info:                  info,
		providerIdToInstance:  make(map[string]environs.Instance),
		machineIdToProviderId: make(map[int]string),
	}
	go p.loop()
	return p, nil
}

func (p *Provisioner) loop() {
	defer p.tomb.Done()
	defer func() {
		err := p.st.Close()
		if err != nil {
			p.tomb.Kill(err)
		}
	}()
	p.environWatcher = p.st.WatchEnvironConfig()
	// TODO(dfc) we need a method like state.IsConnected() here to exit cleanly if
	// there is a connection problem.
	for {
		select {
		case <-p.tomb.Dying():
			return
		case config, ok := <-p.environWatcher.Changes():
			if !ok {
				err := p.environWatcher.Stop()
				if err != nil {
					p.tomb.Kill(err)
				}
				return
			}
			var err error
			p.environ, err = environs.NewEnviron(config.Map())
			if err != nil {
				log.Printf("provisioner loaded invalid environment configuration: %v", err)
				continue
			}
			log.Printf("provisioner loaded new environment configuration")
			p.innerLoop()
		}
	}
}

func (p *Provisioner) innerLoop() {
	p.machinesWatcher = p.st.WatchMachines()

	// TODO(dfc) we need a method like state.IsConnected() here to exit cleanly if
	// there is a connection problem.
	for {
		select {
		case <-p.tomb.Dying():
			return
		case change, ok := <-p.environWatcher.Changes():
			if !ok {
				err := p.environWatcher.Stop()
				if err != nil {
					p.tomb.Kill(err)
				}
				return
			}
			config, err := environs.NewConfig(change.Map())
			if err != nil {
				log.Printf("provisioner loaded invalid environment configuration: %v", err)
				continue
			}
			p.environ.SetConfig(config)
			log.Printf("provisioner loaded new environment configuration")
		case machines, ok := <-p.machinesWatcher.Changes():
			if !ok {
				err := p.machinesWatcher.Stop()
				if err != nil {
					p.tomb.Kill(err)
				}
				return
			}
			if err := p.processMachines(machines); err != nil {
				p.tomb.Kill(err)
			}
		}
	}
}

// Wait waits for the Provisioner to exit.
func (p *Provisioner) Wait() error {
	return p.tomb.Wait()
}

// Stop stops the Provisioner and returns any error encountered while
// provisioning.
func (p *Provisioner) Stop() error {
	p.tomb.Kill(nil)
	return p.tomb.Wait()
}

func (p *Provisioner) processMachines(changes *state.MachinesChange) error {
	// step 1. filter machines with provider ids and without
	var notrunning []*state.Machine
	for _, m := range changes.Added {
		id, err := m.InstanceId()
		if err != nil {
			return err
		}
		if id == "" {
			notrunning = append(notrunning, m)
		} else {
			log.Printf("machine %s already running as instance %q", m, id)
		}
	}

	// step 2. start all the notrunning machines
	if _, err := p.startMachines(notrunning); err != nil {
		return err
	}

	// step 3. stop all unknown machines and the machines that were removed
	// from the state
	stopping, err := p.instancesForMachines(changes.Deleted)
	if err != nil {
		return err
	}

	// although calling StopInstance with an empty slice should produce no change in the
	// provider, environs like dummy do not consider this a noop.
	if len(stopping) > 0 {
		return p.environ.StopInstances(stopping)
	}
	return nil
}

func (p *Provisioner) startMachines(machines []*state.Machine) ([]*state.Machine, error) {
	var started []*state.Machine
	for _, m := range machines {
		if err := p.startMachine(m); err != nil {
			return nil, err
		}
		log.Printf("starting machine %v", m)
		started = append(started, m)
	}
	return started, nil
}

func (p *Provisioner) startMachine(m *state.Machine) error {
	// TODO(dfc) the state.Info passed to environ.StartInstance remains contentious
	// however as the PA only knows one state.Info, and that info is used by MAs and
	// UAs to locate the ZK for this environment, it is logical to use the same
	// state.Info as the PA.
	inst, err := p.environ.StartInstance(m.Id(), p.info)
	if err != nil {
		return err
	}

	// assign the provider id to the macine
	if err := m.SetInstanceId(inst.Id()); err != nil {
		return fmt.Errorf("unable to store provider id for machine %v: %v", m, err)
	}

	// populate the local caches
	p.machineIdToProviderId[m.Id()] = inst.Id()
	p.providerIdToInstance[inst.Id()] = inst
	return nil
}

// instanceForMachine returns the environs.Instance that represents this machines' running
// instance.
func (p *Provisioner) instanceForMachine(m *state.Machine) (environs.Instance, error) {
	id, ok := p.machineIdToProviderId[m.Id()]
	if !ok {
		// not cached locally, ask the environ.
		var err error
		id, err = m.InstanceId()
		if err != nil {
			return nil, err
		}
		if id == "" {
			// nobody knows about this machine, give up.
			return nil, errInstanceNotFound
		}
		p.machineIdToProviderId[m.Id()] = id
	}
	inst, ok := p.providerIdToInstance[id]
	if !ok {
		// not cached locally, ask the provider
		var err error
		inst, err = p.findInstance(id)
		if err != nil {
			// the provider doesn't know about this instance, give up.
			return nil, err
		}
		return nil, nil
	}
	return inst, nil
}

// instancesForMachines returns a list of environs.Instance that represent the list of machines running
// in the provider.
func (p *Provisioner) instancesForMachines(machines []*state.Machine) ([]environs.Instance, error) {
	var insts []environs.Instance
	for _, m := range machines {
		inst, err := p.instanceForMachine(m)
		if err != nil {
			return nil, err
		}
		insts = append(insts, inst)
	}
	return insts, nil
}

func (p *Provisioner) findInstance(id string) (environs.Instance, error) {
	insts, err := p.environ.Instances([]string{id})
	if err != nil {
		return nil, err
	}
	if len(insts) < 1 {
		return nil, errInstanceNotFound
	}
	return insts[0], nil
}
<commit_msg>final tweaks<commit_after>package main

import (
	"fmt"

	"launchpad.net/gnuflag"
	"launchpad.net/juju-core/juju/cmd"
	"launchpad.net/juju-core/juju/environs"
	"launchpad.net/juju-core/juju/log"
	"launchpad.net/juju-core/juju/state"
	"launchpad.net/tomb"

	// register providers
	_ "launchpad.net/juju-core/juju/environs/dummy"
	_ "launchpad.net/juju-core/juju/environs/ec2"
)

// ProvisioningAgent is a cmd.Command responsible for running a provisioning agent.
type ProvisioningAgent struct {
	Conf AgentConf
}

// Info returns usage information for the command.
func (a *ProvisioningAgent) Info() *cmd.Info {
	return &cmd.Info{"provisioning", "", "run a juju provisioning agent", ""}
}

// Init initializes the command for running.
func (a *ProvisioningAgent) Init(f *gnuflag.FlagSet, args []string) error {
	a.Conf.addFlags(f)
	if err := f.Parse(true, args); err != nil {
		return err
	}
	return a.Conf.checkArgs(f.Args())
}

// Run runs a provisioning agent.
func (a *ProvisioningAgent) Run(_ *cmd.Context) error {
	// TODO(dfc) place the logic in a loop with a suitable delay
	p, err := NewProvisioner(&a.Conf.StateInfo)
	if err != nil {
		return err
	}
	return p.Wait()
}

type Provisioner struct {
	st      *state.State
	info    *state.Info
	environ environs.Environ
	tomb    tomb.Tomb

	environWatcher  *state.ConfigWatcher
	machinesWatcher *state.MachinesWatcher

	providerIdToInstance  map[string]environs.Instance
	machineIdToProviderId map[int]string
}

// NewProvisioner returns a Provisioner.
func NewProvisioner(info *state.Info) (*Provisioner, error) {
	st, err := state.Open(info)
	if err != nil {
		return nil, err
	}
	p := &Provisioner{
		st:                    st,
		info:                  info,
		providerIdToInstance:  make(map[string]environs.Instance),
		machineIdToProviderId: make(map[int]string),
	}
	go p.loop()
	return p, nil
}

func (p *Provisioner) loop() {
	defer p.tomb.Done()
	defer p.st.Close()
	p.environWatcher = p.st.WatchEnvironConfig()
	for {
		select {
		case <-p.tomb.Dying():
			return
		case config, ok := <-p.environWatcher.Changes():
			if !ok {
				err := p.environWatcher.Stop()
				if err != nil {
					p.tomb.Kill(err)
				}
				return
			}
			var err error
			p.environ, err = environs.NewEnviron(config.Map())
			if err != nil {
				log.Printf("provisioner loaded invalid environment configuration: %v", err)
				continue
			}
			log.Printf("provisioner loaded new environment configuration")
			p.innerLoop()
		}
	}
}

func (p *Provisioner) innerLoop() {
	p.machinesWatcher = p.st.WatchMachines()
	for {
		select {
		case <-p.tomb.Dying():
			return
		case change, ok := <-p.environWatcher.Changes():
			if !ok {
				err := p.environWatcher.Stop()
				if err != nil {
					p.tomb.Kill(err)
				}
				return
			}
			config, err := environs.NewConfig(change.Map())
			if err != nil {
				log.Printf("provisioner loaded invalid environment configuration: %v", err)
				continue
			}
			p.environ.SetConfig(config)
			log.Printf("provisioner loaded new environment configuration")
		case machines, ok := <-p.machinesWatcher.Changes():
			if !ok {
				err := p.machinesWatcher.Stop()
				if err != nil {
					p.tomb.Kill(err)
				}
				return
			}
			if err := p.processMachines(machines); err != nil {
				p.tomb.Kill(err)
			}
		}
	}
}

// Wait waits for the Provisioner to exit.
func (p *Provisioner) Wait() error {
	return p.tomb.Wait()
}

// Stop stops the Provisioner and returns any error encountered while
// provisioning.
func (p *Provisioner) Stop() error {
	p.tomb.Kill(nil)
	return p.tomb.Wait()
}

func (p *Provisioner) processMachines(changes *state.MachinesChange) error {
	// step 1. filter machines with provider ids and without
	var notrunning []*state.Machine
	for _, m := range changes.Added {
		id, err := m.InstanceId()
		if err != nil {
			return err
		}
		if id == "" {
			notrunning = append(notrunning, m)
		} else {
			log.Printf("machine %s already running as instance %q", m, id)
		}
	}

	// step 2. start all the notrunning machines
	if _, err := p.startMachines(notrunning); err != nil {
		return err
	}

	// step 3. stop all unknown machines and the machines that were removed
	// from the state
	stopping, err := p.instancesForMachines(changes.Deleted)
	if err != nil {
		return err
	}

	// TODO(dfc) obtain a list of running instances from the Environ and compare that
	// with the known instances stored in the machine.InstanceId() config.

	// although calling StopInstance with an empty slice should produce no change in the
	// provider, environs like dummy do not consider this a noop.
	if len(stopping) > 0 {
		return p.environ.StopInstances(stopping)
	}
	return nil
}

func (p *Provisioner) startMachines(machines []*state.Machine) ([]*state.Machine, error) {
	var started []*state.Machine
	for _, m := range machines {
		if err := p.startMachine(m); err != nil {
			return nil, err
		}
		log.Printf("starting machine %v", m)
		started = append(started, m)
	}
	return started, nil
}

func (p *Provisioner) startMachine(m *state.Machine) error {
	// TODO(dfc) the state.Info passed to environ.StartInstance remains contentious
	// however as the PA only knows one state.Info, and that info is used by MAs and
	// UAs to locate the ZK for this environment, it is logical to use the same
	// state.Info as the PA.
	inst, err := p.environ.StartInstance(m.Id(), p.info)
	if err != nil {
		return err
	}

	// assign the provider id to the macine
	if err := m.SetInstanceId(inst.Id()); err != nil {
		return fmt.Errorf("unable to store provider id for machine %v: %v", m, err)
	}

	// populate the local caches
	p.machineIdToProviderId[m.Id()] = inst.Id()
	p.providerIdToInstance[inst.Id()] = inst
	return nil
}

// instanceForMachine returns the environs.Instance that represents this machines' running
// instance.
func (p *Provisioner) instanceForMachine(m *state.Machine) (environs.Instance, error) {
	id, ok := p.machineIdToProviderId[m.Id()]
	if !ok {
		// not cached locally, ask the environ.
		var err error
		id, err = m.InstanceId()
		if err != nil {
			return nil, err
		}
		if id == "" {
			// nobody knows about this machine, give up.
			return nil, fmt.Errorf("machine %s not found", m)
		}
		p.machineIdToProviderId[m.Id()] = id
	}
	inst, ok := p.providerIdToInstance[id]
	if !ok {
		// not cached locally, ask the provider
		var err error
		inst, err = p.findInstance(id)
		if err != nil {
			// the provider doesn't know about this instance, give up.
			return nil, err
		}
		return nil, nil
	}
	return inst, nil
}

// instancesForMachines returns a list of environs.Instance that represent the list of machines running
// in the provider.
func (p *Provisioner) instancesForMachines(machines []*state.Machine) ([]environs.Instance, error) {
	var insts []environs.Instance
	for _, m := range machines {
		inst, err := p.instanceForMachine(m)
		if err != nil {
			return nil, err
		}
		insts = append(insts, inst)
	}
	return insts, nil
}

func (p *Provisioner) findInstance(id string) (environs.Instance, error) {
	insts, err := p.environ.Instances([]string{id})
	if err != nil {
		return nil, err
	}
	return insts[0], nil
}
<|endoftext|>


<commit_before>// +build !heroku

package main

import (
	"flag"
	"go/build"
	"log"
	"net/http"
	"strings"
	"time"

	_ "github.com/lib/pq"

	"github.com/psimika/secure-web-app/https"
	"github.com/psimika/secure-web-app/petfind/postgres"
	"github.com/psimika/secure-web-app/web"
)

func main() {
	var (
		dataSource    = flag.String("datasource", "", "the database URL")
		httpAddr      = flag.String("http", ":8080", "HTTP address for the server to listen on")
		httpsAddr     = flag.String("https", ":8443", "HTTPS address for the server to listen on")
		tmplPath      = flag.String("tmpl", defaultTmplPath(), "path containing the application's templates")
		insecureHTTP  = flag.Bool("insecure", false, "whether to serve insecure HTTP instead of HTTPS")
		certFile      = flag.String("tlscert", "", "TLS public key in PEM format used together with -tlskey")
		keyFile       = flag.String("tlskey", "", "TLS private key in PEM format used together with -tlscert")
		autocertHosts = flag.String("autocert", "", "One or more host names separated by space to get Let's Encrypt certificates automatically")
		autocertCache = flag.String("autocertdir", "", "directory to cache the Let's Encrypt certificates.")
	)
	flag.Parse()
	if !*insecureHTTP && *autocertHosts == "" && (*certFile == "" || *keyFile == "") {
		log.Println("Not enough flags set to start server, exiting...")
		log.Println("This application serves HTTPS by default.")
		log.Println("Use -autocert=example.com to specify a domain for an automatic Let's encrypt certificate.")
		log.Println("Or use -tlscert=<public key file> -tlskey=<private key file> to provide your own certificate.")
		log.Fatal("Or use the -insecure flag to serve insecure HTTP instead of HTTPS.")
	}

	if *dataSource == "" {
		log.Fatal("No database datasource provided, exiting...")
	}

	store, err := postgres.NewStore(*dataSource)
	if err != nil {
		log.Println("NewStore failed:", err)
		return
	}

	appHandlers, err := web.NewServer(*tmplPath, store)
	if err != nil {
		log.Println("NewServer failed:", err)
		return
	}

	if *insecureHTTP {
		log.Fatal(http.ListenAndServe(*httpAddr, appHandlers))
	}

	go func() {
		log.Printf("Serving HTTP->HTTPS redirect on %q", *httpAddr)
		redirectServer := newRedirectServer(*httpAddr, http.HandlerFunc(redirectHTTP))
		log.Fatal(redirectServer.ListenAndServe())
	}()

	if *autocertHosts != "" {
		hosts := strings.Split(*autocertHosts, " ")
		log.Printf("Serving HTTPS on %q using Let's Encrypt certificates for %v", *httpsAddr, hosts)
		if *autocertCache != "" {
			log.Printf("Caching Let's Encrypt certificates in %v", *autocertCache)
		}
		log.Fatal(https.ListenAndServeAutocert(*httpsAddr, *autocertCache, hosts, appHandlers))
	}

	log.Printf("Serving HTTPS on %q using provided certificates", *httpsAddr)
	log.Fatal(https.ListenAndServeTLS(*httpsAddr, *certFile, *keyFile, appHandlers))
}

func newRedirectServer(httpAddr string, redirectHandler http.Handler) *http.Server {
	return &http.Server{
		Addr:         httpAddr,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
		Handler:      redirectHandler,
	}
}

func defaultTmplPath() string {
	p, err := build.Import("github.com/psimika/secure-web-app/web", "", build.FindOnly)
	if err != nil {
		return ""
	}
	return p.Dir
}

func redirectHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Connection", "close")
	u := r.URL
	u.Host = r.Host
	u.Scheme = "https"
	http.Redirect(w, r, u.String(), http.StatusMovedPermanently)
}
<commit_msg>Add log message for serving insecure HTTP<commit_after>// +build !heroku

package main

import (
	"flag"
	"go/build"
	"log"
	"net/http"
	"strings"
	"time"

	_ "github.com/lib/pq"

	"github.com/psimika/secure-web-app/https"
	"github.com/psimika/secure-web-app/petfind/postgres"
	"github.com/psimika/secure-web-app/web"
)

func main() {
	var (
		dataSource    = flag.String("datasource", "", "the database URL")
		httpAddr      = flag.String("http", ":8080", "HTTP address for the server to listen on")
		httpsAddr     = flag.String("https", ":8443", "HTTPS address for the server to listen on")
		tmplPath      = flag.String("tmpl", defaultTmplPath(), "path containing the application's templates")
		insecureHTTP  = flag.Bool("insecure", false, "whether to serve insecure HTTP instead of HTTPS")
		certFile      = flag.String("tlscert", "", "TLS public key in PEM format used together with -tlskey")
		keyFile       = flag.String("tlskey", "", "TLS private key in PEM format used together with -tlscert")
		autocertHosts = flag.String("autocert", "", "One or more host names separated by space to get Let's Encrypt certificates automatically")
		autocertCache = flag.String("autocertdir", "", "directory to cache the Let's Encrypt certificates.")
	)
	flag.Parse()
	if !*insecureHTTP && *autocertHosts == "" && (*certFile == "" || *keyFile == "") {
		log.Println("Not enough flags set to start server, exiting...")
		log.Println("This application serves HTTPS by default.")
		log.Println("Use -autocert=example.com to specify a domain for an automatic Let's encrypt certificate.")
		log.Println("Or use -tlscert=<public key file> -tlskey=<private key file> to provide your own certificate.")
		log.Fatal("Or use the -insecure flag to serve insecure HTTP instead of HTTPS.")
	}

	if *dataSource == "" {
		log.Fatal("No database datasource provided, exiting...")
	}

	store, err := postgres.NewStore(*dataSource)
	if err != nil {
		log.Println("NewStore failed:", err)
		return
	}

	appHandlers, err := web.NewServer(*tmplPath, store)
	if err != nil {
		log.Println("NewServer failed:", err)
		return
	}

	if *insecureHTTP {
		log.Printf("Serving insecure HTTP on %q", *httpAddr)
		log.Fatal(http.ListenAndServe(*httpAddr, appHandlers))
	}

	go func() {
		log.Printf("Serving HTTP->HTTPS redirect on %q", *httpAddr)
		redirectServer := newRedirectServer(*httpAddr, http.HandlerFunc(redirectHTTP))
		log.Fatal(redirectServer.ListenAndServe())
	}()

	if *autocertHosts != "" {
		hosts := strings.Split(*autocertHosts, " ")
		log.Printf("Serving HTTPS on %q using Let's Encrypt certificates for %v", *httpsAddr, hosts)
		if *autocertCache != "" {
			log.Printf("Caching Let's Encrypt certificates in %v", *autocertCache)
		}
		log.Fatal(https.ListenAndServeAutocert(*httpsAddr, *autocertCache, hosts, appHandlers))
	}

	log.Printf("Serving HTTPS on %q using provided certificates", *httpsAddr)
	log.Fatal(https.ListenAndServeTLS(*httpsAddr, *certFile, *keyFile, appHandlers))
}

func newRedirectServer(httpAddr string, redirectHandler http.Handler) *http.Server {
	return &http.Server{
		Addr:         httpAddr,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
		Handler:      redirectHandler,
	}
}

func defaultTmplPath() string {
	p, err := build.Import("github.com/psimika/secure-web-app/web", "", build.FindOnly)
	if err != nil {
		return ""
	}
	return p.Dir
}

func redirectHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Connection", "close")
	u := r.URL
	u.Host = r.Host
	u.Scheme = "https"
	http.Redirect(w, r, u.String(), http.StatusMovedPermanently)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
)

var serviceURL = "https://packagecloud.io"
var token = os.Getenv("PACKAGECLOUD_TOKEN")

func pushPackage(dest, source string) error {
	s := strings.Split(dest, "/")
	user, repo, distro, version := s[0], s[1], s[2], s[3]

	distroVersionName := distro + "/" + version
	distroVersionID, ok := debDistros[distroVersionName]
	if !ok {
		return errors.New("Unknown distro version: " + distroVersionName)
	}

	endpoint := fmt.Sprintf("%s/api/v1/repos/%s/%s/packages.json",
		serviceURL, user, repo)
	extraParams := map[string]string{
		"package[distro_version_id]": strconv.Itoa(distroVersionID),
	}
	request, err := NewFileUploadRequest(endpoint, extraParams,
		"package[package_file]", source)
	if err != nil {
		return err
	}
	request.SetBasicAuth(token, "")

	client := &http.Client{}
	resp, err := client.Do(request)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if resp.StatusCode != http.StatusCreated {
		msg := fmt.Sprintf("HTTP error: %s\nHTTP body: %s\n",
			resp.Status, body)
		return errors.New(msg)
	}

	return nil
}

func main() {
	log.SetFlags(0)

	flag.Parse()
	if flag.NArg() < 2 {
		log.Fatal("Usage: pkgcloud push user/repo/distro/version /path/to/packages")
	}

	dest := flag.Args()[0]
	source := flag.Args()[1]

	fmt.Printf("Pushing %s to %s ...\n", source, dest)
	if err := pushPackage(dest, source); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Allow pushing multiple packages<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
)

var serviceURL = "https://packagecloud.io"
var token = os.Getenv("PACKAGECLOUD_TOKEN")

func pushPackage(dest, source string) error {
	s := strings.Split(dest, "/")
	user, repo, distro, version := s[0], s[1], s[2], s[3]

	// TODO: support other formats like .rpm
	distroVersionName := distro + "/" + version
	distroVersionID, ok := debDistros[distroVersionName]
	if !ok {
		return errors.New("Unknown distro version: " + distroVersionName)
	}

	endpoint := fmt.Sprintf("%s/api/v1/repos/%s/%s/packages.json",
		serviceURL, user, repo)
	extraParams := map[string]string{
		"package[distro_version_id]": strconv.Itoa(distroVersionID),
	}
	request, err := NewFileUploadRequest(endpoint, extraParams,
		"package[package_file]", source)
	if err != nil {
		return err
	}
	request.SetBasicAuth(token, "")

	client := &http.Client{}
	resp, err := client.Do(request)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	// TODO: unmarshal JSON to get error
	if resp.StatusCode != http.StatusCreated {
		msg := fmt.Sprintf("HTTP error: %s\nHTTP body: %s\n",
			resp.Status, body)
		return errors.New(msg)
	}

	return nil
}

func main() {
	log.SetFlags(0)

	flag.Parse()
	if flag.NArg() < 2 {
		log.Fatal("Usage: pkgcloud-push user/repo/distro/version /path/to/packages")
	}

	dest := flag.Args()[0]
	for _, source := range flag.Args()[1:] {
		fmt.Printf("Pushing %s to %s ...\n", source, dest)
		if err := pushPackage(dest, source); err != nil {
			log.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"log"
	"net"
	"os"
	"path/filepath"

	"github.com/buaazp/fasthttprouter"
	"github.com/henkman/steamquery"
	"github.com/valyala/fasthttp"
	"github.com/zserge/webview"
)

func Players(ctx *fasthttp.RequestCtx) {
	ctx.Response.Header.Set("Content-Type", "application/json")
	ctx.Response.Header.Set("Cache-Control", "No-Cache")
	server := ctx.UserValue("server").(string)
	ps, err := steamquery.QueryPlayersString(server)
	if err != nil {
		ctx.Response.SetBodyString("{'error':'" + err.Error() + "'}")
		return
	}
	json.NewEncoder(ctx.Response.BodyWriter()).Encode(&ps)
}

func Info(ctx *fasthttp.RequestCtx) {
	ctx.Response.Header.Set("Content-Type", "application/json")
	ctx.Response.Header.Set("Cache-Control", "No-Cache")
	server := ctx.UserValue("server").(string)
	res, err := steamquery.QueryInfoString(server)
	if err != nil {
		ctx.Response.SetBodyString("{'error':'" + err.Error() + "'}")
		return
	}
	json.NewEncoder(ctx.Response.BodyWriter()).Encode(&res)
}

func Index(server string) fasthttp.RequestHandler {
	return func(ctx *fasthttp.RequestCtx) {
		ctx.Response.Header.Set("X-UA-Compatible", "IE=edge")
		ctx.Response.Header.SetContentType("text/html")
		ctx.Response.SetBodyString(`

	<title>test</title>

	<input type="button" id="refresh" value="refresh">

	Name:

	Map:

	Players:

				Name
				Score
				Online
				Score/Second

`)
	}
}

var (
	_server string
)

func init() {
	flag.StringVar(&_server, "s", "", "server")
	flag.Parse()
}

func main() {
	if _server == "" {
		flag.Usage()
		return
	}
	exe, err := os.Executable()
	if err != nil {
		log.Fatal(err)
	}
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}
	defer ln.Close()
	go func() {
		router := fasthttprouter.New()
		router.GET("/", Index(_server))
		router.GET("/players/:server", Players)
		router.GET("/info/:server", Info)
		router.ServeFiles("/r/*filepath", filepath.Join(filepath.Dir(exe), "r"))
		log.Fatal(fasthttp.Serve(ln, router.Handler))
	}()
	log.Fatal(webview.Open("server state", "http://"+ln.Addr().String(),
		600, 800, false))
}
<commit_msg>serverstateui: ajax error now handled<commit_after>package main

import (
	"encoding/json"
	"flag"
	"log"
	"net"
	"os"
	"path/filepath"

	"github.com/buaazp/fasthttprouter"
	"github.com/henkman/steamquery"
	"github.com/valyala/fasthttp"
	"github.com/zserge/webview"
)

func Players(ctx *fasthttp.RequestCtx) {
	ctx.Response.Header.Set("Content-Type", "application/json")
	ctx.Response.Header.Set("Cache-Control", "No-Cache")
	server := ctx.UserValue("server").(string)
	ps, err := steamquery.QueryPlayersString(server)
	if err != nil {
		ctx.Response.SetBodyString(`{"error":"` + err.Error() + `"}`)
		return
	}
	json.NewEncoder(ctx.Response.BodyWriter()).Encode(&ps)
}

func Info(ctx *fasthttp.RequestCtx) {
	ctx.Response.Header.Set("Content-Type", "application/json")
	ctx.Response.Header.Set("Cache-Control", "No-Cache")
	server := ctx.UserValue("server").(string)
	res, err := steamquery.QueryInfoString(server)
	if err != nil {
		ctx.Response.SetBodyString(`{"error":"` + err.Error() + `"}`)
		return
	}
	json.NewEncoder(ctx.Response.BodyWriter()).Encode(&res)
}

func Index(server string) fasthttp.RequestHandler {
	return func(ctx *fasthttp.RequestCtx) {
		ctx.Response.Header.Set("X-UA-Compatible", "IE=edge")
		ctx.Response.Header.SetContentType("text/html")
		ctx.Response.SetBodyString(`

	<title>test</title>

	<input type="button" id="refresh" value="refresh">

	Name:

	Map:

	Players:

				Name
				Score
				Online
				Score/Second

`)
	}
}

var (
	_server string
)

func init() {
	flag.StringVar(&_server, "s", "", "server")
	flag.Parse()
}

func main() {
	if _server == "" {
		flag.Usage()
		return
	}
	exe, err := os.Executable()
	if err != nil {
		log.Fatal(err)
	}
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}
	defer ln.Close()
	go func() {
		router := fasthttprouter.New()
		router.GET("/", Index(_server))
		router.GET("/players/:server", Players)
		router.GET("/info/:server", Info)
		router.ServeFiles("/r/*filepath", filepath.Join(filepath.Dir(exe), "r"))
		log.Fatal(fasthttp.Serve(ln, router.Handler))
	}()
	log.Fatal(webview.Open("server state", "http://"+ln.Addr().String(),
		600, 800, false))
}
<|endoftext|>