<commit_before>/*
Copyright 2015 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 cache

import (
	"strings"
	"testing"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func testIndexFunc(obj interface{}) ([]string, error) {
	pod := obj.(*v1.Pod)
	return []string{pod.Labels["foo"]}, nil
}

func TestGetIndexFuncValues(t *testing.T) {
	index := NewIndexer(MetaNamespaceKeyFunc, Indexers{"testmodes": testIndexFunc})

	pod1 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "one", Labels: map[string]string{"foo": "bar"}}}
	pod2 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "two", Labels: map[string]string{"foo": "bar"}}}
	pod3 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "tre", Labels: map[string]string{"foo": "biz"}}}

	index.Add(pod1)
	index.Add(pod2)
	index.Add(pod3)

	keys := index.ListIndexFuncValues("testmodes")
	if len(keys) != 2 {
		t.Errorf("Expected 2 keys but got %v", len(keys))
	}

	for _, key := range keys {
		if key != "bar" && key != "biz" {
			t.Errorf("Expected only 'bar' or 'biz' but got %s", key)
		}
	}
}

func testUsersIndexFunc(obj interface{}) ([]string, error) {
	pod := obj.(*v1.Pod)
	usersString := pod.Annotations["users"]

	return strings.Split(usersString, ","), nil
}

func TestMultiIndexKeys(t *testing.T) {
	index := NewIndexer(MetaNamespaceKeyFunc, Indexers{"byUser": testUsersIndexFunc})

	pod1 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "one", Annotations: map[string]string{"users": "ernie,bert"}}}
	pod2 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "two", Annotations: map[string]string{"users": "bert,oscar"}}}
	pod3 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "tre", Annotations: map[string]string{"users": "ernie,elmo"}}}

	index.Add(pod1)
	index.Add(pod2)
	index.Add(pod3)

	erniePods, err := index.ByIndex("byUser", "ernie")
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if len(erniePods) != 2 {
		t.Errorf("Expected 2 pods but got %v", len(erniePods))
	}
	for _, erniePod := range erniePods {
		if erniePod.(*v1.Pod).Name != "one" && erniePod.(*v1.Pod).Name != "tre" {
			t.Errorf("Expected only 'one' or 'tre' but got %s", erniePod.(*v1.Pod).Name)
		}
	}

	bertPods, err := index.ByIndex("byUser", "bert")
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if len(bertPods) != 2 {
		t.Errorf("Expected 2 pods but got %v", len(bertPods))
	}
	for _, bertPod := range bertPods {
		if bertPod.(*v1.Pod).Name != "one" && bertPod.(*v1.Pod).Name != "two" {
			t.Errorf("Expected only 'one' or 'two' but got %s", bertPod.(*v1.Pod).Name)
		}
	}

	oscarPods, err := index.ByIndex("byUser", "oscar")
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if len(oscarPods) != 1 {
		t.Errorf("Expected 1 pods but got %v", len(erniePods))
	}
	for _, oscarPod := range oscarPods {
		if oscarPod.(*v1.Pod).Name != "two" {
			t.Errorf("Expected only 'two' but got %s", oscarPod.(*v1.Pod).Name)
		}
	}

	ernieAndBertKeys, err := index.Index("byUser", pod1)
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if len(ernieAndBertKeys) != 3 {
		t.Errorf("Expected 3 pods but got %v", len(ernieAndBertKeys))
	}
	for _, ernieAndBertKey := range ernieAndBertKeys {
		if ernieAndBertKey.(*v1.Pod).Name != "one" && ernieAndBertKey.(*v1.Pod).Name != "two" && ernieAndBertKey.(*v1.Pod).Name != "tre" {
			t.Errorf("Expected only 'one', 'two' or 'tre' but got %s", ernieAndBertKey.(*v1.Pod).Name)
		}
	}

	index.Delete(pod3)
	erniePods, err = index.ByIndex("byUser", "ernie")
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if len(erniePods) != 1 {
		t.Errorf("Expected 1 pods but got %v", len(erniePods))
	}
	for _, erniePod := range erniePods {
		if erniePod.(*v1.Pod).Name != "one" {
			t.Errorf("Expected only 'one' but got %s", erniePod.(*v1.Pod).Name)
		}
	}

	elmoPods, err := index.ByIndex("byUser", "elmo")
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if len(elmoPods) != 0 {
		t.Errorf("Expected 0 pods but got %v", len(elmoPods))
	}

	copyOfPod2 := pod2.DeepCopy()
	copyOfPod2.Annotations["users"] = "oscar"
	index.Update(copyOfPod2)
	bertPods, err = index.ByIndex("byUser", "bert")
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if len(bertPods) != 1 {
		t.Errorf("Expected 1 pods but got %v", len(bertPods))
	}
	for _, bertPod := range bertPods {
		if bertPod.(*v1.Pod).Name != "one" {
			t.Errorf("Expected only 'one' but got %s", bertPod.(*v1.Pod).Name)
		}
	}

}
<commit_msg>refactor index_test to compress the basic expected assertions<commit_after>/*
Copyright 2015 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 cache

import (
	"k8s.io/apimachinery/pkg/util/sets"
	"strings"
	"testing"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func testIndexFunc(obj interface{}) ([]string, error) {
	pod := obj.(*v1.Pod)
	return []string{pod.Labels["foo"]}, nil
}

func TestGetIndexFuncValues(t *testing.T) {
	index := NewIndexer(MetaNamespaceKeyFunc, Indexers{"testmodes": testIndexFunc})

	pod1 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "one", Labels: map[string]string{"foo": "bar"}}}
	pod2 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "two", Labels: map[string]string{"foo": "bar"}}}
	pod3 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "tre", Labels: map[string]string{"foo": "biz"}}}

	index.Add(pod1)
	index.Add(pod2)
	index.Add(pod3)

	keys := index.ListIndexFuncValues("testmodes")
	if len(keys) != 2 {
		t.Errorf("Expected 2 keys but got %v", len(keys))
	}

	for _, key := range keys {
		if key != "bar" && key != "biz" {
			t.Errorf("Expected only 'bar' or 'biz' but got %s", key)
		}
	}
}

func testUsersIndexFunc(obj interface{}) ([]string, error) {
	pod := obj.(*v1.Pod)
	usersString := pod.Annotations["users"]

	return strings.Split(usersString, ","), nil
}

func TestMultiIndexKeys(t *testing.T) {
	index := NewIndexer(MetaNamespaceKeyFunc, Indexers{"byUser": testUsersIndexFunc})

	pod1 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "one", Annotations: map[string]string{"users": "ernie,bert"}}}
	pod2 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "two", Annotations: map[string]string{"users": "bert,oscar"}}}
	pod3 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "tre", Annotations: map[string]string{"users": "ernie,elmo"}}}

	index.Add(pod1)
	index.Add(pod2)
	index.Add(pod3)

	expected := map[string]sets.String{}
	expected["ernie"] = sets.NewString("one", "tre")
	expected["bert"] = sets.NewString("one", "two")
	expected["elmo"] = sets.NewString("tre")
	expected["oscar"] = sets.NewString("two")
	expected["elmo"] = sets.NewString() // let's just make sure we don't get anything back in this case
	{
		for k, v := range expected {
			found := sets.String{}
			indexResults, err := index.ByIndex("byUser", k)
			if err != nil {
				t.Errorf("Unexpected error %v", err)
			}
			for _, item := range indexResults {
				found.Insert(item.(*v1.Pod).Name)
			}
			items := v.List()
			if !found.HasAll(items...) {
				t.Errorf("missing items, index %s, expected %v but found %v", k, items, found.List())
			}
		}
	}

	index.Delete(pod3)
	erniePods, err := index.ByIndex("byUser", "ernie")
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if len(erniePods) != 1 {
		t.Errorf("Expected 1 pods but got %v", len(erniePods))
	}
	for _, erniePod := range erniePods {
		if erniePod.(*v1.Pod).Name != "one" {
			t.Errorf("Expected only 'one' but got %s", erniePod.(*v1.Pod).Name)
		}
	}

	elmoPods, err := index.ByIndex("byUser", "elmo")
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if len(elmoPods) != 0 {
		t.Errorf("Expected 0 pods but got %v", len(elmoPods))
	}

	copyOfPod2 := pod2.DeepCopy()
	copyOfPod2.Annotations["users"] = "oscar"
	index.Update(copyOfPod2)
	bertPods, err := index.ByIndex("byUser", "bert")
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if len(bertPods) != 1 {
		t.Errorf("Expected 1 pods but got %v", len(bertPods))
	}
	for _, bertPod := range bertPods {
		if bertPod.(*v1.Pod).Name != "one" {
			t.Errorf("Expected only 'one' but got %s", bertPod.(*v1.Pod).Name)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package componentconfig

import (
	"fmt"
	"net"

	utilnet "k8s.io/kubernetes/pkg/util/net"
)

// used for validating command line opts
// TODO(mikedanese): remove these when we remove command line flags

type IPVar struct {
	Val *string
}

func (v IPVar) Set(s string) error {
	if net.ParseIP(s) == nil {
		return fmt.Errorf("%q is not a valid IP address", s)
	}
	if v.Val == nil {
		// it's okay to panic here since this is programmer error
		panic("the string pointer passed into IPVar should not be nil")
	}
	*v.Val = s
	return nil
}

func (v IPVar) String() string {
	if v.Val == nil {
		return ""
	}
	return *v.Val
}

func (v IPVar) Type() string {
	return "ip"
}

func (m *ProxyMode) Set(s string) error {
	nm := ProxyMode(s)
	m = &nm
	return nil
}

func (m *ProxyMode) String() string {
	if m != nil {
		return string(*m)
	}
	return ""
}

func (m *ProxyMode) Type() string {
	return "ProxyMode"
}

type PortRangeVar struct {
	Val *string
}

func (v PortRangeVar) Set(s string) error {
	if _, err := utilnet.ParsePortRange(s); err != nil {
		return fmt.Errorf("%q is not a valid port range: %v", s, err)
	}
	if v.Val == nil {
		// it's okay to panic here since this is programmer error
		panic("the string pointer passed into PortRangeVar should not be nil")
	}
	*v.Val = s
	return nil
}

func (v PortRangeVar) String() string {
	if v.Val == nil {
		return ""
	}
	return *v.Val
}

func (v PortRangeVar) Type() string {
	return "port-range"
}
<commit_msg>componentconfig: fix proxy mode set func<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package componentconfig

import (
	"fmt"
	"net"

	utilnet "k8s.io/kubernetes/pkg/util/net"
)

// used for validating command line opts
// TODO(mikedanese): remove these when we remove command line flags

type IPVar struct {
	Val *string
}

func (v IPVar) Set(s string) error {
	if net.ParseIP(s) == nil {
		return fmt.Errorf("%q is not a valid IP address", s)
	}
	if v.Val == nil {
		// it's okay to panic here since this is programmer error
		panic("the string pointer passed into IPVar should not be nil")
	}
	*v.Val = s
	return nil
}

func (v IPVar) String() string {
	if v.Val == nil {
		return ""
	}
	return *v.Val
}

func (v IPVar) Type() string {
	return "ip"
}

func (m *ProxyMode) Set(s string) error {
	*m = ProxyMode(s)
	return nil
}

func (m *ProxyMode) String() string {
	if m != nil {
		return string(*m)
	}
	return ""
}

func (m *ProxyMode) Type() string {
	return "ProxyMode"
}

type PortRangeVar struct {
	Val *string
}

func (v PortRangeVar) Set(s string) error {
	if _, err := utilnet.ParsePortRange(s); err != nil {
		return fmt.Errorf("%q is not a valid port range: %v", s, err)
	}
	if v.Val == nil {
		// it's okay to panic here since this is programmer error
		panic("the string pointer passed into PortRangeVar should not be nil")
	}
	*v.Val = s
	return nil
}

func (v PortRangeVar) String() string {
	if v.Val == nil {
		return ""
	}
	return *v.Val
}

func (v PortRangeVar) Type() string {
	return "port-range"
}
<|endoftext|>


<commit_before>package runcexecutor

import (
	"context"
	"encoding/json"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/containerd/containerd/contrib/seccomp"
	"github.com/containerd/containerd/mount"
	containerdoci "github.com/containerd/containerd/oci"
	"github.com/containerd/continuity/fs"
	runc "github.com/containerd/go-runc"
	"github.com/moby/buildkit/cache"
	"github.com/moby/buildkit/executor"
	"github.com/moby/buildkit/executor/oci"
	"github.com/moby/buildkit/identity"
	"github.com/moby/buildkit/solver/pb"
	"github.com/moby/buildkit/util/network"
	rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv"
	"github.com/moby/buildkit/util/system"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type Opt struct {
	// root directory
	Root              string
	CommandCandidates []string
	// without root privileges (has nothing to do with Opt.Root directory)
	Rootless bool
	// DefaultCgroupParent is the cgroup-parent name for executor
	DefaultCgroupParent string
}

var defaultCommandCandidates = []string{"buildkit-runc", "runc"}

type runcExecutor struct {
	runc             *runc.Runc
	root             string
	cmd              string
	cgroupParent     string
	rootless         bool
	networkProviders map[pb.NetMode]network.Provider
}

func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Executor, error) {
	cmds := opt.CommandCandidates
	if cmds == nil {
		cmds = defaultCommandCandidates
	}

	var cmd string
	var found bool
	for _, cmd = range cmds {
		if _, err := exec.LookPath(cmd); err == nil {
			found = true
			break
		}
	}
	if !found {
		return nil, errors.Errorf("failed to find %s binary", cmd)
	}

	root := opt.Root

	if err := os.MkdirAll(root, 0700); err != nil {
		return nil, errors.Wrapf(err, "failed to create %s", root)
	}

	root, err := filepath.Abs(root)
	if err != nil {
		return nil, err
	}
	root, err = filepath.EvalSymlinks(root)
	if err != nil {
		return nil, err
	}

	runtime := &runc.Runc{
		Command:      cmd,
		Log:          filepath.Join(root, "runc-log.json"),
		LogFormat:    runc.JSON,
		PdeathSignal: syscall.SIGKILL,
		Setpgid:      true,
		// we don't execute runc with --rootless=(true|false) explicitly,
		// so as to support non-runc runtimes
	}

	w := &runcExecutor{
		runc:             runtime,
		root:             root,
		cgroupParent:     opt.DefaultCgroupParent,
		rootless:         opt.Rootless,
		networkProviders: networkProviders,
	}
	return w, nil
}

func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.Mountable, mounts []executor.Mount, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error {
	provider, ok := w.networkProviders[meta.NetMode]
	if !ok {
		return errors.Errorf("unknown network mode %s", meta.NetMode)
	}
	namespace, err := provider.New()
	if err != nil {
		return err
	}
	defer namespace.Close()

	if meta.NetMode == pb.NetMode_HOST {
		logrus.Info("enabling HostNetworking")
	}

	resolvConf, err := oci.GetResolvConf(ctx, w.root)
	if err != nil {
		return err
	}

	hostsFile, clean, err := oci.GetHostsFile(ctx, w.root, meta.ExtraHosts)
	if err != nil {
		return err
	}
	if clean != nil {
		defer clean()
	}

	mountable, err := root.Mount(ctx, false)
	if err != nil {
		return err
	}

	rootMount, err := mountable.Mount()
	if err != nil {
		return err
	}
	defer mountable.Release()

	id := identity.NewID()
	bundle := filepath.Join(w.root, id)

	if err := os.Mkdir(bundle, 0700); err != nil {
		return err
	}
	defer os.RemoveAll(bundle)
	rootFSPath := filepath.Join(bundle, "rootfs")
	if err := os.Mkdir(rootFSPath, 0700); err != nil {
		return err
	}
	if err := mount.All(rootMount, rootFSPath); err != nil {
		return err
	}
	defer mount.Unmount(rootFSPath, 0)

	uid, gid, sgids, err := oci.GetUser(ctx, rootFSPath, meta.User)
	if err != nil {
		return err
	}

	f, err := os.Create(filepath.Join(bundle, "config.json"))
	if err != nil {
		return err
	}
	defer f.Close()

	opts := []containerdoci.SpecOpts{oci.WithUIDGID(uid, gid, sgids)}
	if system.SeccompSupported() {
		opts = append(opts, seccomp.WithDefaultProfile())
	}
	if meta.ReadonlyRootFS {
		opts = append(opts, containerdoci.WithRootFSReadonly())
	}

	if w.cgroupParent != "" {
		var cgroupsPath string
		lastSeparator := w.cgroupParent[len(w.cgroupParent)-1:]
		if strings.Contains(w.cgroupParent, ".slice") && lastSeparator == ":" {
			cgroupsPath = w.cgroupParent + id
		} else {
			cgroupsPath = filepath.Join("/", w.cgroupParent, "buildkit", id)
		}
		opts = append(opts, containerdoci.WithCgroup(cgroupsPath))
	}
	spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, opts...)
	if err != nil {
		return err
	}
	defer cleanup()

	spec.Root.Path = rootFSPath
	if _, ok := root.(cache.ImmutableRef); ok { // TODO: pass in with mount, not ref type
		spec.Root.Readonly = true
	}

	newp, err := fs.RootPath(rootFSPath, meta.Cwd)
	if err != nil {
		return errors.Wrapf(err, "working dir %s points to invalid target", newp)
	}
	if err := os.MkdirAll(newp, 0755); err != nil {
		return errors.Wrapf(err, "failed to create working directory %s", newp)
	}

	if err := setOOMScoreAdj(spec); err != nil {
		return err
	}
	if w.rootless {
		if err := rootlessspecconv.ToRootless(spec); err != nil {
			return err
		}
	}

	if err := json.NewEncoder(f).Encode(spec); err != nil {
		return err
	}

	forwardIO, err := newForwardIO(stdin, stdout, stderr)
	if err != nil {
		return errors.Wrap(err, "creating new forwarding IO")
	}
	defer forwardIO.Close()

	logrus.Debugf("> creating %s %v", id, meta.Args)
	status, err := w.runc.Run(ctx, id, bundle, &runc.CreateOpts{
		IO: forwardIO,
	})
	if err != nil {
		return err
	}

	if status != 0 {
		return errors.Errorf("exit code: %d", status)
	}

	return nil
}

type forwardIO struct {
	stdin, stdout, stderr *os.File
	toRelease             []io.Closer
	toClose               []io.Closer
}

func newForwardIO(stdin io.ReadCloser, stdout, stderr io.WriteCloser) (f *forwardIO, err error) {
	fio := &forwardIO{}
	defer func() {
		if err != nil {
			fio.Close()
		}
	}()
	if stdin != nil {
		fio.stdin, err = fio.readCloserToFile(stdin)
		if err != nil {
			return nil, err
		}
	}
	if stdout != nil {
		fio.stdout, err = fio.writeCloserToFile(stdout)
		if err != nil {
			return nil, err
		}
	}
	if stderr != nil {
		fio.stderr, err = fio.writeCloserToFile(stderr)
		if err != nil {
			return nil, err
		}
	}
	return fio, nil
}

func (s *forwardIO) Close() error {
	s.CloseAfterStart()
	var err error
	for _, cl := range s.toClose {
		if err1 := cl.Close(); err == nil {
			err = err1
		}
	}
	s.toClose = nil
	return err
}

// release releases active FDs if the process doesn't need them any more
func (s *forwardIO) CloseAfterStart() error {
	for _, cl := range s.toRelease {
		cl.Close()
	}
	s.toRelease = nil
	return nil
}

func (s *forwardIO) Set(cmd *exec.Cmd) {
	cmd.Stdin = s.stdin
	cmd.Stdout = s.stdout
	cmd.Stderr = s.stderr
}

func (s *forwardIO) readCloserToFile(rc io.ReadCloser) (*os.File, error) {
	if f, ok := rc.(*os.File); ok {
		return f, nil
	}
	pr, pw, err := os.Pipe()
	if err != nil {
		return nil, err
	}
	s.toClose = append(s.toClose, pw)
	s.toRelease = append(s.toRelease, pr)
	go func() {
		_, err := io.Copy(pw, rc)
		if err1 := pw.Close(); err == nil {
			err = err1
		}
		_ = err
	}()
	return pr, nil
}

func (s *forwardIO) writeCloserToFile(wc io.WriteCloser) (*os.File, error) {
	if f, ok := wc.(*os.File); ok {
		return f, nil
	}
	pr, pw, err := os.Pipe()
	if err != nil {
		return nil, err
	}
	s.toClose = append(s.toClose, pr)
	s.toRelease = append(s.toRelease, pw)
	go func() {
		_, err := io.Copy(wc, pr)
		if err1 := pw.Close(); err == nil {
			err = err1
		}
		_ = err
	}()
	return pw, nil
}

func (s *forwardIO) Stdin() io.WriteCloser {
	return nil
}

func (s *forwardIO) Stdout() io.ReadCloser {
	return nil
}

func (s *forwardIO) Stderr() io.ReadCloser {
	return nil
}

// setOOMScoreAdj comes from https://github.com/genuinetools/img/blob/2fabe60b7dc4623aa392b515e013bbc69ad510ab/executor/runc/executor.go#L182-L192
func setOOMScoreAdj(spec *specs.Spec) error {
	// Set the oom_score_adj of our children containers to that of the current process.
	b, err := ioutil.ReadFile("/proc/self/oom_score_adj")
	if err != nil {
		return errors.Wrap(err, "failed to read /proc/self/oom_score_adj")
	}
	s := strings.TrimSpace(string(b))
	oom, err := strconv.Atoi(s)
	if err != nil {
		return errors.Wrapf(err, "failed to parse %s as int", s)
	}
	spec.Process.OOMScoreAdj = &oom
	return nil
}
<commit_msg>runcexecutor: revert forwardio<commit_after>package runcexecutor

import (
	"context"
	"encoding/json"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/containerd/containerd/contrib/seccomp"
	"github.com/containerd/containerd/mount"
	containerdoci "github.com/containerd/containerd/oci"
	"github.com/containerd/continuity/fs"
	runc "github.com/containerd/go-runc"
	"github.com/moby/buildkit/cache"
	"github.com/moby/buildkit/executor"
	"github.com/moby/buildkit/executor/oci"
	"github.com/moby/buildkit/identity"
	"github.com/moby/buildkit/solver/pb"
	"github.com/moby/buildkit/util/network"
	rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv"
	"github.com/moby/buildkit/util/system"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type Opt struct {
	// root directory
	Root              string
	CommandCandidates []string
	// without root privileges (has nothing to do with Opt.Root directory)
	Rootless bool
	// DefaultCgroupParent is the cgroup-parent name for executor
	DefaultCgroupParent string
}

var defaultCommandCandidates = []string{"buildkit-runc", "runc"}

type runcExecutor struct {
	runc             *runc.Runc
	root             string
	cmd              string
	cgroupParent     string
	rootless         bool
	networkProviders map[pb.NetMode]network.Provider
}

func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Executor, error) {
	cmds := opt.CommandCandidates
	if cmds == nil {
		cmds = defaultCommandCandidates
	}

	var cmd string
	var found bool
	for _, cmd = range cmds {
		if _, err := exec.LookPath(cmd); err == nil {
			found = true
			break
		}
	}
	if !found {
		return nil, errors.Errorf("failed to find %s binary", cmd)
	}

	root := opt.Root

	if err := os.MkdirAll(root, 0700); err != nil {
		return nil, errors.Wrapf(err, "failed to create %s", root)
	}

	root, err := filepath.Abs(root)
	if err != nil {
		return nil, err
	}
	root, err = filepath.EvalSymlinks(root)
	if err != nil {
		return nil, err
	}

	runtime := &runc.Runc{
		Command:      cmd,
		Log:          filepath.Join(root, "runc-log.json"),
		LogFormat:    runc.JSON,
		PdeathSignal: syscall.SIGKILL,
		Setpgid:      true,
		// we don't execute runc with --rootless=(true|false) explicitly,
		// so as to support non-runc runtimes
	}

	w := &runcExecutor{
		runc:             runtime,
		root:             root,
		cgroupParent:     opt.DefaultCgroupParent,
		rootless:         opt.Rootless,
		networkProviders: networkProviders,
	}
	return w, nil
}

func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.Mountable, mounts []executor.Mount, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error {
	provider, ok := w.networkProviders[meta.NetMode]
	if !ok {
		return errors.Errorf("unknown network mode %s", meta.NetMode)
	}
	namespace, err := provider.New()
	if err != nil {
		return err
	}
	defer namespace.Close()

	if meta.NetMode == pb.NetMode_HOST {
		logrus.Info("enabling HostNetworking")
	}

	resolvConf, err := oci.GetResolvConf(ctx, w.root)
	if err != nil {
		return err
	}

	hostsFile, clean, err := oci.GetHostsFile(ctx, w.root, meta.ExtraHosts)
	if err != nil {
		return err
	}
	if clean != nil {
		defer clean()
	}

	mountable, err := root.Mount(ctx, false)
	if err != nil {
		return err
	}

	rootMount, err := mountable.Mount()
	if err != nil {
		return err
	}
	defer mountable.Release()

	id := identity.NewID()
	bundle := filepath.Join(w.root, id)

	if err := os.Mkdir(bundle, 0700); err != nil {
		return err
	}
	defer os.RemoveAll(bundle)
	rootFSPath := filepath.Join(bundle, "rootfs")
	if err := os.Mkdir(rootFSPath, 0700); err != nil {
		return err
	}
	if err := mount.All(rootMount, rootFSPath); err != nil {
		return err
	}
	defer mount.Unmount(rootFSPath, 0)

	uid, gid, sgids, err := oci.GetUser(ctx, rootFSPath, meta.User)
	if err != nil {
		return err
	}

	f, err := os.Create(filepath.Join(bundle, "config.json"))
	if err != nil {
		return err
	}
	defer f.Close()

	opts := []containerdoci.SpecOpts{oci.WithUIDGID(uid, gid, sgids)}
	if system.SeccompSupported() {
		opts = append(opts, seccomp.WithDefaultProfile())
	}
	if meta.ReadonlyRootFS {
		opts = append(opts, containerdoci.WithRootFSReadonly())
	}

	if w.cgroupParent != "" {
		var cgroupsPath string
		lastSeparator := w.cgroupParent[len(w.cgroupParent)-1:]
		if strings.Contains(w.cgroupParent, ".slice") && lastSeparator == ":" {
			cgroupsPath = w.cgroupParent + id
		} else {
			cgroupsPath = filepath.Join("/", w.cgroupParent, "buildkit", id)
		}
		opts = append(opts, containerdoci.WithCgroup(cgroupsPath))
	}
	spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, opts...)
	if err != nil {
		return err
	}
	defer cleanup()

	spec.Root.Path = rootFSPath
	if _, ok := root.(cache.ImmutableRef); ok { // TODO: pass in with mount, not ref type
		spec.Root.Readonly = true
	}

	newp, err := fs.RootPath(rootFSPath, meta.Cwd)
	if err != nil {
		return errors.Wrapf(err, "working dir %s points to invalid target", newp)
	}
	if err := os.MkdirAll(newp, 0755); err != nil {
		return errors.Wrapf(err, "failed to create working directory %s", newp)
	}

	if err := setOOMScoreAdj(spec); err != nil {
		return err
	}
	if w.rootless {
		if err := rootlessspecconv.ToRootless(spec); err != nil {
			return err
		}
	}

	if err := json.NewEncoder(f).Encode(spec); err != nil {
		return err
	}

	logrus.Debugf("> creating %s %v", id, meta.Args)
	status, err := w.runc.Run(ctx, id, bundle, &runc.CreateOpts{
		IO: &forwardIO{stdin: stdin, stdout: stdout, stderr: stderr},
	})
	if err != nil {
		return err
	}

	if status != 0 {
		return errors.Errorf("exit code: %d", status)
	}

	return nil
}

type forwardIO struct {
	stdin          io.ReadCloser
	stdout, stderr io.WriteCloser
}

func (s *forwardIO) Close() error {
	return nil
}

func (s *forwardIO) Set(cmd *exec.Cmd) {
	cmd.Stdin = s.stdin
	cmd.Stdout = s.stdout
	cmd.Stderr = s.stderr
}

func (s *forwardIO) Stdin() io.WriteCloser {
	return nil
}

func (s *forwardIO) Stdout() io.ReadCloser {
	return nil
}

func (s *forwardIO) Stderr() io.ReadCloser {
	return nil
}

// setOOMScoreAdj comes from https://github.com/genuinetools/img/blob/2fabe60b7dc4623aa392b515e013bbc69ad510ab/executor/runc/executor.go#L182-L192
func setOOMScoreAdj(spec *specs.Spec) error {
	// Set the oom_score_adj of our children containers to that of the current process.
	b, err := ioutil.ReadFile("/proc/self/oom_score_adj")
	if err != nil {
		return errors.Wrap(err, "failed to read /proc/self/oom_score_adj")
	}
	s := strings.TrimSpace(string(b))
	oom, err := strconv.Atoi(s)
	if err != nil {
		return errors.Wrapf(err, "failed to parse %s as int", s)
	}
	spec.Process.OOMScoreAdj = &oom
	return nil
}
<|endoftext|>


<commit_before>package gaurun

import (
	"bytes"
	"errors"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

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

type globalsStore struct {
	ConfGaurun        ConfToml
	QueueNotification chan RequestGaurunNotification
}

var (
	store globalsStore
)

func (store globalsStore) save() {
	store.QueueNotification = QueueNotification
	store.ConfGaurun = ConfGaurun
}

func (store globalsStore) restore() {
	QueueNotification = store.QueueNotification
	ConfGaurun = store.ConfGaurun
}

func TestEnqueueNotifications(t *testing.T) {
	store.save()
	defer store.restore()

	positiveCases := []struct {
		N []RequestGaurunNotification
		C ConfToml
	}{
		{
			[]RequestGaurunNotification{
				{
					Tokens:   []string{"test token"},
					Platform: 1,
					Message:  "test message",
				},
				{
					Tokens:   []string{"test token"},
					Platform: 2,
					Message:  "test message",
				},
			},
			ConfToml{Ios: SectionIos{Enabled: true}, Android: SectionAndroid{Enabled: true}},
		},
	}

	negativeCases := []struct {
		N []RequestGaurunNotification
		C ConfToml
	}{
		// push is disabled
		{
			[]RequestGaurunNotification{
				{
					Tokens:   []string{"test token"},
					Platform: 1,
					Message:  "test message",
				},
				{
					Tokens:   []string{"test token"},
					Platform: 2,
					Message:  "test message",
				},
			},
			ConfToml{Ios: SectionIos{Enabled: false}, Android: SectionAndroid{Enabled: false}},
		},

		// config is invalid
		{
			[]RequestGaurunNotification{
				{
					Tokens: []string{""},
				},
			},
			ConfToml{Ios: SectionIos{Enabled: true}, Android: SectionAndroid{Enabled: true}},
		},
		{
			[]RequestGaurunNotification{
				{
					Tokens:   []string{"test token"},
					Platform: 100, /* neither iOS nor Android */
					Message:  "test message",
				},
			},
			ConfToml{Ios: SectionIos{Enabled: true}, Android: SectionAndroid{Enabled: true}},
		},
	}

	for _, c := range positiveCases {
		QueueNotification = make(chan RequestGaurunNotification, len(c.N))
		ConfGaurun = c.C

		enqueueNotifications(c.N)

		assert.Equal(t, len(QueueNotification), len(c.N))
	}

	for _, c := range negativeCases {
		QueueNotification = make(chan RequestGaurunNotification, len(c.N))
		ConfGaurun = c.C

		enqueueNotifications(c.N)

		assert.Equal(t, len(QueueNotification), 0)
	}
}

