<commit_before>// gRPC server
package gserv

import (
	"bytes"
	"flag"
	"fmt"
	"hash"
	"io"
	"log"
	"net"
	"os"
	"runtime/pprof"
	"time"

	"google.golang.org/grpc"

	"github.com/glycerine/blake2b" // vendor https://github.com/dchest/blake2b"

	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"

	"github.com/glycerine/hnatsd/peer/api"
	pb "github.com/glycerine/hnatsd/peer/streambigfile"
)

type PeerServerClass struct {
	peer api.LocalGetSet
	cfg  *ServerConfig
}

func NewPeerServerClass(peer api.LocalGetSet, cfg *ServerConfig) *PeerServerClass {
	return &PeerServerClass{
		peer: peer,
		cfg:  cfg,
	}
}

// implement pb.PeerServer interface; the server is receiving a file here,
//  because the client called SendFile() on the other end.
//
func (s *PeerServerClass) SendFile(stream pb.Peer_SendFileServer) (err error) {
	p("peer.Server SendFile starting!")
	var chunkCount int64
	path := ""
	var hasher hash.Hash

	hasher, err = blake2b.New(nil)
	if err != nil {
		return err
	}

	var finalChecksum []byte
	var fd *os.File
	var bytesSeen int64

	defer func() {
		if fd != nil {
			fd.Close()
		}
		finalChecksum = []byte(hasher.Sum(nil))
		endTime := time.Now()

		p("this server.SendFile() call got %v chunks, byteCount=%v. with "+
			"final checksum '%x'. defer running/is returning with err='%v'",
			chunkCount, bytesSeen, finalChecksum, err)
		errStr := ""
		if err != nil {
			errStr = err.Error()
		}

		sacErr := stream.SendAndClose(&pb.BigFileAck{
			Filepath:         path,
			SizeInBytes:      bytesSeen,
			RecvTime:         uint64(endTime.UnixNano()),
			WholeFileBlake2B: finalChecksum,
			Err:              errStr,
		})
		if sacErr != nil {
			log.Printf("warning: sacErr='%s' in gserv server.go PeerServerClass.SendFile() attempt to stream.SendAndClose().", sacErr)
		}
	}()

	firstChunkSeen := false
	var allChunks bytes.Buffer
	var nk *pb.BigFileChunk

	for {
		nk, err = stream.Recv()
		if err == io.EOF {
			if nk != nil && len(nk.Data) > 0 {
				// we are assuming that this never happens!
				panic("we need to save this last chunk too!")
			}
			p("server doing stream.Recv(); sees err == "+
				"io.EOF. nk=%p. bytesSeen=%v. chunkCount=%v.",
				nk, bytesSeen, chunkCount)

			return nil
		}
		if err != nil {
			return err
		}

		// INVAR: we have a chunk
		if !firstChunkSeen {
			if nk.Filepath != "" {
				fd, err = os.Create(nk.Filepath + fmt.Sprintf("__%v", time.Now()))
				if err != nil {
					return err
				}
			}
			firstChunkSeen = true
		}

		hasher.Write(nk.Data)
		cumul := []byte(hasher.Sum(nil))
		if 0 != bytes.Compare(cumul, nk.Blake2BCumulative) {
			return fmt.Errorf("cumulative checksums failed at chunk %v of '%s'. Observed: '%x', expected: '%x'.", nk.ChunkNumber, nk.Filepath, cumul, nk.Blake2BCumulative)
		} else {
			p("cumulative checksum on nk.ChunkNumber=%v looks good; cumul='%x'.  nk.IsLastChunk=%v", nk.ChunkNumber, nk.Blake2BCumulative, nk.IsLastChunk)
		}
		if path == "" {
			path = nk.Filepath
			p("peer.Server SendFile sees new file '%s'", path)
		}
		if path != "" && path != nk.Filepath {
			panic(fmt.Errorf("confusing between two different streams! '%s' vs '%s'", path, nk.Filepath))
		}

		if nk.SizeInBytes != int64(len(nk.Data)) {
			return fmt.Errorf("%v == nk.SizeInBytes != int64(len(nk.Data)) == %v", nk.SizeInBytes, int64(len(nk.Data)))
		}

		checksum := blake2bOfBytes(nk.Data)
		cmp := bytes.Compare(checksum, nk.Blake2B)
		if cmp != 0 {
			return fmt.Errorf("chunk %v bad .Data, checksum mismatch!",
				nk.ChunkNumber)
		}

		// INVAR: chunk passes tests, keep it.
		bytesSeen += int64(len(nk.Data))
		chunkCount++
		var nw int

		nw, err = allChunks.Write(nk.Data)
		panicOn(err)
		if nw != len(nk.Data) {
			panic("short write to bytes.Buffer")
		}

		if nk.IsLastChunk {
			toWrite := allChunks.Bytes()
			err = s.peer.LocalSet(&api.KeyInv{Key: []byte("chk"), Val: toWrite, When: time.Unix(0, int64(nk.SendTime))})
			p("server sees the last chunk of '%s', writing to bolt %v bytes gave '%v', and returning now.", nk.Filepath, len(toWrite), err)

			var reply api.BcastGetReply
			_, err = reply.UnmarshalMsg(toWrite)
			if err != nil {
				return fmt.Errorf("reply.UnmarshalMsg() errored '%v'", err)
			}

			select {
			case s.cfg.ServerGotReply <- &reply:
				p("gserv server.go sent reply on s.cfg.ServerGotReply; reply='%s'/'%#v'")
			case <-s.cfg.Halt.ReqStop.Chan:
			}

			return err
		}

	} // end for
	return nil
}

const ProgramName = "gprcServer"