func TestValidateNotification(t *testing.T) {
	cases := []struct {
		Notification RequestGaurunNotification
		Expected     error
	}{
		// positive cases
		{
			RequestGaurunNotification{
				Tokens:   []string{"test token"},
				Platform: 1,
				Message:  "test message",
			},
			nil,
		},
		{
			RequestGaurunNotification{
				Tokens:   []string{"test token"},
				Platform: 2,
				Message:  "test message",
			},
			nil,
		},

		// negative cases
		{
			RequestGaurunNotification{
				Tokens: []string{""},
			},
			errors.New("empty token"),
		},
		{
			RequestGaurunNotification{
				Tokens:   []string{"test token"},
				Platform: 100, /* neither iOS nor Android */
			},
			errors.New("invalid platform"),
		},
		{
			RequestGaurunNotification{
				Tokens:   []string{"test token"},
				Platform: 1,
				Message:  "",
			},
			errors.New("empty message"),
		},
	}

	for _, c := range cases {
		actual := validateNotification(&c.Notification)
		assert.Equal(t, actual, c.Expected)
	}
}

func TestSendResponse(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		sendResponse(w, "valid message", http.StatusOK)
		return
	}))
	defer s.Close()

	res, err := http.Get(s.URL)
	assert.Nil(t, err)
	assert.Equal(t, res.StatusCode, http.StatusOK)

	body, err := ioutil.ReadAll(res.Body)
	assert.Nil(t, err)
	assert.Equal(t, string(body), "{\"message\":\"valid message\"}\n")
}

func TestPushNotificationHandler(t *testing.T) {
	store.save()
	defer store.restore()

	cases := []struct {
		Method       string
		RequestBody  string
		Conf         ConfToml
		ExpectedBody string
		ExpectedCode int
	}{
		{
			"GET",
			"",
			ConfToml{},
			"{\"message\":\"method must be POST\"}\n",
			http.StatusBadRequest,
		},
		{
			"POST",
			"",
			ConfToml{},
			"{\"message\":\"request body is empty\"}\n",
			http.StatusBadRequest,
		},
		{
			"POST",
			"invalid json",
			ConfToml{},
			"{\"message\":\"Request-body is malformed\"}\n",
			http.StatusBadRequest,
		},
		/* NOTE It gets "empty notification" actually ...
		   {
		     "POST",
		     "invalid json",
		     ConfToml{Log: SectionLog{Level: "debug"}},
		     "{\"message\":\"Request-body is malformed\"}\n",
		     http.StatusBadRequest,
		   },
		*/
		{
			"POST",
			"{\"notifications\":[]}",
			ConfToml{},
			"{\"message\":\"empty notification\"}\n",
			http.StatusBadRequest,
		},
		{
			"POST",
			"{\"notifications\":[{}]}",
			ConfToml{Core: SectionCore{NotificationMax: 0}},
			"{\"message\":\"number of notifications(1) over limit(0)\"}\n",
			http.StatusBadRequest,
		},
		// NOTE It will cause goroutine leak ...
		{
			"POST",
			"{\"notifications\":[{}]}",
			ConfToml{Core: SectionCore{NotificationMax: 10}},
			"{\"message\":\"ok\"}\n",
			http.StatusOK,
		},
	}

	for _, c := range cases {
		s := httptest.NewServer(http.HandlerFunc(PushNotificationHandler))
		defer s.Close()

		ConfGaurun = c.Conf

		client := http.Client{}
		req, _ := http.NewRequest(c.Method, s.URL, bytes.NewBufferString(c.RequestBody))
		res, err := client.Do(req)

		assert.Nil(t, err)
		assert.Equal(t, res.StatusCode, c.ExpectedCode)

		body, err := ioutil.ReadAll(res.Body)
		assert.Nil(t, err)
		assert.Equal(t, string(body), c.ExpectedBody)
	}
}
<commit_msg>Remove test codes modify global variables<commit_after>package gaurun