func MainExample() {

	myflags := flag.NewFlagSet(ProgramName, flag.ExitOnError)
	cfg := NewServerConfig()
	cfg.DefineFlags(myflags)

	sshegoCfg := setupSshFlags(myflags)

	err := myflags.Parse(os.Args[1:])

	if cfg.CpuProfilePath != "" {
		f, err := os.Create(cfg.CpuProfilePath)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	err = cfg.ValidateConfig()
	if err != nil {
		log.Fatalf("%s command line flag error: '%s'", ProgramName, err)
	}
	cfg.SshegoCfg = sshegoCfg
	//	cfg.StartGrpcServer()
}

func (cfg *ServerConfig) Stop() {
	if cfg != nil && cfg.GrpcServer != nil {
		cfg.GrpcServer.Stop()
	}
}

func (cfg *ServerConfig) StartGrpcServer(peer api.LocalGetSet, sshdReady chan bool) {

	var gRpcBindPort int
	var gRpcHost string
	if cfg.UseTLS {
		// use TLS
		gRpcBindPort = cfg.ExternalLsnPort
		gRpcHost = cfg.Host

		p("gRPC with TLS listening on %v:%v", gRpcHost, gRpcBindPort)

	} else {
		// SSH will take the external, gRPC will take the internal.
		gRpcBindPort = cfg.InternalLsnPort
		gRpcHost = "127.0.0.1" // local only, behind the SSHD

		p("external SSHd listening on %v:%v, internal gRPC service listening on 127.0.0.1:%v", cfg.Host, cfg.ExternalLsnPort, cfg.InternalLsnPort)

	}

	lis, err := net.Listen("tcp", fmt.Sprintf("%v:%d", gRpcHost, gRpcBindPort))
	if err != nil {
		grpclog.Fatalf("failed to listen: %v", err)
	}

	var opts []grpc.ServerOption

	if cfg.UseTLS {
		// use TLS
		creds, err := credentials.NewServerTLSFromFile(cfg.CertPath, cfg.KeyPath)
		if err != nil {
			grpclog.Fatalf("Failed to generate credentials %v", err)
		}
		opts = []grpc.ServerOption{grpc.Creds(creds)}
	} else {
		// use SSH
		err = serverSshMain(cfg.SshegoCfg, cfg.Host,
			cfg.ExternalLsnPort, cfg.InternalLsnPort)
		panicOn(err)
		close(sshdReady)
	}

	cfg.GrpcServer = grpc.NewServer(opts...)
	pb.RegisterPeerServer(cfg.GrpcServer, NewPeerServerClass(peer, cfg))

	// blocks until shutdown
	cfg.GrpcServer.Serve(lis)
}

func blake2bOfBytes(by []byte) []byte {
	h, err := blake2b.New(nil)
	panicOn(err)
	h.Write(by)
	return []byte(h.Sum(nil))
}
<commit_msg>green 103, still polishing on it<commit_after>// gRPC server
package gserv

import (
	"bytes"
	"flag"
	"fmt"
	"hash"
	"io"
	"log"
	"net"
	"os"
	"runtime/pprof"
	"time"

	"google.golang.org/grpc"

	"github.com/glycerine/blake2b" // vendor https://github.com/dchest/blake2b"

	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"

	"github.com/glycerine/hnatsd/peer/api"
	pb "github.com/glycerine/hnatsd/peer/streambigfile"
)

type PeerServerClass struct {
	peer api.LocalGetSet
	cfg  *ServerConfig
}

func NewPeerServerClass(peer api.LocalGetSet, cfg *ServerConfig) *PeerServerClass {
	return &PeerServerClass{
		peer: peer,
		cfg:  cfg,
	}
}

// implement pb.PeerServer interface; the server is receiving a file here,
//  because the client called SendFile() on the other end.
//
func (s *PeerServerClass) SendFile(stream pb.Peer_SendFileServer) (err error) {
	p("peer.Server SendFile starting!")
	var chunkCount int64
	path := ""
	var hasher hash.Hash

	hasher, err = blake2b.New(nil)
	if err != nil {
		return err
	}

	var finalChecksum []byte
	var fd *os.File
	var bytesSeen int64

	defer func() {
		if fd != nil {
			fd.Close()
		}
		finalChecksum = []byte(hasher.Sum(nil))
		endTime := time.Now()

		p("this server.SendFile() call got %v chunks, byteCount=%v. with "+
			"final checksum '%x'. defer running/is returning with err='%v'",
			chunkCount, bytesSeen, finalChecksum, err)
		errStr := ""
		if err != nil {
			errStr = err.Error()
		}

		sacErr := stream.SendAndClose(&pb.BigFileAck{
			Filepath:         path,
			SizeInBytes:      bytesSeen,
			RecvTime:         uint64(endTime.UnixNano()),
			WholeFileBlake2B: finalChecksum,
			Err:              errStr,
		})
		if sacErr != nil {
			log.Printf("warning: sacErr='%s' in gserv server.go PeerServerClass.SendFile() attempt to stream.SendAndClose().", sacErr)
		}
	}()

	firstChunkSeen := false
	var allChunks bytes.Buffer
	var nk *pb.BigFileChunk

	for {
		nk, err = stream.Recv()
		if err == io.EOF {
			if nk != nil && len(nk.Data) > 0 {
				// we are assuming that this never happens!
				panic("we need to save this last chunk too!")
			}
			p("server doing stream.Recv(); sees err == "+
				"io.EOF. nk=%p. bytesSeen=%v. chunkCount=%v.",
				nk, bytesSeen, chunkCount)

			return nil
		}
		if err != nil {
			return err
		}

		// INVAR: we have a chunk
		if !firstChunkSeen {
			if nk.Filepath != "" {
				fd, err = os.Create(nk.Filepath + fmt.Sprintf("__%v", time.Now()))
				if err != nil {
					return err
				}
			}
			firstChunkSeen = true
		}

		hasher.Write(nk.Data)
		cumul := []byte(hasher.Sum(nil))
		if 0 != bytes.Compare(cumul, nk.Blake2BCumulative) {
			return fmt.Errorf("cumulative checksums failed at chunk %v of '%s'. Observed: '%x', expected: '%x'.", nk.ChunkNumber, nk.Filepath, cumul, nk.Blake2BCumulative)
		} else {
			p("cumulative checksum on nk.ChunkNumber=%v looks good; cumul='%x'.  nk.IsLastChunk=%v", nk.ChunkNumber, nk.Blake2BCumulative, nk.IsLastChunk)
		}
		if path == "" {
			path = nk.Filepath
			p("peer.Server SendFile sees new file '%s'", path)
		}
		if path != "" && path != nk.Filepath {
			panic(fmt.Errorf("confusing between two different streams! '%s' vs '%s'", path, nk.Filepath))
		}

		if nk.SizeInBytes != int64(len(nk.Data)) {
			return fmt.Errorf("%v == nk.SizeInBytes != int64(len(nk.Data)) == %v", nk.SizeInBytes, int64(len(nk.Data)))
		}

		checksum := blake2bOfBytes(nk.Data)
		cmp := bytes.Compare(checksum, nk.Blake2B)
		if cmp != 0 {
			return fmt.Errorf("chunk %v bad .Data, checksum mismatch!",
				nk.ChunkNumber)
		}

		// INVAR: chunk passes tests, keep it.
		bytesSeen += int64(len(nk.Data))
		chunkCount++
		var nw int

		nw, err = allChunks.Write(nk.Data)
		panicOn(err)
		if nw != len(nk.Data) {
			panic("short write to bytes.Buffer")
		}

		if nk.IsLastChunk {
			toWrite := allChunks.Bytes()
			err = s.peer.LocalSet(&api.KeyInv{Key: []byte("chk"), Val: toWrite, When: time.Unix(0, int64(nk.SendTime))})
			p("server sees the last chunk of '%s', writing to bolt %v bytes gave '%v', and returning now.", nk.Filepath, len(toWrite), err)

			var reply api.BcastGetReply
			_, err = reply.UnmarshalMsg(toWrite)
			if err != nil {
				return fmt.Errorf("reply.UnmarshalMsg() errored '%v'", err)
			}

			select {
			case s.cfg.ServerGotReply <- &reply:
				p("gserv server.go sent reply on s.cfg.ServerGotReply; reply='%s'/'%#v'", reply, reply)
			case <-s.cfg.Halt.ReqStop.Chan:
			}

			return err
		}

	} // end for
	return nil
}

const ProgramName = "gprcServer"

func MainExample() {

	myflags := flag.NewFlagSet(ProgramName, flag.ExitOnError)
	cfg := NewServerConfig()
	cfg.DefineFlags(myflags)

	sshegoCfg := setupSshFlags(myflags)

	err := myflags.Parse(os.Args[1:])

	if cfg.CpuProfilePath != "" {
		f, err := os.Create(cfg.CpuProfilePath)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	err = cfg.ValidateConfig()
	if err != nil {
		log.Fatalf("%s command line flag error: '%s'", ProgramName, err)
	}
	cfg.SshegoCfg = sshegoCfg
	//	cfg.StartGrpcServer()
}

func (cfg *ServerConfig) Stop() {
	if cfg != nil && cfg.GrpcServer != nil {
		cfg.GrpcServer.Stop()
	}
}

func (cfg *ServerConfig) StartGrpcServer(peer api.LocalGetSet, sshdReady chan bool) {

	var gRpcBindPort int
	var gRpcHost string
	if cfg.UseTLS {
		// use TLS
		gRpcBindPort = cfg.ExternalLsnPort
		gRpcHost = cfg.Host

		p("gRPC with TLS listening on %v:%v", gRpcHost, gRpcBindPort)

	} else {
		// SSH will take the external, gRPC will take the internal.
		gRpcBindPort = cfg.InternalLsnPort
		gRpcHost = "127.0.0.1" // local only, behind the SSHD

		p("external SSHd listening on %v:%v, internal gRPC service listening on 127.0.0.1:%v", cfg.Host, cfg.ExternalLsnPort, cfg.InternalLsnPort)

	}

	lis, err := net.Listen("tcp", fmt.Sprintf("%v:%d", gRpcHost, gRpcBindPort))
	if err != nil {
		grpclog.Fatalf("failed to listen: %v", err)
	}

	var opts []grpc.ServerOption

	if cfg.UseTLS {
		// use TLS
		creds, err := credentials.NewServerTLSFromFile(cfg.CertPath, cfg.KeyPath)
		if err != nil {
			grpclog.Fatalf("Failed to generate credentials %v", err)
		}
		opts = []grpc.ServerOption{grpc.Creds(creds)}
	} else {
		// use SSH
		err = serverSshMain(cfg.SshegoCfg, cfg.Host,
			cfg.ExternalLsnPort, cfg.InternalLsnPort)
		panicOn(err)
		close(sshdReady)
	}

	cfg.GrpcServer = grpc.NewServer(opts...)
	pb.RegisterPeerServer(cfg.GrpcServer, NewPeerServerClass(peer, cfg))

	// blocks until shutdown
	cfg.GrpcServer.Serve(lis)
}

func blake2bOfBytes(by []byte) []byte {
	h, err := blake2b.New(nil)
	panicOn(err)
	h.Write(by)
	return []byte(h.Sum(nil))
}
<|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 (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	testify "github.com/stretchr/testify/assert"
	"gotest.tools/v3/assert"
	"gotest.tools/v3/icmd"
)

func TestLocalComposeUp(t *testing.T) {
	// this test shares a fixture with TestCompatibility and can't run at the same time
	c := NewCLI(t)

	const projectName = "compose-e2e-demo"

	t.Run("up", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "-f", "./fixtures/sentences/compose.yaml", "--project-name", projectName, "up", "-d")
	})

	t.Run("check accessing running app", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-p", projectName, "ps")
		res.Assert(t, icmd.Expected{Out: `web`})

		endpoint := "http://localhost:90"
		output := HTTPGetWithRetry(t, endpoint+"/words/noun", http.StatusOK, 2*time.Second, 20*time.Second)
		assert.Assert(t, strings.Contains(output, `"word":`))

		res = c.RunDockerCmd(t, "network", "ls")
		res.Assert(t, icmd.Expected{Out: projectName + "_default"})
	})

	t.Run("top", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-p", projectName, "top")
		output := res.Stdout()
		head := []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"}
		for _, h := range head {
			assert.Assert(t, strings.Contains(output, h), output)
		}
		assert.Assert(t, strings.Contains(output, `java -Xmx8m -Xms8m -jar /app/words.jar`), output)
		assert.Assert(t, strings.Contains(output, `/dispatcher`), output)
	})

	t.Run("check compose labels", func(t *testing.T) {
		res := c.RunDockerCmd(t, "inspect", projectName+"-web-1")
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.container-number": "1"`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": "compose-e2e-demo"`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.oneoff": "False",`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.config-hash":`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project.config_files":`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project.working_dir":`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.service": "web"`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.version":`})

		res = c.RunDockerCmd(t, "network", "inspect", projectName+"_default")
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.network": "default"`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": `})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.version": `})
	})

	t.Run("check user labels", func(t *testing.T) {
		res := c.RunDockerCmd(t, "inspect", projectName+"-web-1")
		res.Assert(t, icmd.Expected{Out: `"my-label": "test"`})

	})

	t.Run("check healthcheck output", func(t *testing.T) {
		c.WaitForCmdResult(t, c.NewDockerComposeCmd(t, "-p", projectName, "ps", "--format", "json"),
			StdoutContains(`"Name":"compose-e2e-demo-web-1","Command":"/dispatcher","Project":"compose-e2e-demo","Service":"web","State":"running","Health":"healthy"`),
			5*time.Second, 1*time.Second)

		res := c.RunDockerComposeCmd(t, "-p", projectName, "ps")
		res.Assert(t, icmd.Expected{Out: `NAME                       COMMAND                  SERVICE             STATUS              PORTS`})
		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-web-1     "/dispatcher"            web                 running (healthy)   0.0.0.0:90->80/tcp`})
		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-db-1      "docker-entrypoint.s…"   db                  running             5432/tcp`})
	})

	t.Run("images", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-p", projectName, "images")
		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-db-1      gtardif/sentences-db    latest`})
		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-web-1     gtardif/sentences-web   latest`})
		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-words-1   gtardif/sentences-api   latest`})
	})

	t.Run("down", func(t *testing.T) {
		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
	})

	t.Run("check containers after down", func(t *testing.T) {
		res := c.RunDockerCmd(t, "ps", "--all")
		assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined())
	})

	t.Run("check networks after down", func(t *testing.T) {
		res := c.RunDockerCmd(t, "network", "ls")
		assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined())
	})
}

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

	t.Run("Verify image pulled", func(t *testing.T) {
		// cleanup existing images
		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/simple", "down", "--rmi", "all")

		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/simple", "pull")
		output := res.Combined()

		assert.Assert(t, strings.Contains(output, "simple Pulled"))
		assert.Assert(t, strings.Contains(output, "another Pulled"))
	})

	t.Run("Verify a image is pulled once", func(t *testing.T) {
		// cleanup existing images
		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/duplicate-images", "down", "--rmi", "all")

		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/duplicate-images", "pull")
		output := res.Combined()

		if strings.Contains(output, "another Pulled") {
			assert.Assert(t, strings.Contains(output, "another Pulled"))
			assert.Assert(t, strings.Contains(output, "Skipped - Image is already being pulled by another"))
		} else {
			assert.Assert(t, strings.Contains(output, "simple Pulled"))
			assert.Assert(t, strings.Contains(output, "Skipped - Image is already being pulled by simple"))
		}
	})

	t.Run("Verify skipped pull if image is already present locally", func(t *testing.T) {
		// make sure the requied image is present
		c.RunDockerCmd(t, "pull", "alpine")

		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/image-present-locally", "pull")
		output := res.Combined()

		assert.Assert(t, strings.Contains(output, "Skipped - Image is already present locally"))
	})

	t.Run("Verify skipped no image to be pulled", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/no-image-name-given", "pull")
		output := res.Combined()

		assert.Assert(t, strings.Contains(output, "Skipped - No image to be pulled"))
	})
}

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

	tmpFolder, err := os.MkdirTemp("fixtures/simple-composefile", "test-tmp")
	assert.NilError(t, err)
	defer os.Remove(tmpFolder) // nolint: errcheck
	projectName := filepath.Base(tmpFolder)

	res := c.RunDockerComposeCmd(t, "--project-directory", tmpFolder, "up", "-d")
	res.Assert(t, icmd.Expected{Err: "Started", ExitCode: 0})

	res = c.RunDockerComposeCmd(t, "-p", projectName, "down")
	res.Assert(t, icmd.Expected{Err: "Removed", ExitCode: 0})
}

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

	cmd := c.NewDockerComposeCmd(t, "--ansi=never", "--project-directory", "./fixtures/attach-restart", "up")
	res := icmd.StartCmd(cmd)
	defer c.RunDockerComposeCmd(t, "-p", "attach-restart", "down")

	c.WaitForCondition(t, func() (bool, string) {
		debug := res.Combined()
		return strings.Count(res.Stdout(),
				"failing-1 exited with code 1") == 3, fmt.Sprintf("'failing-1 exited with code 1' not found 3 times in : \n%s\n",
				debug)
	}, 2*time.Minute, 2*time.Second)

	assert.Equal(t, strings.Count(res.Stdout(), "failing-1  | world"), 3, res.Combined())
}

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

	res := c.RunDockerComposeCmd(t, "--ansi=never", "--project-directory", "./fixtures/init-container", "up")
	defer c.RunDockerComposeCmd(t, "-p", "init-container", "down")
	testify.Regexp(t, "foo-1  | hello(?m:.*)bar-1  | world", res.Stdout())
}

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

	const projectName = "compose-e2e-rm"

	t.Run("up", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "up", "-d")
	})

	t.Run("rm -sf", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm",
			"-sf", "simple")
		res.Assert(t, icmd.Expected{Err: "Removed", ExitCode: 0})
	})

	t.Run("check containers after rm -sf", func(t *testing.T) {
		res := c.RunDockerCmd(t, "ps", "--all")
		assert.Assert(t, !strings.Contains(res.Combined(), projectName+"_simple"), res.Combined())
	})

	t.Run("rm -sf <none>", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm",
			"-sf", "simple")
		res.Assert(t, icmd.Expected{ExitCode: 0})
	})

	t.Run("down", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "-p", projectName, "down")
	})
}

func TestCompatibility(t *testing.T) {
	// this test shares a fixture with TestLocalComposeUp and can't run at the same time
	c := NewCLI(t)

	const projectName = "compose-e2e-compatibility"

	t.Run("up", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "--compatibility", "-f", "./fixtures/sentences/compose.yaml", "--project-name",
			projectName, "up", "-d")
	})

	t.Run("check container names", func(t *testing.T) {
		res := c.RunDockerCmd(t, "ps", "--format", "{{.Names}}")
		res.Assert(t, icmd.Expected{Out: "compose-e2e-compatibility_web_1"})
		res.Assert(t, icmd.Expected{Out: "compose-e2e-compatibility_words_1"})
		res.Assert(t, icmd.Expected{Out: "compose-e2e-compatibility_db_1"})
	})

	t.Run("down", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "-p", projectName, "down")
	})
}

func TestConvert(t *testing.T) {
	const projectName = "compose-e2e-convert"
	c := NewParallelCLI(t)

	wd, err := os.Getwd()
	assert.NilError(t, err)

	t.Run("up", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-build-test/compose.yaml", "-p", projectName, "convert")
		res.Assert(t, icmd.Expected{Out: fmt.Sprintf(`services:
  nginx:
    build:
      context: %s
      dockerfile: Dockerfile
    networks:
      default: null
networks:
  default:
    name: compose-e2e-convert_default`, filepath.Join(wd, "fixtures", "simple-build-test", "nginx-build")), ExitCode: 0})
	})
}
<commit_msg>Fix package name<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 (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	testify "github.com/stretchr/testify/assert"
	"gotest.tools/v3/assert"
	"gotest.tools/v3/icmd"
)

func TestLocalComposeUp(t *testing.T) {
	// this test shares a fixture with TestCompatibility and can't run at the same time
	c := NewCLI(t)

	const projectName = "compose-e2e-demo"

	t.Run("up", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "-f", "./fixtures/sentences/compose.yaml", "--project-name", projectName, "up", "-d")
	})

	t.Run("check accessing running app", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-p", projectName, "ps")
		res.Assert(t, icmd.Expected{Out: `web`})

		endpoint := "http://localhost:90"
		output := HTTPGetWithRetry(t, endpoint+"/words/noun", http.StatusOK, 2*time.Second, 20*time.Second)
		assert.Assert(t, strings.Contains(output, `"word":`))

		res = c.RunDockerCmd(t, "network", "ls")
		res.Assert(t, icmd.Expected{Out: projectName + "_default"})
	})

	t.Run("top", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-p", projectName, "top")
		output := res.Stdout()
		head := []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"}
		for _, h := range head {
			assert.Assert(t, strings.Contains(output, h), output)
		}
		assert.Assert(t, strings.Contains(output, `java -Xmx8m -Xms8m -jar /app/words.jar`), output)
		assert.Assert(t, strings.Contains(output, `/dispatcher`), output)
	})

	t.Run("check compose labels", func(t *testing.T) {
		res := c.RunDockerCmd(t, "inspect", projectName+"-web-1")
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.container-number": "1"`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": "compose-e2e-demo"`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.oneoff": "False",`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.config-hash":`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project.config_files":`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project.working_dir":`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.service": "web"`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.version":`})

		res = c.RunDockerCmd(t, "network", "inspect", projectName+"_default")
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.network": "default"`})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": `})
		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.version": `})
	})

	t.Run("check user labels", func(t *testing.T) {
		res := c.RunDockerCmd(t, "inspect", projectName+"-web-1")
		res.Assert(t, icmd.Expected{Out: `"my-label": "test"`})

	})

	t.Run("check healthcheck output", func(t *testing.T) {
		c.WaitForCmdResult(t, c.NewDockerComposeCmd(t, "-p", projectName, "ps", "--format", "json"),
			StdoutContains(`"Name":"compose-e2e-demo-web-1","Command":"/dispatcher","Project":"compose-e2e-demo","Service":"web","State":"running","Health":"healthy"`),
			5*time.Second, 1*time.Second)

		res := c.RunDockerComposeCmd(t, "-p", projectName, "ps")
		res.Assert(t, icmd.Expected{Out: `NAME                       COMMAND                  SERVICE             STATUS              PORTS`})
		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-web-1     "/dispatcher"            web                 running (healthy)   0.0.0.0:90->80/tcp`})
		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-db-1      "docker-entrypoint.s…"   db                  running             5432/tcp`})
	})

	t.Run("images", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-p", projectName, "images")
		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-db-1      gtardif/sentences-db    latest`})
		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-web-1     gtardif/sentences-web   latest`})
		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-words-1   gtardif/sentences-api   latest`})
	})

	t.Run("down", func(t *testing.T) {
		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
	})

	t.Run("check containers after down", func(t *testing.T) {
		res := c.RunDockerCmd(t, "ps", "--all")
		assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined())
	})

	t.Run("check networks after down", func(t *testing.T) {
		res := c.RunDockerCmd(t, "network", "ls")
		assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined())
	})
}

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

	t.Run("Verify image pulled", func(t *testing.T) {
		// cleanup existing images
		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/simple", "down", "--rmi", "all")

		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/simple", "pull")
		output := res.Combined()

		assert.Assert(t, strings.Contains(output, "simple Pulled"))
		assert.Assert(t, strings.Contains(output, "another Pulled"))
	})

	t.Run("Verify a image is pulled once", func(t *testing.T) {
		// cleanup existing images
		c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/duplicate-images", "down", "--rmi", "all")

		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/duplicate-images", "pull")
		output := res.Combined()

		if strings.Contains(output, "another Pulled") {
			assert.Assert(t, strings.Contains(output, "another Pulled"))
			assert.Assert(t, strings.Contains(output, "Skipped - Image is already being pulled by another"))
		} else {
			assert.Assert(t, strings.Contains(output, "simple Pulled"))
			assert.Assert(t, strings.Contains(output, "Skipped - Image is already being pulled by simple"))
		}
	})

	t.Run("Verify skipped pull if image is already present locally", func(t *testing.T) {
		// make sure the requied image is present
		c.RunDockerCmd(t, "pull", "alpine:3.13")

		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/image-present-locally", "pull")
		output := res.Combined()

		assert.Assert(t, strings.Contains(output, "Skipped - Image is already present locally"))
	})

	t.Run("Verify skipped no image to be pulled", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/no-image-name-given", "pull")
		output := res.Combined()

		assert.Assert(t, strings.Contains(output, "Skipped - No image to be pulled"))
	})
}

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

	tmpFolder, err := os.MkdirTemp("fixtures/simple-composefile", "test-tmp")
	assert.NilError(t, err)
	defer os.Remove(tmpFolder) // nolint: errcheck
	projectName := filepath.Base(tmpFolder)

	res := c.RunDockerComposeCmd(t, "--project-directory", tmpFolder, "up", "-d")
	res.Assert(t, icmd.Expected{Err: "Started", ExitCode: 0})

	res = c.RunDockerComposeCmd(t, "-p", projectName, "down")
	res.Assert(t, icmd.Expected{Err: "Removed", ExitCode: 0})
}

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

	cmd := c.NewDockerComposeCmd(t, "--ansi=never", "--project-directory", "./fixtures/attach-restart", "up")
	res := icmd.StartCmd(cmd)
	defer c.RunDockerComposeCmd(t, "-p", "attach-restart", "down")

	c.WaitForCondition(t, func() (bool, string) {
		debug := res.Combined()
		return strings.Count(res.Stdout(),
				"failing-1 exited with code 1") == 3, fmt.Sprintf("'failing-1 exited with code 1' not found 3 times in : \n%s\n",
				debug)
	}, 2*time.Minute, 2*time.Second)

	assert.Equal(t, strings.Count(res.Stdout(), "failing-1  | world"), 3, res.Combined())
}

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

	res := c.RunDockerComposeCmd(t, "--ansi=never", "--project-directory", "./fixtures/init-container", "up")
	defer c.RunDockerComposeCmd(t, "-p", "init-container", "down")
	testify.Regexp(t, "foo-1  | hello(?m:.*)bar-1  | world", res.Stdout())
}

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

	const projectName = "compose-e2e-rm"

	t.Run("up", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "up", "-d")
	})

	t.Run("rm -sf", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm",
			"-sf", "simple")
		res.Assert(t, icmd.Expected{Err: "Removed", ExitCode: 0})
	})

	t.Run("check containers after rm -sf", func(t *testing.T) {
		res := c.RunDockerCmd(t, "ps", "--all")
		assert.Assert(t, !strings.Contains(res.Combined(), projectName+"_simple"), res.Combined())
	})

	t.Run("rm -sf <none>", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm",
			"-sf", "simple")
		res.Assert(t, icmd.Expected{ExitCode: 0})
	})

	t.Run("down", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "-p", projectName, "down")
	})
}

func TestCompatibility(t *testing.T) {
	// this test shares a fixture with TestLocalComposeUp and can't run at the same time
	c := NewCLI(t)

	const projectName = "compose-e2e-compatibility"

	t.Run("up", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "--compatibility", "-f", "./fixtures/sentences/compose.yaml", "--project-name",
			projectName, "up", "-d")
	})

	t.Run("check container names", func(t *testing.T) {
		res := c.RunDockerCmd(t, "ps", "--format", "{{.Names}}")
		res.Assert(t, icmd.Expected{Out: "compose-e2e-compatibility_web_1"})
		res.Assert(t, icmd.Expected{Out: "compose-e2e-compatibility_words_1"})
		res.Assert(t, icmd.Expected{Out: "compose-e2e-compatibility_db_1"})
	})

	t.Run("down", func(t *testing.T) {
		c.RunDockerComposeCmd(t, "-p", projectName, "down")
	})
}

func TestConvert(t *testing.T) {
	const projectName = "compose-e2e-convert"
	c := NewParallelCLI(t)

	wd, err := os.Getwd()
	assert.NilError(t, err)

	t.Run("up", func(t *testing.T) {
		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-build-test/compose.yaml", "-p", projectName, "convert")
		res.Assert(t, icmd.Expected{Out: fmt.Sprintf(`services:
  nginx:
    build:
      context: %s
      dockerfile: Dockerfile
    networks:
      default: null