import (
	"errors"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

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

func TestValidateNotification(t *testing.T) {
	cases := []struct {
		Notification RequestGaurunNotification
		Expected     error
	}{
		// positive cases
		{
			RequestGaurunNotification{
				Tokens:   []string{"test token"},
				Platform: 1,
				Message:  "test message",
			},
			nil,
		},
		{
			RequestGaurunNotification{
				Tokens:   []string{"test token"},
				Platform: 2,
				Message:  "test message",
			},
			nil,
		},

		// negative cases
		{
			RequestGaurunNotification{
				Tokens: []string{""},
			},
			errors.New("empty token"),
		},
		{
			RequestGaurunNotification{
				Tokens:   []string{"test token"},
				Platform: 100, /* neither iOS nor Android */
			},
			errors.New("invalid platform"),
		},
		{
			RequestGaurunNotification{
				Tokens:   []string{"test token"},
				Platform: 1,
				Message:  "",
			},
			errors.New("empty message"),
		},
	}

	for _, c := range cases {
		actual := validateNotification(&c.Notification)
		assert.Equal(t, actual, c.Expected)
	}
}

func TestSendResponse(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		sendResponse(w, "valid message", http.StatusOK)
		return
	}))
	defer s.Close()

	res, err := http.Get(s.URL)
	assert.Nil(t, err)
	assert.Equal(t, res.StatusCode, http.StatusOK)

	body, err := ioutil.ReadAll(res.Body)
	assert.Nil(t, err)
	assert.Equal(t, string(body), "{\"message\":\"valid message\"}\n")
}

<|endoftext|>