networks:
  default:
    name: compose-e2e-convert_default`, filepath.Join(wd, "fixtures", "simple-build-test", "nginx-build")), ExitCode: 0})
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2019 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 out provides a mechanism for sending localized, stylized output to the console.
package out

import (
	"bytes"
	"fmt"
	"github.com/briandowns/spinner"
	isatty "github.com/mattn/go-isatty"
	"html"
	"html/template"
	"io"
	"os"
	"strconv"
	"strings"
	"time"

	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/minikube/out/register"
	"k8s.io/minikube/pkg/minikube/style"
	"k8s.io/minikube/pkg/minikube/translate"
)

// By design, this package uses global references to language and output objects, in preference
// to passing a console object throughout the code base. Typical usage is:
//
// out.SetOutFile(os.Stdout)
// out.String("Starting up!")
// out.Step(style.StatusChange, "Configuring things")

// out.SetErrFile(os.Stderr)
// out.Fatal("Oh no, everything failed.")

// NOTE: If you do not want colorized output, set MINIKUBE_IN_STYLE=false in your environment.

var (
	// outFile is where Out* functions send output to. Set using SetOutFile()
	outFile fdWriter
	// errFile is where Err* functions send output to. Set using SetErrFile()
	errFile fdWriter
	// useColor is whether or not color output should be used, updated by Set*Writer.
	useColor = false
	// OverrideEnv is the environment variable used to override color/emoji usage
	OverrideEnv = "MINIKUBE_IN_STYLE"
	// JSON is whether or not we should output stdout in JSON format. Set using SetJSON()
	JSON = false
	// spin is spinner showed at starting minikube
	spin = spinner.New(spinner.CharSets[9], 100*time.Millisecond)
)

// MaxLogEntries controls the number of log entries to show for each source
const MaxLogEntries = 3

// fdWriter is the subset of file.File that implements io.Writer and Fd()
type fdWriter interface {
	io.Writer
	Fd() uintptr
}

// V is a convenience wrapper for templating, it represents the variable key/value pair.
type V map[string]interface{}

// Step writes a stylized and templated message to stdout
func Step(st style.Enum, format string, a ...V) {
	if st == style.Option {
		Infof(format, a...)
		return
	}
	outStyled := stylized(st, useColor, format, a...)
	if JSON {
		register.PrintStep(outStyled)
		return
	}
	register.RecordStep(outStyled)
	String(outStyled)
}

// Step writes a stylized and templated message to stdout
func SpinnerStep(st style.Enum, format string, a ...V) {
	if st == style.Option {
		Infof(format, a...)
		return
	}
	outStyled := spinnerStylized(st, useColor, format, a...)
	if JSON {
		register.PrintStep(outStyled)
		return
	}
	register.RecordStep(outStyled)
	SpinnerString(outStyled)
}

// Infof is used for informational logs (options, env variables, etc)
func Infof(format string, a ...V) {
	outStyled := stylized(style.Option, useColor, format, a...)
	if JSON {
		register.PrintInfo(outStyled)
		return
	}
	String(outStyled)
}

// String writes a basic formatted string to stdout
func String(format string, a ...interface{}) {
	// Flush log buffer so that output order makes sense
	klog.Flush()

	if outFile == nil {
		klog.Warningf("[unset outFile]: %s", fmt.Sprintf(format, a...))
		return
	}

	klog.Infof(format, a...)
	if spin.Active() == true {
	}
	_, err := fmt.Fprintf(outFile, format, a...)
	if err != nil {
		klog.Errorf("Fprintf failed: %v", err)
	}
}

// SpinnerString writes a basic formatted string and spinner to stdout
func SpinnerString(format string, a ...interface{}) {
	// Flush log buffer so that output order makes sense
	klog.Flush()

	if outFile == nil {
		klog.Warningf("[unset outFile]: %s", fmt.Sprintf(format, a...))
		return
	}

	klog.Infof(format, a...)
	_, err := fmt.Fprintf(outFile, format, a...)
	if err != nil {
		klog.Errorf("Fprintf failed: %v", err)
	}
	spin.Start()

}

// Ln writes a basic formatted string with a newline to stdout
func Ln(format string, a ...interface{}) {
	if JSON {
		klog.Warningf("please use out.T to log steps in JSON")
		return
	}
	String(format+"\n", a...)
}

// ErrT writes a stylized and templated error message to stderr
func ErrT(st style.Enum, format string, a ...V) {
	errStyled := stylized(st, useColor, format, a...)
	Err(errStyled)
}

// Err writes a basic formatted string to stderr
func Err(format string, a ...interface{}) {
	if JSON {
		register.PrintError(format)
		return
	}
	register.RecordError(format)

	if errFile == nil {
		klog.Errorf("[unset errFile]: %s", fmt.Sprintf(format, a...))
		return
	}

	klog.Warningf(format, a...)

	_, err := fmt.Fprintf(errFile, format, a...)
	if err != nil {
		klog.Errorf("Fprint failed: %v", err)
	}
}

// ErrLn writes a basic formatted string with a newline to stderr
func ErrLn(format string, a ...interface{}) {
	Err(format+"\n", a...)
}

// SuccessT is a shortcut for writing a templated success message to stdout
func SuccessT(format string, a ...V) {
	Step(style.Success, format, a...)
}

// FatalT is a shortcut for writing a templated fatal message to stderr
func FatalT(format string, a ...V) {
	ErrT(style.Fatal, format, a...)
}

// WarningT is a shortcut for writing a templated warning message to stderr
func WarningT(format string, a ...V) {
	if JSON {
		register.PrintWarning(stylized(style.Warning, useColor, format, a...))
		return
	}
	ErrT(style.Warning, format, a...)
}

// FailureT is a shortcut for writing a templated failure message to stderr
func FailureT(format string, a ...V) {
	ErrT(style.Failure, format, a...)
}

// SetOutFile configures which writer standard output goes to.
func SetOutFile(w fdWriter) {
	klog.Infof("Setting OutFile to fd %d ...", w.Fd())
	outFile = w
	useColor = wantsColor(w.Fd())
}

// SetJSON configures printing to STDOUT in JSON
func SetJSON(j bool) {
	klog.Infof("Setting JSON to %v", j)
	JSON = j
}

// SetErrFile configures which writer error output goes to.
func SetErrFile(w fdWriter) {
	klog.Infof("Setting ErrFile to fd %d...", w.Fd())
	errFile = w
	useColor = wantsColor(w.Fd())
}

// wantsColor determines if the user might want colorized output.
func wantsColor(fd uintptr) bool {
	// First process the environment: we allow users to force colors on or off.
	//
	// MINIKUBE_IN_STYLE=[1, T, true, TRUE]
	// MINIKUBE_IN_STYLE=[0, f, false, FALSE]
	//
	// If unset, we try to automatically determine suitability from the environment.
	val := os.Getenv(OverrideEnv)
	if val != "" {
		klog.Infof("%s=%q\n", OverrideEnv, os.Getenv(OverrideEnv))
		override, err := strconv.ParseBool(val)
		if err != nil {
			// That's OK, we will just fall-back to automatic detection.
			klog.Errorf("ParseBool(%s): %v", OverrideEnv, err)
		} else {
			return override
		}
	}

	// New Windows Terminal
	if os.Getenv("WT_SESSION") != "" {
		return true
	}

	term := os.Getenv("TERM")
	colorTerm := os.Getenv("COLORTERM")
	// Example: term-256color
	if !strings.Contains(term, "color") && !strings.Contains(colorTerm, "truecolor") && !strings.Contains(colorTerm, "24bit") && !strings.Contains(colorTerm, "yes") {
		klog.Infof("TERM=%s,COLORTERM=%s, which probably does not support color", term, colorTerm)
		return false
	}

	isT := isatty.IsTerminal(fd)
	klog.Infof("isatty.IsTerminal(%d) = %v\n", fd, isT)
	return isT
}

// LogEntries outputs an error along with any important log entries.
func LogEntries(msg string, err error, entries map[string][]string) {
	DisplayError(msg, err)

	for name, lines := range entries {
		Step(style.Failure, "Problems detected in {{.entry}}:", V{"entry": name})
		if len(lines) > MaxLogEntries {
			lines = lines[:MaxLogEntries]
		}
		for _, l := range lines {
			Step(style.LogEntry, l)
		}
	}
}

// DisplayError prints the error and displays the standard minikube error messaging
func DisplayError(msg string, err error) {
	klog.Warningf(fmt.Sprintf("%s: %v", msg, err))
	if JSON {
		FatalT("{{.msg}}: {{.err}}", V{"msg": translate.T(msg), "err": err})
		return
	}
	// use Warning because Error will display a duplicate message to stderr
	ErrT(style.Empty, "")
	FatalT("{{.msg}}: {{.err}}", V{"msg": translate.T(msg), "err": err})
	ErrT(style.Empty, "")
	ErrT(style.Sad, "minikube is exiting due to an error. If the above message is not useful, open an issue:")
	ErrT(style.URL, "https://github.com/kubernetes/minikube/issues/new/choose")
}

// applyTmpl applies formatting
func applyTmpl(format string, a ...V) string {
	if len(a) == 0 {
		klog.Warningf("no arguments passed for %q - returning raw string", format)
		return format
	}

	var buf bytes.Buffer
	t, err := template.New(format).Parse(format)
	if err != nil {
		klog.Errorf("unable to parse %q: %v - returning raw string.", format, err)
		return format
	}
	err = t.Execute(&buf, a[0])
	if err != nil {
		klog.Errorf("unable to execute %s: %v - returning raw string.", format, err)
		return format
	}
	out := buf.String()

	// Return quotes back to normal
	out = html.UnescapeString(out)

	// escape any outstanding '%' signs so that they don't get interpreted
	// as a formatting directive down the line
	out = strings.Replace(out, "%", "%%", -1)
	// avoid doubling up in case this function is called multiple times
	out = strings.Replace(out, "%%%%", "%%", -1)
	return out
}

// Fmt applies formatting and translation
func Fmt(format string, a ...V) string {
	format = translate.T(format)
	return applyTmpl(format, a...)
}
<commit_msg>restore break line<commit_after>/*
Copyright 2019 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 out provides a mechanism for sending localized, stylized output to the console.
package out

import (
	"bytes"
	"fmt"
	"github.com/briandowns/spinner"
	isatty "github.com/mattn/go-isatty"
	"html"
	"html/template"
	"io"
	"os"
	"strconv"
	"strings"
	"time"

	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/minikube/out/register"
	"k8s.io/minikube/pkg/minikube/style"
	"k8s.io/minikube/pkg/minikube/translate"
)

// By design, this package uses global references to language and output objects, in preference
// to passing a console object throughout the code base. Typical usage is:
//
// out.SetOutFile(os.Stdout)
// out.String("Starting up!")
// out.Step(style.StatusChange, "Configuring things")

// out.SetErrFile(os.Stderr)
// out.Fatal("Oh no, everything failed.")

// NOTE: If you do not want colorized output, set MINIKUBE_IN_STYLE=false in your environment.

var (
	// outFile is where Out* functions send output to. Set using SetOutFile()
	outFile fdWriter
	// errFile is where Err* functions send output to. Set using SetErrFile()
	errFile fdWriter
	// useColor is whether or not color output should be used, updated by Set*Writer.
	useColor = false
	// OverrideEnv is the environment variable used to override color/emoji usage
	OverrideEnv = "MINIKUBE_IN_STYLE"
	// JSON is whether or not we should output stdout in JSON format. Set using SetJSON()
	JSON = false
	// spin is spinner showed at starting minikube
	spin = spinner.New(spinner.CharSets[9], 100*time.Millisecond)
)

// MaxLogEntries controls the number of log entries to show for each source
const MaxLogEntries = 3

// fdWriter is the subset of file.File that implements io.Writer and Fd()
type fdWriter interface {
	io.Writer
	Fd() uintptr
}

// V is a convenience wrapper for templating, it represents the variable key/value pair.
type V map[string]interface{}

// Step writes a stylized and templated message to stdout
func Step(st style.Enum, format string, a ...V) {
	if st == style.Option {
		Infof(format, a...)
		return
	}
	outStyled := stylized(st, useColor, format, a...)
	if JSON {
		register.PrintStep(outStyled)
		return
	}
	register.RecordStep(outStyled)
	String(outStyled)
}

// Step writes a stylized and templated message to stdout
func SpinnerStep(st style.Enum, format string, a ...V) {
	if st == style.Option {
		Infof(format, a...)
		return
	}
	outStyled := spinnerStylized(st, useColor, format, a...)
	if JSON {
		register.PrintStep(outStyled)
		return
	}
	register.RecordStep(outStyled)
	SpinnerString(outStyled)
}

// Infof is used for informational logs (options, env variables, etc)
func Infof(format string, a ...V) {
	outStyled := stylized(style.Option, useColor, format, a...)
	if JSON {
		register.PrintInfo(outStyled)
		return
	}
	String(outStyled)
}

// String writes a basic formatted string to stdout
func String(format string, a ...interface{}) {
	// Flush log buffer so that output order makes sense
	klog.Flush()

	if outFile == nil {
		klog.Warningf("[unset outFile]: %s", fmt.Sprintf(format, a...))
		return
	}

	klog.Infof(format, a...)
	if spin.Active() == true {
		spin.Stop()
	}
	_, err := fmt.Fprintf(outFile, format, a...)
	if err != nil {
		klog.Errorf("Fprintf failed: %v", err)
	}
}

// SpinnerString writes a basic formatted string and spinner to stdout
func SpinnerString(format string, a ...interface{}) {
	// Flush log buffer so that output order makes sense
	klog.Flush()

	if outFile == nil {
		klog.Warningf("[unset outFile]: %s", fmt.Sprintf(format, a...))
		return
	}

	klog.Infof(format, a...)
	_, err := fmt.Fprintf(outFile, format, a...)
	if err != nil {
		klog.Errorf("Fprintf failed: %v", err)
	}
	spin.Start()

}

// Ln writes a basic formatted string with a newline to stdout
func Ln(format string, a ...interface{}) {
	if JSON {
		klog.Warningf("please use out.T to log steps in JSON")
		return
	}
	String(format+"\n", a...)
}

// ErrT writes a stylized and templated error message to stderr
func ErrT(st style.Enum, format string, a ...V) {
	errStyled := stylized(st, useColor, format, a...)
	Err(errStyled)
}

// Err writes a basic formatted string to stderr
func Err(format string, a ...interface{}) {
	if JSON {
		register.PrintError(format)
		return
	}
	register.RecordError(format)

	if errFile == nil {
		klog.Errorf("[unset errFile]: %s", fmt.Sprintf(format, a...))
		return
	}

	klog.Warningf(format, a...)

	_, err := fmt.Fprintf(errFile, format, a...)
	if err != nil {
		klog.Errorf("Fprint failed: %v", err)
	}
}

// ErrLn writes a basic formatted string with a newline to stderr
func ErrLn(format string, a ...interface{}) {
	Err(format+"\n", a...)
}

// SuccessT is a shortcut for writing a templated success message to stdout
func SuccessT(format string, a ...V) {
	Step(style.Success, format, a...)
}

// FatalT is a shortcut for writing a templated fatal message to stderr
func FatalT(format string, a ...V) {
	ErrT(style.Fatal, format, a...)
}

// WarningT is a shortcut for writing a templated warning message to stderr
func WarningT(format string, a ...V) {
	if JSON {
		register.PrintWarning(stylized(style.Warning, useColor, format, a...))
		return
	}
	ErrT(style.Warning, format, a...)
}

// FailureT is a shortcut for writing a templated failure message to stderr
func FailureT(format string, a ...V) {
	ErrT(style.Failure, format, a...)
}

// SetOutFile configures which writer standard output goes to.
func SetOutFile(w fdWriter) {
	klog.Infof("Setting OutFile to fd %d ...", w.Fd())
	outFile = w
	useColor = wantsColor(w.Fd())
}

// SetJSON configures printing to STDOUT in JSON
func SetJSON(j bool) {
	klog.Infof("Setting JSON to %v", j)
	JSON = j
}

// SetErrFile configures which writer error output goes to.
func SetErrFile(w fdWriter) {
	klog.Infof("Setting ErrFile to fd %d...", w.Fd())
	errFile = w
	useColor = wantsColor(w.Fd())
}

// wantsColor determines if the user might want colorized output.
func wantsColor(fd uintptr) bool {
	// First process the environment: we allow users to force colors on or off.
	//
	// MINIKUBE_IN_STYLE=[1, T, true, TRUE]
	// MINIKUBE_IN_STYLE=[0, f, false, FALSE]
	//
	// If unset, we try to automatically determine suitability from the environment.
	val := os.Getenv(OverrideEnv)
	if val != "" {
		klog.Infof("%s=%q\n", OverrideEnv, os.Getenv(OverrideEnv))
		override, err := strconv.ParseBool(val)
		if err != nil {
			// That's OK, we will just fall-back to automatic detection.
			klog.Errorf("ParseBool(%s): %v", OverrideEnv, err)
		} else {
			return override
		}
	}

	// New Windows Terminal
	if os.Getenv("WT_SESSION") != "" {
		return true
	}

	term := os.Getenv("TERM")
	colorTerm := os.Getenv("COLORTERM")
	// Example: term-256color
	if !strings.Contains(term, "color") && !strings.Contains(colorTerm, "truecolor") && !strings.Contains(colorTerm, "24bit") && !strings.Contains(colorTerm, "yes") {
		klog.Infof("TERM=%s,COLORTERM=%s, which probably does not support color", term, colorTerm)
		return false
	}

	isT := isatty.IsTerminal(fd)
	klog.Infof("isatty.IsTerminal(%d) = %v\n", fd, isT)
	return isT
}

// LogEntries outputs an error along with any important log entries.
func LogEntries(msg string, err error, entries map[string][]string) {
	DisplayError(msg, err)

	for name, lines := range entries {
		Step(style.Failure, "Problems detected in {{.entry}}:", V{"entry": name})
		if len(lines) > MaxLogEntries {
			lines = lines[:MaxLogEntries]
		}
		for _, l := range lines {
			Step(style.LogEntry, l)
		}
	}
}

// DisplayError prints the error and displays the standard minikube error messaging
func DisplayError(msg string, err error) {
	klog.Warningf(fmt.Sprintf("%s: %v", msg, err))
	if JSON {
		FatalT("{{.msg}}: {{.err}}", V{"msg": translate.T(msg), "err": err})
		return
	}
	// use Warning because Error will display a duplicate message to stderr
	ErrT(style.Empty, "")
	FatalT("{{.msg}}: {{.err}}", V{"msg": translate.T(msg), "err": err})
	ErrT(style.Empty, "")
	ErrT(style.Sad, "minikube is exiting due to an error. If the above message is not useful, open an issue:")
	ErrT(style.URL, "https://github.com/kubernetes/minikube/issues/new/choose")
}

// applyTmpl applies formatting
func applyTmpl(format string, a ...V) string {
	if len(a) == 0 {
		klog.Warningf("no arguments passed for %q - returning raw string", format)
		return format
	}

	var buf bytes.Buffer
	t, err := template.New(format).Parse(format)
	if err != nil {
		klog.Errorf("unable to parse %q: %v - returning raw string.", format, err)
		return format
	}
	err = t.Execute(&buf, a[0])
	if err != nil {
		klog.Errorf("unable to execute %s: %v - returning raw string.", format, err)
		return format
	}
	out := buf.String()

	// Return quotes back to normal
	out = html.UnescapeString(out)

	// escape any outstanding '%' signs so that they don't get interpreted
	// as a formatting directive down the line
	out = strings.Replace(out, "%", "%%", -1)
	// avoid doubling up in case this function is called multiple times
	out = strings.Replace(out, "%%%%", "%%", -1)
	return out
}

// Fmt applies formatting and translation
func Fmt(format string, a ...V) string {
	format = translate.T(format)
	return applyTmpl(format, a...)
}
<|endoftext|>


<commit_before>package settings

import (
	"encoding/json"
	"os"
	"strings"

	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
)

var (
	settings       = map[string]Setting{}
	provider       Provider
	InjectDefaults string

	AgentImage                      = NewSetting("agent-image", "rancher/rancher-agent:master")
	AuthImage                       = NewSetting("auth-image", v3.ToolsSystemImages.AuthSystemImages.KubeAPIAuth)
	CACerts                         = NewSetting("cacerts", "")
	CLIURLDarwin                    = NewSetting("cli-url-darwin", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-darwin-amd64-v1.0.0-alpha8.tar.gz")
	CLIURLLinux                     = NewSetting("cli-url-linux", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-linux-amd64-v1.0.0-alpha8.tar.gz")
	CLIURLWindows                   = NewSetting("cli-url-windows", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-windows-386-v1.0.0-alpha8.zip")
	EngineInstallURL                = NewSetting("engine-install-url", "https://releases.rancher.com/install-docker/18.09.sh")
	EngineISOURL                    = NewSetting("engine-iso-url", "https://releases.rancher.com/os/latest/rancheros-vmware.iso")
	EngineNewestVersion             = NewSetting("engine-newest-version", "v17.12.0")
	EngineSupportedRange            = NewSetting("engine-supported-range", "~v1.11.2 || ~v1.12.0 || ~v1.13.0 || ~v17.03.0 || ~v17.06.0 || ~v17.09.0 || ~v18.06.0 || ~v18.09.0")
	FirstLogin                      = NewSetting("first-login", "true")
	GlobalRegistryEnabled           = NewSetting("global-registry-enabled", "false")
	HelmVersion                     = NewSetting("helm-version", "dev")
	IngressIPDomain                 = NewSetting("ingress-ip-domain", "xip.io")
	InstallUUID                     = NewSetting("install-uuid", "")
	KubernetesVersion               = NewSetting("k8s-version", v3.DefaultK8s)
	KubernetesVersionToSystemImages = NewSetting("k8s-version-to-images", getSystemImages())
	MachineVersion                  = NewSetting("machine-version", "dev")
	Namespace                       = NewSetting("namespace", os.Getenv("CATTLE_NAMESPACE"))
	PeerServices                    = NewSetting("peer-service", os.Getenv("CATTLE_PEER_SERVICE"))
	RDNSServerBaseURL               = NewSetting("rdns-base-url", "https://api.lb.rancher.cloud/v1")
	RkeVersion                      = NewSetting("rke-version", "")
	ServerImage                     = NewSetting("server-image", "rancher/rancher")
	ServerURL                       = NewSetting("server-url", "")
	ServerVersion                   = NewSetting("server-version", "dev")
	SystemDefaultRegistry           = NewSetting("system-default-registry", "")
	SystemNamespaces                = NewSetting("system-namespaces", "kube-system,kube-public,cattle-system,cattle-alerting,cattle-logging,cattle-pipeline,cattle-prometheus,ingress-nginx,cattle-global-data,cattle-istio")
	TelemetryOpt                    = NewSetting("telemetry-opt", "prompt")
	TLSMinVersion                   = NewSetting("tls-min-version", "1.2")
	TLSCiphers                      = NewSetting("tls-ciphers", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305")
	UIFeedBackForm                  = NewSetting("ui-feedback-form", "")
	UIIndex                         = NewSetting("ui-index", "https://releases.rancher.com/ui/latest2/index.html")
	UIPath                          = NewSetting("ui-path", "")
	UIPL                            = NewSetting("ui-pl", "rancher")
	UIKubernetesSupportedVersions   = NewSetting("ui-k8s-supported-versions-range", ">= 1.11.0 <=1.14.x")
	UIKubernetesDefaultVersion      = NewSetting("ui-k8s-default-version-range", "<=1.14.x")
	WhitelistDomain                 = NewSetting("whitelist-domain", "forums.rancher.com")
	SystemMonitoringCatalogID       = NewSetting("system-monitoring-catalog-id", "catalog://?catalog=system-library&template=rancher-monitoring&version=0.0.3")
	SystemLoggingCatalogID          = NewSetting("system-logging-catalog-id", "catalog://?catalog=system-library&template=rancher-logging&version=0.1.1")
	SystemExternalDNSCatalogID      = NewSetting("system-externaldns-catalog-id", "catalog://?catalog=system-library&template=rancher-external-dns&version=0.0.1")
	SystemGlobalIstioCatalogID      = NewSetting("system-global-istio-catalog-id", "catalog://?catalog=system-library&template=rancher-istio&version=0.0.1")
	AuthUserInfoResyncCron          = NewSetting("auth-user-info-resync-cron", "0 0 * * *")
	AuthUserInfoMaxAgeSeconds       = NewSetting("auth-user-info-max-age-seconds", "3600") // 1 hour
	APIUIVersion                    = NewSetting("api-ui-version", "1.1.6")                // Please update the CATTLE_API_UI_VERSION in package/Dockerfile when updating the version here.
	RotateCertsIfExpiringInDays     = NewSetting("rotate-certs-if-expiring-in-days", "7")  // 7 days
)

func init() {
	if InjectDefaults == "" {
		return
	}
	defaults := map[string]string{}
	if err := json.Unmarshal([]byte(InjectDefaults), &defaults); err != nil {
		return
	}
	for name, defaultValue := range defaults {
		value, ok := settings[name]
		if !ok {
			continue
		}
		value.Default = defaultValue
		settings[name] = value
	}
}

type Provider interface {
	Get(name string) string
	Set(name, value string) error
	SetIfUnset(name, value string) error
	SetAll(settings map[string]Setting) error
}

type Setting struct {
	Name     string
	Default  string
	ReadOnly bool
}

func (s Setting) SetIfUnset(value string) error {
	if provider == nil {
		return s.Set(value)
	}
	return provider.SetIfUnset(s.Name, value)
}

func (s Setting) Set(value string) error {
	if provider == nil {
		s, ok := settings[s.Name]
		if ok {
			s.Default = value
			settings[s.Name] = s
		}
	} else {
		return provider.Set(s.Name, value)
	}
	return nil
}

func (s Setting) Get() string {
	if provider == nil {
		s := settings[s.Name]
		return s.Default
	}
	return provider.Get(s.Name)
}

func SetProvider(p Provider) error {
	if err := p.SetAll(settings); err != nil {
		return err
	}
	provider = p
	return nil
}

func NewSetting(name, def string) Setting {
	s := Setting{
		Name:    name,
		Default: def,
	}
	settings[s.Name] = s
	return s
}

func getSystemImages() string {
	newMap := map[string]interface{}{}
	for k := range v3.K8sVersionToRKESystemImages {
		newMap[k] = nil
	}

	data, err := json.Marshal(newMap)
	if err != nil {
		return ""
	}
	return string(data)
}

func GetEnvKey(key string) string {
	return "CATTLE_" + strings.ToUpper(strings.Replace(key, "-", "_", -1))
}
<commit_msg>Add kube-node-lease namespace to System project<commit_after>package settings

import (
	"encoding/json"
	"os"
	"strings"

	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
)

var (
	settings       = map[string]Setting{}
	provider       Provider
	InjectDefaults string

	AgentImage                      = NewSetting("agent-image", "rancher/rancher-agent:master")
	AuthImage                       = NewSetting("auth-image", v3.ToolsSystemImages.AuthSystemImages.KubeAPIAuth)
	CACerts                         = NewSetting("cacerts", "")
	CLIURLDarwin                    = NewSetting("cli-url-darwin", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-darwin-amd64-v1.0.0-alpha8.tar.gz")
	CLIURLLinux                     = NewSetting("cli-url-linux", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-linux-amd64-v1.0.0-alpha8.tar.gz")
	CLIURLWindows                   = NewSetting("cli-url-windows", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-windows-386-v1.0.0-alpha8.zip")
	EngineInstallURL                = NewSetting("engine-install-url", "https://releases.rancher.com/install-docker/18.09.sh")
	EngineISOURL                    = NewSetting("engine-iso-url", "https://releases.rancher.com/os/latest/rancheros-vmware.iso")
	EngineNewestVersion             = NewSetting("engine-newest-version", "v17.12.0")
	EngineSupportedRange            = NewSetting("engine-supported-range", "~v1.11.2 || ~v1.12.0 || ~v1.13.0 || ~v17.03.0 || ~v17.06.0 || ~v17.09.0 || ~v18.06.0 || ~v18.09.0")
	FirstLogin                      = NewSetting("first-login", "true")
	GlobalRegistryEnabled           = NewSetting("global-registry-enabled", "false")
	HelmVersion                     = NewSetting("helm-version", "dev")
	IngressIPDomain                 = NewSetting("ingress-ip-domain", "xip.io")
	InstallUUID                     = NewSetting("install-uuid", "")
	KubernetesVersion               = NewSetting("k8s-version", v3.DefaultK8s)
	KubernetesVersionToSystemImages = NewSetting("k8s-version-to-images", getSystemImages())
	MachineVersion                  = NewSetting("machine-version", "dev")
	Namespace                       = NewSetting("namespace", os.Getenv("CATTLE_NAMESPACE"))
	PeerServices                    = NewSetting("peer-service", os.Getenv("CATTLE_PEER_SERVICE"))
	RDNSServerBaseURL               = NewSetting("rdns-base-url", "https://api.lb.rancher.cloud/v1")
	RkeVersion                      = NewSetting("rke-version", "")
	ServerImage                     = NewSetting("server-image", "rancher/rancher")
	ServerURL                       = NewSetting("server-url", "")
	ServerVersion                   = NewSetting("server-version", "dev")
	SystemDefaultRegistry           = NewSetting("system-default-registry", "")
	SystemNamespaces                = NewSetting("system-namespaces", "kube-system,kube-public,cattle-system,cattle-alerting,cattle-logging,cattle-pipeline,cattle-prometheus,ingress-nginx,cattle-global-data,cattle-istio,kube-node-lease")
	TelemetryOpt                    = NewSetting("telemetry-opt", "prompt")
	TLSMinVersion                   = NewSetting("tls-min-version", "1.2")
	TLSCiphers                      = NewSetting("tls-ciphers", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305")
	UIFeedBackForm                  = NewSetting("ui-feedback-form", "")
	UIIndex                         = NewSetting("ui-index", "https://releases.rancher.com/ui/latest2/index.html")
	UIPath                          = NewSetting("ui-path", "")
	UIPL                            = NewSetting("ui-pl", "rancher")
	UIKubernetesSupportedVersions   = NewSetting("ui-k8s-supported-versions-range", ">= 1.11.0 <=1.14.x")
	UIKubernetesDefaultVersion      = NewSetting("ui-k8s-default-version-range", "<=1.14.x")
	WhitelistDomain                 = NewSetting("whitelist-domain", "forums.rancher.com")
	SystemMonitoringCatalogID       = NewSetting("system-monitoring-catalog-id", "catalog://?catalog=system-library&template=rancher-monitoring&version=0.0.3")
	SystemLoggingCatalogID          = NewSetting("system-logging-catalog-id", "catalog://?catalog=system-library&template=rancher-logging&version=0.1.1")
	SystemExternalDNSCatalogID      = NewSetting("system-externaldns-catalog-id", "catalog://?catalog=system-library&template=rancher-external-dns&version=0.0.1")
	SystemGlobalIstioCatalogID      = NewSetting("system-global-istio-catalog-id", "catalog://?catalog=system-library&template=rancher-istio&version=0.0.1")
	AuthUserInfoResyncCron          = NewSetting("auth-user-info-resync-cron", "0 0 * * *")
	AuthUserInfoMaxAgeSeconds       = NewSetting("auth-user-info-max-age-seconds", "3600") // 1 hour
	APIUIVersion                    = NewSetting("api-ui-version", "1.1.6")                // Please update the CATTLE_API_UI_VERSION in package/Dockerfile when updating the version here.
	RotateCertsIfExpiringInDays     = NewSetting("rotate-certs-if-expiring-in-days", "7")  // 7 days
)

func init() {
	if InjectDefaults == "" {
		return
	}
	defaults := map[string]string{}
	if err := json.Unmarshal([]byte(InjectDefaults), &defaults); err != nil {
		return
	}
	for name, defaultValue := range defaults {
		value, ok := settings[name]
		if !ok {
			continue
		}
		value.Default = defaultValue
		settings[name] = value
	}
}

type Provider interface {
	Get(name string) string
	Set(name, value string) error
	SetIfUnset(name, value string) error
	SetAll(settings map[string]Setting) error
}

type Setting struct {
	Name     string
	Default  string
	ReadOnly bool
}

func (s Setting) SetIfUnset(value string) error {
	if provider == nil {
		return s.Set(value)
	}
	return provider.SetIfUnset(s.Name, value)
}

func (s Setting) Set(value string) error {
	if provider == nil {
		s, ok := settings[s.Name]
		if ok {
			s.Default = value
			settings[s.Name] = s
		}
	} else {
		return provider.Set(s.Name, value)
	}
	return nil
}

func (s Setting) Get() string {
	if provider == nil {
		s := settings[s.Name]
		return s.Default
	}
	return provider.Get(s.Name)
}

func SetProvider(p Provider) error {
	if err := p.SetAll(settings); err != nil {
		return err
	}
	provider = p
	return nil
}

func NewSetting(name, def string) Setting {
	s := Setting{
		Name:    name,
		Default: def,
	}
	settings[s.Name] = s
	return s
}

func getSystemImages() string {
	newMap := map[string]interface{}{}
	for k := range v3.K8sVersionToRKESystemImages {
		newMap[k] = nil
	}

	data, err := json.Marshal(newMap)
	if err != nil {
		return ""
	}
	return string(data)
}

func GetEnvKey(key string) string {
	return "CATTLE_" + strings.ToUpper(strings.Replace(key, "-", "_", -1))
}
<|endoftext|>


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

package driver

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"os/exec"

	Log "github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/drivers/remote/api"
	docker "github.com/fsouza/go-dockerclient"

	"github.com/gorilla/mux"
	"github.com/vishvananda/netlink"
)

const (
	MethodReceiver = "NetworkDriver"
)

type Driver interface {
	Listen(net.Listener) error
}

type driver struct {
	client  *docker.Client
	version string
}

func New(version string) (Driver, error) {
	client, err := docker.NewClient("unix:///var/run/docker.sock")
	if err != nil {
		return nil, fmt.Errorf("could not connect to docker: %s", err)
	}

	return &driver{
		client:  client,
		version: version,
	}, nil
}

func (driver *driver) Listen(socket net.Listener) error {
	router := mux.NewRouter()
	router.NotFoundHandler = http.HandlerFunc(notFound)

	router.Methods("GET").Path("/status").HandlerFunc(driver.status)
	router.Methods("POST").Path("/Plugin.Activate").HandlerFunc(driver.handshake)

	handleMethod := func(method string, h http.HandlerFunc) {
		router.Methods("POST").Path(fmt.Sprintf("/%s.%s", MethodReceiver, method)).HandlerFunc(h)
	}

	handleMethod("GetCapabilities", driver.getCapabilities)
	handleMethod("CreateNetwork", driver.createNetwork)
	handleMethod("DeleteNetwork", driver.deleteNetwork)
	handleMethod("CreateEndpoint", driver.createEndpoint)
	handleMethod("DeleteEndpoint", driver.deleteEndpoint)
	handleMethod("EndpointOperInfo", driver.infoEndpoint)
	handleMethod("Join", driver.joinEndpoint)
	handleMethod("Leave", driver.leaveEndpoint)

	return http.Serve(socket, router)
}

// Handle Functions

type handshakeResp struct {
	Implements []string
}

func (driver *driver) handshake(w http.ResponseWriter, r *http.Request) {
	err := json.NewEncoder(w).Encode(&handshakeResp{
		[]string{"NetworkDriver"},
	})
	if err != nil {
		sendError(w, "encode error", http.StatusInternalServerError)
		Log.Error("handshake encode:", err)
		return
	}
	Log.Infof("Handshake completed")
}

func (driver *driver) status(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, fmt.Sprintln("PLUMgrid Libnetwork-plugin", driver.version))
}

type getcapabilitiesResp struct {
	Scope string
}

func (driver *driver) getCapabilities(w http.ResponseWriter, r *http.Request) {
	err := json.NewEncoder(w).Encode(&getcapabilitiesResp{
		scope,
	})
	if err != nil {
		Log.Fatal("get capability encode:", err)
		sendError(w, "encode error", http.StatusInternalServerError)
		return
	}
	Log.Infof("Get Capabilities completed")
}

// create network call
func (driver *driver) createNetwork(w http.ResponseWriter, r *http.Request) {
	var create api.CreateNetworkRequest
	err := json.NewDecoder(r.Body).Decode(&create)
	if err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}
	Log.Infof("Create network request %+v", &create)

	domainid := create.Options["com.docker.network.generic"].(map[string]interface{})["domain"]
	if domainid == nil {
		domainid = default_vd
	}
	DomainCreate(domainid.(string))
	router := create.Options["com.docker.network.generic"].(map[string]interface{})["router"]
	gatewayip := create.IPv4Data[0].Gateway.IP.String()
	BridgeCreate(create.NetworkID, domainid.(string), gatewayip)

	if router != nil {
		cidr := create.IPv4Data[0].Pool.String()
		_, ipnet, _ := net.ParseCIDR(cidr)
		tm, _ := hex.DecodeString(ipnet.Mask.String())
		netmask := fmt.Sprintf("%v.%v.%v.%v", tm[0], tm[1], tm[2], tm[3])
		Log.Infof("Adding router interface for : ", router, gatewayip, netmask)
		CreateNetworkLink(router.(string), domainid.(string), create.NetworkID, gatewayip, netmask)
	}

	emptyResponse(w)

	Log.Infof("Create network %s", create.NetworkID)
}

// delete network call
func (driver *driver) deleteNetwork(w http.ResponseWriter, r *http.Request) {
	var delete api.DeleteNetworkRequest
	if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}
	Log.Infof("Delete network request: %+v", &delete)

	domainid := FindDomainFromNetwork(delete.NetworkID)
	if domainid == "" {
		domainid = default_vd
	}
	DeleteNetworkLinks(domainid, delete.NetworkID)
	BridgeDelete(delete.NetworkID, domainid)
	DomainDelete(domainid)

	emptyResponse(w)
	Log.Infof("Destroy network %s", delete.NetworkID)
}

// create endpoint call
func (driver *driver) createEndpoint(w http.ResponseWriter, r *http.Request) {
	var create api.CreateEndpointRequest
	if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}
	Log.Infof("Create endpoint request %+v", &create)

	endID := create.EndpointID

	ip := create.Interface.Address
	Log.Infof("Got IP from IPAM %s", ip)

	resp := &api.CreateEndpointResponse{
		Interface: &api.EndpointInterface{},
	}

	objectResponse(w, resp)
	Log.Infof("Create endpoint %s %+v", endID, resp)
}

// delete endpoint call
func (driver *driver) deleteEndpoint(w http.ResponseWriter, r *http.Request) {
	var delete api.DeleteEndpointRequest
	if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	Log.Debugf("Delete endpoint request: %+v", &delete)
	emptyResponse(w)
	Log.Infof("Delete endpoint %s", delete.EndpointID)
}

// endpoint info request
func (driver *driver) infoEndpoint(w http.ResponseWriter, r *http.Request) {
	var info api.EndpointInfoRequest
	if err := json.NewDecoder(r.Body).Decode(&info); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	Log.Infof("Endpoint info request: %+v", &info)
	objectResponse(w, &api.EndpointInfoResponse{Value: map[string]interface{}{}})
	Log.Infof("Endpoint info %s", info.EndpointID)
}

// join call
func (driver *driver) joinEndpoint(w http.ResponseWriter, r *http.Request) {
	var j api.JoinRequest
	if err := json.NewDecoder(r.Body).Decode(&j); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	Log.Infof("Join request: %+v", &j)

	netID := j.NetworkID
	endID := j.EndpointID
	domainid := FindDomainFromNetwork(netID)
	if domainid == "" {
		domainid = default_vd
	}
	gatewayIP := FindNetworkGateway(domainid, netID)
	// create and attach local name to the bridge
	local := vethPair(endID[:5])
	if err := netlink.LinkAdd(local); err != nil {
		Log.Error(err)
		errorResponsef(w, "could not create veth pair")
		return
	}

	if_local_name := "tap" + endID[:5]

	//getting mac address of tap...
	cmdStr0 := "ifconfig " + local.PeerName + " | awk '/HWaddr/ {print $NF}'"
	Log.Infof("mac address cmd: %s", cmdStr0)
	cmd0 := exec.Command("/bin/sh", "-c", cmdStr0)
	var out0 bytes.Buffer
	cmd0.Stdout = &out0
	err0 := cmd0.Run()
	if err0 != nil {
		Log.Error("Error thrown: ", err0)
	}
	mac := out0.String()
	Log.Infof("output of cmd: %s\n", mac)

	//first command {adding port on plumgrid}
	cmdStr1 := "sudo /opt/pg/bin/ifc_ctl gateway add_port " + if_local_name
	Log.Infof("second cmd: %s", cmdStr1)
	cmd1 := exec.Command("/bin/sh", "-c", cmdStr1)
	var out1 bytes.Buffer
	cmd1.Stdout = &out1
	err1 := cmd1.Run()
	if err1 != nil {
		Log.Error("Error thrown: ", err1)
	}
	Log.Infof("output of cmd: %+v\n", out1.String())

	//second command {up the port on plumgrid}
	cmdStr2 := "sudo /opt/pg/bin/ifc_ctl gateway ifup " + if_local_name + " access_vm cont_" + endID[:2] + " " + mac[:17] + " pgtag2=bridge-" + netID[:10] + " pgtag1=" + domainid
	Log.Infof("third cmd: %s", cmdStr2)
	cmd2 := exec.Command("/bin/sh", "-c", cmdStr2)
	var out2 bytes.Buffer
	cmd2.Stdout = &out2
	err2 := cmd2.Run()
	if err2 != nil {
		Log.Error("Error thrown: ", err2)
	}
	Log.Infof("output of cmd: %+v\n", out2.String())

	if netlink.LinkSetUp(local) != nil {
		errorResponsef(w, `unable to bring veth up`)
		return
	}

	ifname := &api.InterfaceName{
		SrcName:   local.PeerName,
		DstPrefix: "ethpg",
	}

	res := &api.JoinResponse{
		InterfaceName: ifname,
		Gateway:       gatewayIP,
	}

	objectResponse(w, res)
	Log.Infof("Join endpoint %s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)
}

// leave call
func (driver *driver) leaveEndpoint(w http.ResponseWriter, r *http.Request) {
	var l api.LeaveRequest
	if err := json.NewDecoder(r.Body).Decode(&l); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	Log.Infof("Leave request: %+v", &l)

	if_local_name := "tap" + l.EndpointID[:5]

	//first command {adding port on plumgrid}
	cmdStr1 := "sudo /opt/pg/bin/ifc_ctl gateway ifdown " + if_local_name
	Log.Infof("second cmd: %s", cmdStr1)
	cmd1 := exec.Command("/bin/sh", "-c", cmdStr1)
	var out1 bytes.Buffer
	cmd1.Stdout = &out1
	err1 := cmd1.Run()
	if err1 != nil {
		Log.Error("Error thrown: ", err1)
	}
	Log.Infof("output of cmd: %+v\n", out1.String())

	//second command {up the port on plumgrid}
	cmdStr2 := "sudo /opt/pg/bin/ifc_ctl gateway del_port " + if_local_name
	Log.Infof("third cmd: %s", cmdStr2)
	cmd2 := exec.Command("/bin/sh", "-c", cmdStr2)
	var out2 bytes.Buffer
	cmd2.Stdout = &out2
	err2 := cmd2.Run()
	if err2 != nil {
		Log.Error("Error thrown: ", err2)
	}
	Log.Infof("output of cmd: %+v\n", out2.String())

	local := vethPair(l.EndpointID[:5])
	if err := netlink.LinkDel(local); err != nil {
		Log.Warningf("unable to delete veth on leave: %s", err)
	}
	emptyResponse(w)
	Log.Infof("Leave %s:%s", l.NetworkID, l.EndpointID)
}
<commit_msg>changing access type<commit_after>// Copyright 2016 PLUMgrid
//
// 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

package driver

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"os/exec"

	Log "github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/drivers/remote/api"
	docker "github.com/fsouza/go-dockerclient"

	"github.com/gorilla/mux"
	"github.com/vishvananda/netlink"
)

const (
	MethodReceiver = "NetworkDriver"
)

type Driver interface {
	Listen(net.Listener) error
}

type driver struct {
	client  *docker.Client
	version string
}

func New(version string) (Driver, error) {
	client, err := docker.NewClient("unix:///var/run/docker.sock")
	if err != nil {
		return nil, fmt.Errorf("could not connect to docker: %s", err)
	}

	return &driver{
		client:  client,
		version: version,
	}, nil
}

func (driver *driver) Listen(socket net.Listener) error {
	router := mux.NewRouter()
	router.NotFoundHandler = http.HandlerFunc(notFound)

	router.Methods("GET").Path("/status").HandlerFunc(driver.status)
	router.Methods("POST").Path("/Plugin.Activate").HandlerFunc(driver.handshake)

	handleMethod := func(method string, h http.HandlerFunc) {
		router.Methods("POST").Path(fmt.Sprintf("/%s.%s", MethodReceiver, method)).HandlerFunc(h)
	}

	handleMethod("GetCapabilities", driver.getCapabilities)
	handleMethod("CreateNetwork", driver.createNetwork)
	handleMethod("DeleteNetwork", driver.deleteNetwork)
	handleMethod("CreateEndpoint", driver.createEndpoint)
	handleMethod("DeleteEndpoint", driver.deleteEndpoint)
	handleMethod("EndpointOperInfo", driver.infoEndpoint)
	handleMethod("Join", driver.joinEndpoint)
	handleMethod("Leave", driver.leaveEndpoint)

	return http.Serve(socket, router)
}

// Handle Functions

type handshakeResp struct {
	Implements []string
}

func (driver *driver) handshake(w http.ResponseWriter, r *http.Request) {
	err := json.NewEncoder(w).Encode(&handshakeResp{
		[]string{"NetworkDriver"},
	})
	if err != nil {
		sendError(w, "encode error", http.StatusInternalServerError)
		Log.Error("handshake encode:", err)
		return
	}
	Log.Infof("Handshake completed")
}

func (driver *driver) status(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, fmt.Sprintln("PLUMgrid Libnetwork-plugin", driver.version))
}

type getcapabilitiesResp struct {
	Scope string
}

func (driver *driver) getCapabilities(w http.ResponseWriter, r *http.Request) {
	err := json.NewEncoder(w).Encode(&getcapabilitiesResp{
		scope,
	})
	if err != nil {
		Log.Fatal("get capability encode:", err)
		sendError(w, "encode error", http.StatusInternalServerError)
		return
	}
	Log.Infof("Get Capabilities completed")
}

// create network call
func (driver *driver) createNetwork(w http.ResponseWriter, r *http.Request) {
	var create api.CreateNetworkRequest
	err := json.NewDecoder(r.Body).Decode(&create)
	if err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}
	Log.Infof("Create network request %+v", &create)

	domainid := create.Options["com.docker.network.generic"].(map[string]interface{})["domain"]
	if domainid == nil {
		domainid = default_vd
	}
	DomainCreate(domainid.(string))
	router := create.Options["com.docker.network.generic"].(map[string]interface{})["router"]
	gatewayip := create.IPv4Data[0].Gateway.IP.String()
	BridgeCreate(create.NetworkID, domainid.(string), gatewayip)

	if router != nil {
		cidr := create.IPv4Data[0].Pool.String()
		_, ipnet, _ := net.ParseCIDR(cidr)
		tm, _ := hex.DecodeString(ipnet.Mask.String())
		netmask := fmt.Sprintf("%v.%v.%v.%v", tm[0], tm[1], tm[2], tm[3])
		Log.Infof("Adding router interface for : ", router, gatewayip, netmask)
		CreateNetworkLink(router.(string), domainid.(string), create.NetworkID, gatewayip, netmask)
	}

	emptyResponse(w)

	Log.Infof("Create network %s", create.NetworkID)
}

// delete network call
func (driver *driver) deleteNetwork(w http.ResponseWriter, r *http.Request) {
	var delete api.DeleteNetworkRequest
	if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}
	Log.Infof("Delete network request: %+v", &delete)

	domainid := FindDomainFromNetwork(delete.NetworkID)
	if domainid == "" {
		domainid = default_vd
	}
	DeleteNetworkLinks(domainid, delete.NetworkID)
	BridgeDelete(delete.NetworkID, domainid)
	DomainDelete(domainid)

	emptyResponse(w)
	Log.Infof("Destroy network %s", delete.NetworkID)
}

// create endpoint call
func (driver *driver) createEndpoint(w http.ResponseWriter, r *http.Request) {
	var create api.CreateEndpointRequest
	if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}
	Log.Infof("Create endpoint request %+v", &create)

	endID := create.EndpointID

	ip := create.Interface.Address
	Log.Infof("Got IP from IPAM %s", ip)

	resp := &api.CreateEndpointResponse{
		Interface: &api.EndpointInterface{},
	}

	objectResponse(w, resp)
	Log.Infof("Create endpoint %s %+v", endID, resp)
}

// delete endpoint call
func (driver *driver) deleteEndpoint(w http.ResponseWriter, r *http.Request) {
	var delete api.DeleteEndpointRequest
	if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	Log.Debugf("Delete endpoint request: %+v", &delete)
	emptyResponse(w)
	Log.Infof("Delete endpoint %s", delete.EndpointID)
}

// endpoint info request
func (driver *driver) infoEndpoint(w http.ResponseWriter, r *http.Request) {
	var info api.EndpointInfoRequest
	if err := json.NewDecoder(r.Body).Decode(&info); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	Log.Infof("Endpoint info request: %+v", &info)
	objectResponse(w, &api.EndpointInfoResponse{Value: map[string]interface{}{}})
	Log.Infof("Endpoint info %s", info.EndpointID)
}

// join call
func (driver *driver) joinEndpoint(w http.ResponseWriter, r *http.Request) {
	var j api.JoinRequest
	if err := json.NewDecoder(r.Body).Decode(&j); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	Log.Infof("Join request: %+v", &j)

	netID := j.NetworkID
	endID := j.EndpointID
	domainid := FindDomainFromNetwork(netID)
	if domainid == "" {
		domainid = default_vd
	}
	gatewayIP := FindNetworkGateway(domainid, netID)
	// create and attach local name to the bridge
	local := vethPair(endID[:5])
	if err := netlink.LinkAdd(local); err != nil {
		Log.Error(err)
		errorResponsef(w, "could not create veth pair")
		return
	}

	if_local_name := "tap" + endID[:5]

	//getting mac address of tap...
	cmdStr0 := "ifconfig " + local.PeerName + " | awk '/HWaddr/ {print $NF}'"
	Log.Infof("mac address cmd: %s", cmdStr0)
	cmd0 := exec.Command("/bin/sh", "-c", cmdStr0)
	var out0 bytes.Buffer
	cmd0.Stdout = &out0
	err0 := cmd0.Run()
	if err0 != nil {
		Log.Error("Error thrown: ", err0)
	}
	mac := out0.String()
	Log.Infof("output of cmd: %s\n", mac)

	//first command {adding port on plumgrid}
	cmdStr1 := "sudo /opt/pg/bin/ifc_ctl gateway add_port " + if_local_name
	Log.Infof("second cmd: %s", cmdStr1)
	cmd1 := exec.Command("/bin/sh", "-c", cmdStr1)
	var out1 bytes.Buffer
	cmd1.Stdout = &out1
	err1 := cmd1.Run()
	if err1 != nil {
		Log.Error("Error thrown: ", err1)
	}
	Log.Infof("output of cmd: %+v\n", out1.String())

	//second command {up the port on plumgrid}
	cmdStr2 := "sudo /opt/pg/bin/ifc_ctl gateway ifup " + if_local_name + " access_container cont_" + endID[:2] + " " + mac[:17] + " pgtag2=bridge-" + netID[:10] + " pgtag1=" + domainid
	Log.Infof("third cmd: %s", cmdStr2)
	cmd2 := exec.Command("/bin/sh", "-c", cmdStr2)
	var out2 bytes.Buffer
	cmd2.Stdout = &out2
	err2 := cmd2.Run()
	if err2 != nil {
		Log.Error("Error thrown: ", err2)
	}
	Log.Infof("output of cmd: %+v\n", out2.String())

	if netlink.LinkSetUp(local) != nil {
		errorResponsef(w, `unable to bring veth up`)
		return
	}

	ifname := &api.InterfaceName{
		SrcName:   local.PeerName,
		DstPrefix: "ethpg",
	}

	res := &api.JoinResponse{
		InterfaceName: ifname,
		Gateway:       gatewayIP,
	}

	objectResponse(w, res)
	Log.Infof("Join endpoint %s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)
}

// leave call
func (driver *driver) leaveEndpoint(w http.ResponseWriter, r *http.Request) {
	var l api.LeaveRequest
	if err := json.NewDecoder(r.Body).Decode(&l); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	Log.Infof("Leave request: %+v", &l)

	if_local_name := "tap" + l.EndpointID[:5]

	//first command {adding port on plumgrid}
	cmdStr1 := "sudo /opt/pg/bin/ifc_ctl gateway ifdown " + if_local_name
	Log.Infof("second cmd: %s", cmdStr1)
	cmd1 := exec.Command("/bin/sh", "-c", cmdStr1)
	var out1 bytes.Buffer
	cmd1.Stdout = &out1
	err1 := cmd1.Run()
	if err1 != nil {
		Log.Error("Error thrown: ", err1)
	}
	Log.Infof("output of cmd: %+v\n", out1.String())

	//second command {up the port on plumgrid}
	cmdStr2 := "sudo /opt/pg/bin/ifc_ctl gateway del_port " + if_local_name
	Log.Infof("third cmd: %s", cmdStr2)
	cmd2 := exec.Command("/bin/sh", "-c", cmdStr2)
	var out2 bytes.Buffer
	cmd2.Stdout = &out2
	err2 := cmd2.Run()
	if err2 != nil {
		Log.Error("Error thrown: ", err2)
	}
	Log.Infof("output of cmd: %+v\n", out2.String())

	local := vethPair(l.EndpointID[:5])
	if err := netlink.LinkDel(local); err != nil {
		Log.Warningf("unable to delete veth on leave: %s", err)
	}
	emptyResponse(w)
	Log.Infof("Leave %s:%s", l.NetworkID, l.EndpointID)
}
<|endoftext|>