<commit_before>/*
   Copyright 2020 Docker Compose CLI 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 e2e

import (
	"strings"
	"testing"

	"gotest.tools/v3/assert"
	"gotest.tools/v3/icmd"
)

func TestEnvPriority(t *testing.T) {
	c := NewParallelCLI(t)

	projectDir := "./fixtures/environment/env-priority"

	t.Run("up", func(t *testing.T) {
		c.RunDockerOrExitError(t, "rmi", "env-compose-priority")
		c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose-with-env.yaml",
			"--project-directory", projectDir, "up", "-d", "--build")
	})

	// Full options activated
	// 1. Compose file <-- Result expected
	// 2. Shell environment variables
	// 3. Environment file
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("compose file priority", func(t *testing.T) {
		cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose-with-env.yaml",
			"--project-directory", projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run",
			"--rm", "-e", "WHEREAMI", "env-compose-priority")
		res := icmd.RunCmd(cmd, icmd.WithEnv("WHEREAMI=shell"))
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "Compose File")
	})

	// No Compose file, all other options
	// 1. Compose file
	// 2. Shell environment variables <-- Result expected
	// 3. Environment file
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("shell priority", func(t *testing.T) {
		cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
			projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run", "--rm", "-e",
			"WHEREAMI", "env-compose-priority")
		res := icmd.RunCmd(cmd, icmd.WithEnv("WHEREAMI=shell"))
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "shell")
	})

	//  No Compose file and env variable pass to the run command
	// 1. Compose file
	// 2. Shell environment variables <-- Result expected
	// 3. Environment file
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("shell priority from run command", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
			projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run", "--rm", "-e",
			"WHEREAMI=shell-run", "env-compose-priority")
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "shell-run")
	})

	//  No Compose file & no env variable but override env file
	// 1. Compose file
	// 2. Shell environment variables
	// 3. Environment file <-- Result expected
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("override env file", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
			projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run", "--rm", "-e",
			"WHEREAMI", "env-compose-priority")
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "override")
	})

	//  No Compose file & no env variable but override env file
	// 1. Compose file
	// 2. Shell environment variables
	// 3. Environment file <-- Result expected
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("env file", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
			projectDir, "run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "Env File")
	})

	//  No Compose file & no env variable, using an empty override env file
	// 1. Compose file
	// 2. Shell environment variables
	// 3. Environment file
	// 4. Dockerfile   <-- Result expected
	// 5. Variable is not defined
	t.Run("use Dockerfile", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
			projectDir, "--env-file", "./fixtures/environment/env-priority/.env.empty", "run", "--rm", "-e", "WHEREAMI",
			"env-compose-priority")
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "Dockerfile")
	})

	t.Run("down", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "--project-directory", projectDir, "down")
	})
}

func TestEnvInterpolation(t *testing.T) {
	c := NewParallelCLI(t)

	projectDir := "./fixtures/environment/env-interpolation"

	//  No variable defined in the Compose file and env variable pass to the run command
	// 1. Compose file
	// 2. Shell environment variables <-- Result expected
	// 3. Environment file
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("shell priority from run command", func(t *testing.T) {
		cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-interpolation/compose.yaml",
			"--project-directory", projectDir, "config")
		res := icmd.RunCmd(cmd, icmd.WithEnv("WHEREAMI=shell"))
		res.Assert(t, icmd.Expected{Out: `IMAGE: default_env:shell`})
	})
}

func TestCommentsInEnvFile(t *testing.T) {
	c := NewParallelCLI(t)

	projectDir := "./fixtures/environment/env-file-comments"

	t.Run("comments in env files", func(t *testing.T) {
		c.RunDockerOrExitError(t, "rmi", "env-file-comments")

		c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-file-comments/compose.yaml", "--project-directory",
			projectDir, "up", "-d", "--build")

		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-file-comments/compose.yaml",
			"--project-directory", projectDir, "run", "--rm", "-e", "COMMENT", "-e", "NO_COMMENT", "env-file-comments")

		res.Assert(t, icmd.Expected{Out: `COMMENT=1234`})
		res.Assert(t, icmd.Expected{Out: `NO_COMMENT=1234#5`})

		c.RunDockerComposeCmd(t, "--project-directory", projectDir, "down", "--rmi", "all")
	})
}
<commit_msg>e2e: fix per-command env overrides<commit_after>/*
   Copyright 2020 Docker Compose CLI 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 e2e

import (
	"strings"
	"testing"

	"gotest.tools/v3/assert"
	"gotest.tools/v3/icmd"
)

func TestEnvPriority(t *testing.T) {
	c := NewParallelCLI(t)

	projectDir := "./fixtures/environment/env-priority"

	t.Run("up", func(t *testing.T) {
		c.RunDockerOrExitError(t, "rmi", "env-compose-priority")
		c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose-with-env.yaml",
			"--project-directory", projectDir, "up", "-d", "--build")
	})

	// Full options activated
	// 1. Compose file <-- Result expected
	// 2. Shell environment variables
	// 3. Environment file
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("compose file priority", func(t *testing.T) {
		cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose-with-env.yaml",
			"--project-directory", projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run",
			"--rm", "-e", "WHEREAMI", "env-compose-priority")
		cmd.Env = append(cmd.Env, "WHEREAMI=shell")
		res := icmd.RunCmd(cmd)
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "Compose File")
	})

	// No Compose file, all other options
	// 1. Compose file
	// 2. Shell environment variables <-- Result expected
	// 3. Environment file
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("shell priority", func(t *testing.T) {
		cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
			projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run", "--rm", "-e",
			"WHEREAMI", "env-compose-priority")
		cmd.Env = append(cmd.Env, "WHEREAMI=shell")
		res := icmd.RunCmd(cmd)
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "shell")
	})

	//  No Compose file and env variable pass to the run command
	// 1. Compose file
	// 2. Shell environment variables <-- Result expected
	// 3. Environment file
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("shell priority from run command", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
			projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run", "--rm", "-e",
			"WHEREAMI=shell-run", "env-compose-priority")
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "shell-run")
	})

	//  No Compose file & no env variable but override env file
	// 1. Compose file
	// 2. Shell environment variables
	// 3. Environment file <-- Result expected
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("override env file", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
			projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run", "--rm", "-e",
			"WHEREAMI", "env-compose-priority")
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "override")
	})

	//  No Compose file & no env variable but override env file
	// 1. Compose file
	// 2. Shell environment variables
	// 3. Environment file <-- Result expected
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("env file", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
			projectDir, "run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "Env File")
	})

	//  No Compose file & no env variable, using an empty override env file
	// 1. Compose file
	// 2. Shell environment variables
	// 3. Environment file
	// 4. Dockerfile   <-- Result expected
	// 5. Variable is not defined
	t.Run("use Dockerfile", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
			projectDir, "--env-file", "./fixtures/environment/env-priority/.env.empty", "run", "--rm", "-e", "WHEREAMI",
			"env-compose-priority")
		assert.Equal(t, strings.TrimSpace(res.Stdout()), "Dockerfile")
	})

	t.Run("down", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "--project-directory", projectDir, "down")
	})
}

func TestEnvInterpolation(t *testing.T) {
	c := NewParallelCLI(t)

	projectDir := "./fixtures/environment/env-interpolation"

	//  No variable defined in the Compose file and env variable pass to the run command
	// 1. Compose file
	// 2. Shell environment variables <-- Result expected
	// 3. Environment file
	// 4. Dockerfile
	// 5. Variable is not defined
	t.Run("shell priority from run command", func(t *testing.T) {
		cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-interpolation/compose.yaml",
			"--project-directory", projectDir, "config")
		cmd.Env = append(cmd.Env, "WHEREAMI=shell")
		res := icmd.RunCmd(cmd)
		res.Assert(t, icmd.Expected{Out: `IMAGE: default_env:shell`})
	})
}

func TestCommentsInEnvFile(t *testing.T) {
	c := NewParallelCLI(t)

	projectDir := "./fixtures/environment/env-file-comments"

	t.Run("comments in env files", func(t *testing.T) {
		c.RunDockerOrExitError(t, "rmi", "env-file-comments")

		c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-file-comments/compose.yaml", "--project-directory",
			projectDir, "up", "-d", "--build")

		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-file-comments/compose.yaml",
			"--project-directory", projectDir, "run", "--rm", "-e", "COMMENT", "-e", "NO_COMMENT", "env-file-comments")

		res.Assert(t, icmd.Expected{Out: `COMMENT=1234`})
		res.Assert(t, icmd.Expected{Out: `NO_COMMENT=1234#5`})

		c.RunDockerComposeCmd(t, "--project-directory", projectDir, "down", "--rmi", "all")
	})
}
<|endoftext|>


<commit_before>package resolve

import (
	"github.com/Aptomi/aptomi/pkg/lang"
	"strings"
)

// componentInstanceKeySeparator is a separator between strings in ComponentInstanceKey
const componentInstanceKeySeparator = "#"

// componentUnresolvedName is placeholder for unresolved entries
const componentUnresolvedName = "unknown"

// componentRootName is a name of component for service entry (which in turn consists of components)
const componentRootName = "root"

// ComponentInstanceKey is a key for component instance. During policy resolution every component instance gets
// assigned a unique string key. It's important to form those keys correctly, so that we can make actual comparison
// of actual state (components with their keys) and desired state (components with their keys).
//
// Currently, component keys are formed from multiple parameters as follows.
// Cluster gets included as a part of the key (components running on different clusters must have different keys).
// Namespace gets included as a part of the key (components from different namespaces must have different keys).
// Contract, Context (with allocation keys), Service get included as a part of the key (Service must be within the same namespace as Contract).
// ComponentName gets included as a part of the key. For service-level component instances, ComponentName is
// set to componentRootName, while for all component instances within a service an actual Component.Name is used.
type ComponentInstanceKey struct {
	// cached version of component key
	key string

	// required fields
	ClusterName         string // mandatory
	Namespace           string // determined from the contract
	ContractName        string // mandatory
	ContextName         string // mandatory
	KeysResolved        string // mandatory
	ContextNameWithKeys string // calculated
	ServiceName         string // determined from the context (included into key for readability)
	ComponentName       string // component name
}

// NewComponentInstanceKey creates a new ComponentInstanceKey
func NewComponentInstanceKey(cluster *lang.Cluster, contract *lang.Contract, context *lang.Context, allocationKeysResolved []string, service *lang.Service, component *lang.ServiceComponent) *ComponentInstanceKey {
	contextName := getContextNameUnsafe(context)
	keysResolved := strings.Join(allocationKeysResolved, componentInstanceKeySeparator)
	contextNameWithKeys := strings.Join([]string{contextName, keysResolved}, componentInstanceKeySeparator)
	return &ComponentInstanceKey{
		ClusterName:         getClusterNameUnsafe(cluster),
		Namespace:           getContractNamespaceUnsafe(contract),
		ContractName:        getContractNameUnsafe(contract),
		ContextName:         contextName,
		KeysResolved:        keysResolved,
		ContextNameWithKeys: contextNameWithKeys,
		ServiceName:         getServiceNameUnsafe(service),
		ComponentName:       getComponentNameUnsafe(component),
	}
}

// MakeCopy creates a copy of ComponentInstanceKey
func (cik *ComponentInstanceKey) MakeCopy() *ComponentInstanceKey {
	return &ComponentInstanceKey{
		ClusterName:         cik.ClusterName,
		Namespace:           cik.Namespace,
		ContractName:        cik.ContractName,
		ContextName:         cik.ContextName,
		KeysResolved:        cik.KeysResolved,
		ContextNameWithKeys: cik.ContextNameWithKeys,
		ComponentName:       cik.ComponentName,
	}
}

// IsService returns 'true' if it's a contract instance key and we can't go up anymore. And it will return 'false' if it's a component instance key
func (cik *ComponentInstanceKey) IsService() bool {
	return cik.ComponentName == componentRootName
}

// IsComponent returns 'true' if it's a component instance key and we can go up to the corresponding service. And it will return 'false' if it's a service instance key
func (cik *ComponentInstanceKey) IsComponent() bool {
	return cik.ComponentName != componentRootName
}

// GetParentServiceKey returns a key for the parent service, replacing componentName with componentRootName
func (cik *ComponentInstanceKey) GetParentServiceKey() *ComponentInstanceKey {
	if cik.ComponentName == componentRootName {
		return cik
	}
	serviceCik := cik.MakeCopy()
	serviceCik.ComponentName = componentRootName
	return serviceCik
}

// GetKey returns a string key
func (cik ComponentInstanceKey) GetKey() string {
	if cik.key == "" {
		cik.key = strings.Join(
			[]string{
				cik.ClusterName,
				cik.Namespace,
				cik.ContractName,
				cik.ContextNameWithKeys,
				cik.ComponentName,
			}, componentInstanceKeySeparator)
	}
	return cik.key
}

// GetDeployName returns a string that could be used as name for deployment inside the cluster
func (cik ComponentInstanceKey) GetDeployName() string {
	return strings.Join(
		[]string{
			cik.Namespace,
			cik.ContractName,
			cik.ContextNameWithKeys,
			cik.ComponentName,
		}, componentInstanceKeySeparator)
}

// If cluster has not been resolved yet and we need a key, generate one
// Otherwise use cluster name
func getClusterNameUnsafe(cluster *lang.Cluster) string {
	if cluster == nil {
		return componentUnresolvedName
	}
	return cluster.Name
}

// If contract has not been resolved yet and we need a key, generate one
// Otherwise use contract name
func getContractNameUnsafe(contract *lang.Contract) string {
	if contract == nil {
		return componentUnresolvedName
	}
	return contract.Name
}

// If contract has not been resolved yet and we need a key, generate one
// Otherwise use contract namespace
func getContractNamespaceUnsafe(contract *lang.Contract) string {
	if contract == nil {
		return componentUnresolvedName
	}
	return contract.Namespace
}

// If context has not been resolved yet and we need a key, generate one
// Otherwise use context name
func getContextNameUnsafe(context *lang.Context) string {
	if context == nil {
		return componentUnresolvedName
	}
	return context.Name
}

// If service has not been resolved yet and we need a key, generate one
// Otherwise use service name
func getServiceNameUnsafe(service *lang.Service) string {
	if service == nil {
		return componentUnresolvedName
	}
	return service.Name
}

// If component has not been resolved yet and we need a key, generate one
// Otherwise use component name
func getComponentNameUnsafe(component *lang.ServiceComponent) string {
	if component == nil {
		return componentRootName
	}
	return component.Name
}
<commit_msg>fixed component key generation (do not add revolved keys if they are empty)<commit_after>package resolve

import (
	"github.com/Aptomi/aptomi/pkg/lang"
	"strings"
)

// componentInstanceKeySeparator is a separator between strings in ComponentInstanceKey
const componentInstanceKeySeparator = "#"

// componentUnresolvedName is placeholder for unresolved entries
const componentUnresolvedName = "unknown"

// componentRootName is a name of component for service entry (which in turn consists of components)
const componentRootName = "root"

// ComponentInstanceKey is a key for component instance. During policy resolution every component instance gets
// assigned a unique string key. It's important to form those keys correctly, so that we can make actual comparison
// of actual state (components with their keys) and desired state (components with their keys).
//
// Currently, component keys are formed from multiple parameters as follows.
// Cluster gets included as a part of the key (components running on different clusters must have different keys).
// Namespace gets included as a part of the key (components from different namespaces must have different keys).
// Contract, Context (with allocation keys), Service get included as a part of the key (Service must be within the same namespace as Contract).
// ComponentName gets included as a part of the key. For service-level component instances, ComponentName is
// set to componentRootName, while for all component instances within a service an actual Component.Name is used.
type ComponentInstanceKey struct {
	// cached version of component key
	key string

	// required fields
	ClusterName         string // mandatory
	Namespace           string // determined from the contract
	ContractName        string // mandatory
	ContextName         string // mandatory
	KeysResolved        string // mandatory
	ContextNameWithKeys string // calculated
	ServiceName         string // determined from the context (included into key for readability)
	ComponentName       string // component name
}

// NewComponentInstanceKey creates a new ComponentInstanceKey
func NewComponentInstanceKey(cluster *lang.Cluster, contract *lang.Contract, context *lang.Context, allocationKeysResolved []string, service *lang.Service, component *lang.ServiceComponent) *ComponentInstanceKey {
	contextName := getContextNameUnsafe(context)
	keysResolved := strings.Join(allocationKeysResolved, componentInstanceKeySeparator)
	contextNameWithKeys := contextName
	if len(keysResolved) > 0 {
		contextNameWithKeys = strings.Join([]string{contextNameWithKeys, keysResolved}, componentInstanceKeySeparator)
	}
	return &ComponentInstanceKey{
		ClusterName:         getClusterNameUnsafe(cluster),
		Namespace:           getContractNamespaceUnsafe(contract),
		ContractName:        getContractNameUnsafe(contract),
		ContextName:         contextName,
		KeysResolved:        keysResolved,
		ContextNameWithKeys: contextNameWithKeys,
		ServiceName:         getServiceNameUnsafe(service),
		ComponentName:       getComponentNameUnsafe(component),
	}
}

// MakeCopy creates a copy of ComponentInstanceKey
func (cik *ComponentInstanceKey) MakeCopy() *ComponentInstanceKey {
	return &ComponentInstanceKey{
		ClusterName:         cik.ClusterName,
		Namespace:           cik.Namespace,
		ContractName:        cik.ContractName,
		ContextName:         cik.ContextName,
		KeysResolved:        cik.KeysResolved,
		ContextNameWithKeys: cik.ContextNameWithKeys,
		ComponentName:       cik.ComponentName,
	}
}

// IsService returns 'true' if it's a contract instance key and we can't go up anymore. And it will return 'false' if it's a component instance key
func (cik *ComponentInstanceKey) IsService() bool {
	return cik.ComponentName == componentRootName
}

// IsComponent returns 'true' if it's a component instance key and we can go up to the corresponding service. And it will return 'false' if it's a service instance key
func (cik *ComponentInstanceKey) IsComponent() bool {
	return cik.ComponentName != componentRootName
}

// GetParentServiceKey returns a key for the parent service, replacing componentName with componentRootName
func (cik *ComponentInstanceKey) GetParentServiceKey() *ComponentInstanceKey {
	if cik.ComponentName == componentRootName {
		return cik
	}
	serviceCik := cik.MakeCopy()
	serviceCik.ComponentName = componentRootName
	return serviceCik
}

// GetKey returns a string key
func (cik ComponentInstanceKey) GetKey() string {
	if cik.key == "" {
		cik.key = strings.Join(
			[]string{
				cik.ClusterName,
				cik.Namespace,
				cik.ContractName,
				cik.ContextNameWithKeys,
				cik.ComponentName,
			}, componentInstanceKeySeparator)
	}
	return cik.key
}

// GetDeployName returns a string that could be used as name for deployment inside the cluster
func (cik ComponentInstanceKey) GetDeployName() string {
	return strings.Join(
		[]string{
			cik.Namespace,
			cik.ContractName,
			cik.ContextNameWithKeys,
			cik.ComponentName,
		}, componentInstanceKeySeparator)
}

// If cluster has not been resolved yet and we need a key, generate one
// Otherwise use cluster name
func getClusterNameUnsafe(cluster *lang.Cluster) string {
	if cluster == nil {
		return componentUnresolvedName
	}
	return cluster.Name
}

// If contract has not been resolved yet and we need a key, generate one
// Otherwise use contract name
func getContractNameUnsafe(contract *lang.Contract) string {
	if contract == nil {
		return componentUnresolvedName
	}
	return contract.Name
}

// If contract has not been resolved yet and we need a key, generate one
// Otherwise use contract namespace
func getContractNamespaceUnsafe(contract *lang.Contract) string {
	if contract == nil {
		return componentUnresolvedName
	}
	return contract.Namespace
}

// If context has not been resolved yet and we need a key, generate one
// Otherwise use context name
func getContextNameUnsafe(context *lang.Context) string {
	if context == nil {
		return componentUnresolvedName
	}
	return context.Name
}

// If service has not been resolved yet and we need a key, generate one
// Otherwise use service name
func getServiceNameUnsafe(service *lang.Service) string {
	if service == nil {
		return componentUnresolvedName
	}
	return service.Name
}

// If component has not been resolved yet and we need a key, generate one
// Otherwise use component name
func getComponentNameUnsafe(component *lang.ServiceComponent) string {
	if component == nil {
		return componentRootName
	}
	return component.Name
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Juniper Networks, Inc. All rights reserved.

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

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

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

package opencontrail

import (
	"fmt"
	"strings"

	"github.com/golang/glog"

	"github.com/Juniper/contrail-go-api"
	"github.com/Juniper/contrail-go-api/config"
	"github.com/Juniper/contrail-go-api/types"
)

type NetworkManager interface {
	LocateFloatingIpPool(network *types.VirtualNetwork, subnet string) (*types.FloatingIpPool, error)
	DeleteFloatingIpPool(network *types.VirtualNetwork, cascade bool) error
	LookupNetwork(projectName, networkName string) (*types.VirtualNetwork, error)
	LocateNetwork(project, name, subnet string) (*types.VirtualNetwork, error)
	DeleteNetwork(*types.VirtualNetwork) error
	ReleaseNetworkIfEmpty(namespace, name string) error
	LocateFloatingIp(network *types.VirtualNetwork, resourceName, address string) (*types.FloatingIp, error)
	GetPublicNetwork() *types.VirtualNetwork
	GetServiceNetwork() *types.VirtualNetwork
	GetGatewayAddress(network *types.VirtualNetwork) (string, error)
}

type NetworkManagerImpl struct {
	client        contrail.ApiClient
	config        *Config
	publicNetwork *types.VirtualNetwork
	serviceNetwork *types.VirtualNetwork
}

func NewNetworkManager(client contrail.ApiClient, config *Config) NetworkManager {
	manager := new(NetworkManagerImpl)
	manager.client = client
	manager.config = config
	manager.initializePublicNetwork()
	manager.initializeServiceNetwork()
	return manager
}

func (m *NetworkManagerImpl) GetServiceNetwork() *types.VirtualNetwork {
	return m.serviceNetwork
}

func (m *NetworkManagerImpl) GetPublicNetwork() *types.VirtualNetwork {
	return m.publicNetwork
}

func makePoolName(network *types.VirtualNetwork) string {
	fqn := make([]string, len(network.GetFQName()), len(network.GetFQName())+1)
	copy(fqn, network.GetFQName())
	fqn = append(fqn, fqn[len(fqn)-1])
	return strings.Join(fqn, ":")
}

func (m *NetworkManagerImpl) LocateFloatingIpPool(
	network *types.VirtualNetwork, subnet string) (*types.FloatingIpPool, error) {
	obj, err := m.client.FindByName(
		"floating-ip-pool", makePoolName(network))
	if err == nil {
		return obj.(*types.FloatingIpPool), nil
	}

	address, prefixlen := PrefixToAddressLen(subnet)

	pool := new(types.FloatingIpPool)
	pool.SetName(network.GetName())
	pool.SetParent(network)
	pool.SetFloatingIpPoolPrefixes(
		&types.FloatingIpPoolType{
			Subnet: []types.SubnetType{types.SubnetType{address, prefixlen}}})
	err = m.client.Create(pool)
	if err != nil {
		glog.Errorf("Create floating-ip-pool %s: %v", network.GetName(), err)
		return nil, err
	}
	return pool, nil
}

func (m *NetworkManagerImpl) floatingIpPoolDeleteChildren(pool *types.FloatingIpPool) error {
	fips, err := pool.GetFloatingIps()
	if err != nil {
		glog.Errorf("Get floating-ip-pool %s: %v", pool.GetName(), err)
		return err
	}
	for _, fip := range fips {
		err := m.client.DeleteByUuid("floating-ip", fip.Uuid)
		if err != nil {
			glog.Errorf("Delete floating-ip %s: %v", fip.Uuid, err)
		}
	}
	return nil
}
func (m *NetworkManagerImpl) DeleteFloatingIpPool(network *types.VirtualNetwork, cascade bool) error {
	obj, err := m.client.FindByName("floating-ip-pool", makePoolName(network))
	if err != nil {
		glog.Errorf("Get floating-ip-pool %s: %v", network.GetName(), err)
		return err
	}
	if cascade {
		pool := obj.(*types.FloatingIpPool)
		m.floatingIpPoolDeleteChildren(pool)
	}
	m.client.Delete(obj)
	return nil
}

func (m *NetworkManagerImpl) initializeServiceNetwork() {
	var network *types.VirtualNetwork
	obj, err := m.client.FindByName("virtual-network", m.config.ServiceNetwork)
	if err != nil {
		fqn := strings.Split(m.config.ServiceNetwork, ":")
		parent := strings.Join(fqn[0:len(fqn)-1], ":")
		projectId, err := m.client.UuidByName("project", parent)
		if err != nil {
			glog.Fatalf("%s: %v", parent, err)
		}
		var networkId string
		networkName := fqn[len(fqn)-1]
		if len(m.config.ServiceSubnet) > 0 {
			networkId, err = config.CreateNetworkWithSubnet(
				m.client, projectId, networkName, m.config.ServiceSubnet)
		} else {
			networkId, err = config.CreateNetwork(m.client, projectId, networkName)
		}
		if err != nil {
			glog.Fatalf("%s: %v", parent, err)
		}

		glog.Infof("Created network %s", m.config.ServiceNetwork)

		obj, err := m.client.FindByUuid("virtual-network", networkId)
		if err != nil {
			glog.Fatalf("GET %s %v", networkId, err)
		}
		network = obj.(*types.VirtualNetwork)
	} else {
		network = obj.(*types.VirtualNetwork)
	}

	m.serviceNetwork = network

	// TODO(prm): Ensure that the subnet is as specified.
	if len(m.config.ServiceSubnet) > 0 {
		m.LocateFloatingIpPool(network, m.config.ServiceSubnet)
	}
}

func (m *NetworkManagerImpl) initializePublicNetwork() {
	var network *types.VirtualNetwork
	obj, err := m.client.FindByName("virtual-network", m.config.PublicNetwork)
	if err != nil {
		fqn := strings.Split(m.config.PublicNetwork, ":")
		parent := strings.Join(fqn[0:len(fqn)-1], ":")
		projectId, err := m.client.UuidByName("project", parent)
		if err != nil {
			glog.Fatalf("%s: %v", parent, err)
		}
		var networkId string
		networkName := fqn[len(fqn)-1]
		if len(m.config.PublicSubnet) > 0 {
			networkId, err = config.CreateNetworkWithSubnet(
				m.client, projectId, networkName, m.config.PublicSubnet)
		} else {
			networkId, err = config.CreateNetwork(m.client, projectId, networkName)
		}
		if err != nil {
			glog.Fatalf("%s: %v", parent, err)
		}

		glog.Infof("Created network %s", m.config.PublicNetwork)

		obj, err := m.client.FindByUuid("virtual-network", networkId)
		if err != nil {
			glog.Fatalf("GET %s %v", networkId, err)
		}
		network = obj.(*types.VirtualNetwork)
	} else {
		network = obj.(*types.VirtualNetwork)
	}

	m.publicNetwork = network

	// TODO(prm): Ensure that the subnet is as specified.
	if len(m.config.PublicSubnet) > 0 {
		m.LocateFloatingIpPool(network, m.config.PublicSubnet)
	}
}

func (m *NetworkManagerImpl) LookupNetwork(projectName, networkName string) (*types.VirtualNetwork, error) {
	fqn := []string{DefaultDomain, projectName, networkName}
	obj, err := m.client.FindByName("virtual-network", strings.Join(fqn, ":"))
	if err != nil {
		glog.Errorf("GET virtual-network %s: %v", networkName, err)
		return nil, err
	}
	return obj.(*types.VirtualNetwork), nil
}

func (m *NetworkManagerImpl) LocateNetwork(project, name, subnet string) (*types.VirtualNetwork, error) {
	fqn := []string{DefaultDomain, project, name}
	fqname := strings.Join(fqn, ":")

	obj, err := m.client.FindByName("virtual-network", fqname)
	if err == nil {
		return obj.(*types.VirtualNetwork), nil
	}

	projectId, err := m.client.UuidByName("project", fmt.Sprintf("%s:%s", DefaultDomain, project))
	if err != nil {
		glog.Infof("GET %s: %v", project, err)
		return nil, err
	}
	uid, err := config.CreateNetworkWithSubnet(
		m.client, projectId, name, subnet)
	if err != nil {
		glog.Infof("Create %s: %v", name, err)
		return nil, err
	}
	obj, err = m.client.FindByUuid("virtual-network", uid)
	if err != nil {
		glog.Infof("GET %s: %v", name, err)
		return nil, err
	}
	glog.Infof("Create network %s", fqname)
	return obj.(*types.VirtualNetwork), nil
}

func (m *NetworkManagerImpl) ReleaseNetworkIfEmpty(namespace, name string) error {
	fqn := []string{DefaultDomain, namespace, name}
	obj, err := m.client.FindByName("virtual-network", strings.Join(fqn, ":"))
	if err != nil {
		glog.Errorf("Get virtual-network %s: %v", name, err)
		return err
	}
	network := obj.(*types.VirtualNetwork)
	refs, err := network.GetVirtualMachineInterfaceBackRefs()
	if err != nil {
		glog.Errorf("Get network vmi references %s: %v", name, err)
		return err
	}
	if len(refs) == 0 {
		err = m.client.Delete(network)
		if err != nil {
			glog.Errorf("Delete virtual-network %s: %v", name, err)
			return err
		}
	}
	return nil
}

func (m *NetworkManagerImpl) LocateFloatingIp(network *types.VirtualNetwork, resourceName, address string) (*types.FloatingIp, error) {
	obj, err := m.client.FindByName("floating-ip-pool", makePoolName(network))
	if err != nil {
		glog.Errorf("Get floating-ip-pool %s: %v", network.GetName(), err)
		return nil, err
	}
	pool := obj.(*types.FloatingIpPool)

	fqn := AppendConst(pool.GetFQName(), resourceName)
	obj, err = m.client.FindByName("floating-ip", strings.Join(fqn, ":"))
	if err == nil {
		fip := obj.(*types.FloatingIp)
		if fip.GetFloatingIpAddress() != address {
			fip.SetFloatingIpAddress(address)
			err = m.client.Update(fip)
			if err != nil {
				glog.Errorf("Update floating-ip %s: %v", resourceName, err)
				return nil, err
			}
		}
		return fip, nil
	}

	projectFQN := network.GetFQName()[0 : len(network.GetFQName())-1]
	obj, err = m.client.FindByName("project", strings.Join(projectFQN, ":"))
	if err != nil {
		glog.Errorf("Get project %s: %v", projectFQN[len(projectFQN)-1], err)
		return nil, err
	}
	project := obj.(*types.Project)

	fip := new(types.FloatingIp)
	fip.SetParent(pool)
	fip.SetName(resourceName)
	fip.SetFloatingIpAddress(address)
	fip.AddProject(project)
	err = m.client.Create(fip)
	if err != nil {
		glog.Errorf("Create floating-ip %s: %v", resourceName, err)
		return nil, err
	}
	return fip, nil
}

func (m *NetworkManagerImpl) GetGatewayAddress(network *types.VirtualNetwork) (string, error) {
	refs, err := network.GetNetworkIpamRefs()
	if err != nil {
		glog.Errorf("Get network %s network-ipam refs: %v", network.GetName(), err)
		return "", err
	}

	attr := refs[0].Attr.(types.VnSubnetsType)
	if len(attr.IpamSubnets) == 0 {
		glog.Errorf("Network %s has no subnets configured", network.GetName())
		return "", fmt.Errorf("Network %s: empty subnet list", network.GetName())
	}

	gateway := attr.IpamSubnets[0].DefaultGateway
	if gateway == "" {
		return "", fmt.Errorf("Gateway is empty: %+v", attr.IpamSubnets)
	}

	return gateway, nil
}

func (m *NetworkManagerImpl) DeleteNetwork(network *types.VirtualNetwork) error {
	refs, err := network.GetNetworkPolicyRefs()
	if err != nil {
		glog.Errorf("Get %s policy refs: %v", network.GetName(), err)
	}
	m.client.Delete(network)

	for _, ref := range refs {
		obj, err := m.client.FindByUuid("network-policy", ref.Uuid)
		if err != nil {
			glog.Errorf("Get policy %s: %v", ref.Uuid, err)
		}
		policy := obj.(*types.NetworkPolicy)
		npRefs, err := policy.GetVirtualNetworkBackRefs()
		if len(npRefs) == 0 {
			m.client.Delete(policy)
		}
	}
	return nil
}
<commit_msg>add some debugs<commit_after>/*
Copyright 2015 Juniper Networks, Inc. All rights reserved.

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

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

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

package opencontrail

import (
	"fmt"
	"strings"

	"github.com/golang/glog"

	"github.com/Juniper/contrail-go-api"
	"github.com/Juniper/contrail-go-api/config"
	"github.com/Juniper/contrail-go-api/types"
)

type NetworkManager interface {
	LocateFloatingIpPool(network *types.VirtualNetwork, subnet string) (*types.FloatingIpPool, error)
	DeleteFloatingIpPool(network *types.VirtualNetwork, cascade bool) error
	LookupNetwork(projectName, networkName string) (*types.VirtualNetwork, error)
	LocateNetwork(project, name, subnet string) (*types.VirtualNetwork, error)
	DeleteNetwork(*types.VirtualNetwork) error
	ReleaseNetworkIfEmpty(namespace, name string) error
	LocateFloatingIp(network *types.VirtualNetwork, resourceName, address string) (*types.FloatingIp, error)
	GetPublicNetwork() *types.VirtualNetwork
	GetServiceNetwork() *types.VirtualNetwork
	GetGatewayAddress(network *types.VirtualNetwork) (string, error)
}

type NetworkManagerImpl struct {
	client        contrail.ApiClient
	config        *Config
	publicNetwork *types.VirtualNetwork
	serviceNetwork *types.VirtualNetwork
}

func NewNetworkManager(client contrail.ApiClient, config *Config) NetworkManager {
	manager := new(NetworkManagerImpl)
	manager.client = client
	manager.config = config
	manager.initializePublicNetwork()
	manager.initializeServiceNetwork()
	return manager
}

func (m *NetworkManagerImpl) GetServiceNetwork() *types.VirtualNetwork {
	return m.serviceNetwork
}

func (m *NetworkManagerImpl) GetPublicNetwork() *types.VirtualNetwork {
	return m.publicNetwork
}

func makePoolName(network *types.VirtualNetwork) string {
	fqn := make([]string, len(network.GetFQName()), len(network.GetFQName())+1)
	copy(fqn, network.GetFQName())
	fqn = append(fqn, fqn[len(fqn)-1])
	return strings.Join(fqn, ":")
}

func (m *NetworkManagerImpl) LocateFloatingIpPool(
	network *types.VirtualNetwork, subnet string) (*types.FloatingIpPool, error) {
	obj, err := m.client.FindByName(
		"floating-ip-pool", makePoolName(network))
	if err == nil {
		return obj.(*types.FloatingIpPool), nil
	}

	address, prefixlen := PrefixToAddressLen(subnet)

	pool := new(types.FloatingIpPool)
	pool.SetName(network.GetName())
	pool.SetParent(network)
	pool.SetFloatingIpPoolPrefixes(
		&types.FloatingIpPoolType{
			Subnet: []types.SubnetType{types.SubnetType{address, prefixlen}}})
	err = m.client.Create(pool)
	if err != nil {
		glog.Errorf("Create floating-ip-pool %s: %v", network.GetName(), err)
		return nil, err
	}
	return pool, nil
}

func (m *NetworkManagerImpl) floatingIpPoolDeleteChildren(pool *types.FloatingIpPool) error {
	fips, err := pool.GetFloatingIps()
	if err != nil {
		glog.Errorf("Get floating-ip-pool %s: %v", pool.GetName(), err)
		return err
	}
	for _, fip := range fips {
		err := m.client.DeleteByUuid("floating-ip", fip.Uuid)
		if err != nil {
			glog.Errorf("Delete floating-ip %s: %v", fip.Uuid, err)
		}
	}
	return nil
}
func (m *NetworkManagerImpl) DeleteFloatingIpPool(network *types.VirtualNetwork, cascade bool) error {
	obj, err := m.client.FindByName("floating-ip-pool", makePoolName(network))
	if err != nil {
		glog.Errorf("Get floating-ip-pool %s: %v", network.GetName(), err)
		return err
	}
	if cascade {
		pool := obj.(*types.FloatingIpPool)
		m.floatingIpPoolDeleteChildren(pool)
	}
	m.client.Delete(obj)
	return nil
}

func (m *NetworkManagerImpl) initializeServiceNetwork() {
	var network *types.VirtualNetwork
	obj, err := m.client.FindByName("virtual-network", m.config.ServiceNetwork)
	if err != nil {
		fqn := strings.Split(m.config.ServiceNetwork, ":")
		parent := strings.Join(fqn[0:len(fqn)-1], ":")
		projectId, err := m.client.UuidByName("project", parent)
		if err != nil {
			glog.Fatalf("%s: %v", parent, err)
		}
		var networkId string
		networkName := fqn[len(fqn)-1]
		if len(m.config.ServiceSubnet) > 0 {
			networkId, err = config.CreateNetworkWithSubnet(
				m.client, projectId, networkName, m.config.ServiceSubnet)
		} else {
			networkId, err = config.CreateNetwork(m.client, projectId, networkName)
		}
		if err != nil {
			glog.Fatalf("%s: %v", parent, err)
		}

		glog.Infof("Created network %s", m.config.ServiceNetwork)

		obj, err := m.client.FindByUuid("virtual-network", networkId)
		if err != nil {
			glog.Fatalf("GET %s %v", networkId, err)
		}
		network = obj.(*types.VirtualNetwork)
	} else {
		network = obj.(*types.VirtualNetwork)
	}

	m.serviceNetwork = network

	// TODO(prm): Ensure that the subnet is as specified.
	if len(m.config.ServiceSubnet) > 0 {
		m.LocateFloatingIpPool(network, m.config.ServiceSubnet)
	}
}

func (m *NetworkManagerImpl) initializePublicNetwork() {
	var network *types.VirtualNetwork
	obj, err := m.client.FindByName("virtual-network", m.config.PublicNetwork)
	if err != nil {
		fqn := strings.Split(m.config.PublicNetwork, ":")
		parent := strings.Join(fqn[0:len(fqn)-1], ":")
		projectId, err := m.client.UuidByName("project", parent)
		if err != nil {
			glog.Fatalf("%s: %v", parent, err)
		}
		var networkId string
		networkName := fqn[len(fqn)-1]
		if len(m.config.PublicSubnet) > 0 {
			networkId, err = config.CreateNetworkWithSubnet(
				m.client, projectId, networkName, m.config.PublicSubnet)
		} else {
			networkId, err = config.CreateNetwork(m.client, projectId, networkName)
		}
		if err != nil {
			glog.Fatalf("%s: %v", parent, err)
		}

		glog.Infof("Created network %s", m.config.PublicNetwork)

		obj, err := m.client.FindByUuid("virtual-network", networkId)
		if err != nil {
			glog.Fatalf("GET %s %v", networkId, err)
		}
		network = obj.(*types.VirtualNetwork)
	} else {
		network = obj.(*types.VirtualNetwork)
	}

	m.publicNetwork = network

	// TODO(prm): Ensure that the subnet is as specified.
	if len(m.config.PublicSubnet) > 0 {
		m.LocateFloatingIpPool(network, m.config.PublicSubnet)
	}
}

func (m *NetworkManagerImpl) LookupNetwork(projectName, networkName string) (*types.VirtualNetwork, error) {
	fqn := []string{DefaultDomain, projectName, networkName}
	obj, err := m.client.FindByName("virtual-network", strings.Join(fqn, ":"))
	if err != nil {
		glog.Errorf("GET virtual-network %s: %v", networkName, err)
		return nil, err
	}
	return obj.(*types.VirtualNetwork), nil
}

func (m *NetworkManagerImpl) LocateNetwork(project, name, subnet string) (*types.VirtualNetwork, error) {
	fqn := []string{DefaultDomain, project, name}
	fqname := strings.Join(fqn, ":")

	obj, err := m.client.FindByName("virtual-network", fqname)
	if err == nil {
		return obj.(*types.VirtualNetwork), nil
	}

	projectId, err := m.client.UuidByName("project", fmt.Sprintf("%s:%s", DefaultDomain, project))
	if err != nil {
		glog.Infof("GET %s: %v", project, err)
		return nil, err
	}
	uid, err := config.CreateNetworkWithSubnet(
		m.client, projectId, name, subnet)
	if err != nil {
		glog.Infof("Create %s: %v", name, err)
		return nil, err
	}
	obj, err = m.client.FindByUuid("virtual-network", uid)
	if err != nil {
		glog.Infof("GET %s: %v", name, err)
		return nil, err
	}
	glog.Infof("Create network %s", fqname)
	return obj.(*types.VirtualNetwork), nil
}

func (m *NetworkManagerImpl) ReleaseNetworkIfEmpty(namespace, name string) error {
	fqn := []string{DefaultDomain, namespace, name}
	obj, err := m.client.FindByName("virtual-network", strings.Join(fqn, ":"))
	if err != nil {
		glog.Errorf("Get virtual-network %s: %v", name, err)
		return err
	}
	network := obj.(*types.VirtualNetwork)
	refs, err := network.GetVirtualMachineInterfaceBackRefs()
	if err != nil {
		glog.Errorf("Get network vmi references %s: %v", name, err)
		return err
	}
	if len(refs) == 0 {
		err = m.client.Delete(network)
		if err != nil {
			glog.Errorf("Delete virtual-network %s: %v", name, err)
			return err
		}
	}
	return nil
}

func (m *NetworkManagerImpl) LocateFloatingIp(network *types.VirtualNetwork, resourceName, address string) (*types.FloatingIp, error) {
	glog.Infof("LocateFloatingIp: %s in resource: %s", address, resourceName)
	obj, err := m.client.FindByName("floating-ip-pool", makePoolName(network))
	if err != nil {
		glog.Errorf("Get floating-ip-pool %s: %v", network.GetName(), err)
		return nil, err
	}
	pool := obj.(*types.FloatingIpPool)

	fqn := AppendConst(pool.GetFQName(), resourceName)
	obj, err = m.client.FindByName("floating-ip", strings.Join(fqn, ":"))
	if err == nil {
		fip := obj.(*types.FloatingIp)
		if fip.GetFloatingIpAddress() != address {
			fip.SetFloatingIpAddress(address)
			err = m.client.Update(fip)
			if err != nil {
				glog.Errorf("Update floating-ip %s: %v", resourceName, err)
				return nil, err
			}
		}
		return fip, nil
	}

	projectFQN := network.GetFQName()[0 : len(network.GetFQName())-1]
	obj, err = m.client.FindByName("project", strings.Join(projectFQN, ":"))
	if err != nil {
		glog.Errorf("Get project %s: %v", projectFQN[len(projectFQN)-1], err)
		return nil, err
	}
	project := obj.(*types.Project)

	fip := new(types.FloatingIp)
	fip.SetParent(pool)
	fip.SetName(resourceName)
	fip.SetFloatingIpAddress(address)
	fip.AddProject(project)
	err = m.client.Create(fip)
	if err != nil {
		glog.Errorf("Create floating-ip %s: %v", resourceName, err)
		return nil, err
	}
	return fip, nil
}

func (m *NetworkManagerImpl) GetGatewayAddress(network *types.VirtualNetwork) (string, error) {
	refs, err := network.GetNetworkIpamRefs()
	if err != nil {
		glog.Errorf("Get network %s network-ipam refs: %v", network.GetName(), err)
		return "", err
	}

	attr := refs[0].Attr.(types.VnSubnetsType)
	if len(attr.IpamSubnets) == 0 {
		glog.Errorf("Network %s has no subnets configured", network.GetName())
		return "", fmt.Errorf("Network %s: empty subnet list", network.GetName())
	}

	gateway := attr.IpamSubnets[0].DefaultGateway
	if gateway == "" {
		return "", fmt.Errorf("Gateway is empty: %+v", attr.IpamSubnets)
	}

	return gateway, nil
}

func (m *NetworkManagerImpl) DeleteNetwork(network *types.VirtualNetwork) error {
	refs, err := network.GetNetworkPolicyRefs()
	if err != nil {
		glog.Errorf("Get %s policy refs: %v", network.GetName(), err)
	}
	m.client.Delete(network)

	for _, ref := range refs {
		obj, err := m.client.FindByUuid("network-policy", ref.Uuid)
		if err != nil {
			glog.Errorf("Get policy %s: %v", ref.Uuid, err)
		}
		policy := obj.(*types.NetworkPolicy)
		npRefs, err := policy.GetVirtualNetworkBackRefs()
		if len(npRefs) == 0 {
			m.client.Delete(policy)
		}
	}
	return nil
}
<|endoftext|>