<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/cascades-fbp/cascades/graph"
	"github.com/cascades-fbp/cascades/library"
	"github.com/codegangsta/cli"
)

// Implements catalog updating command
func addToLibrary(c *cli.Context) {
	if len(c.Args()) != 1 {
		fmt.Printf("Incorrect Usage. You need to provide a directory/file path as argument!\n\n")
		cli.ShowAppHelp(c)
		return
	}

	// read components library file if exists
	data, err := ioutil.ReadFile(c.GlobalString("file"))
	if err != nil && os.IsExist(err) {
		fmt.Printf("Failed to read catalogue file: %s", err.Error())
		return
	}

	// create JSON library (the only implementation for now)
	var db library.JSONLibrary
	if data != nil {
		err = json.Unmarshal(data, &db)
	}
	if err != nil {
		db = library.JSONLibrary{
			Entries: make(map[string]library.Entry),
		}
		db.Name = "Local Components Library"
		db.Created = time.Now()
	}

	info, err := os.Stat(c.Args().First())
	if err != nil {
		fmt.Printf("Failed to access given directory/file: %s", err.Error())
		fmt.Println("")
		return
	}

	if info.IsDir() {
		path, err := filepath.Abs(c.Args().First())
		if err != nil {
			fmt.Printf("Failed to resolve absolute path for %s: %s", c.Args().First(), err.Error())
			fmt.Println("")
			return
		}
		addDirToLibrary(c, db, path)
	} else {
		if c.String("name") == "" {
			fmt.Println("You need to provide a name when adding a component file")
			cli.ShowAppHelp(c)
			fmt.Println("")
			return
		}
		err = addFileToLibrary(c, db, c.Args().First(), c.String("name"))
		if err != nil {
			fmt.Printf("Failed to add a component: %s", err.Error())
			fmt.Println("")
			return
		}
	}

	// write index back or create if not exists
	db.Updated = time.Now()
	result, err := db.JSON()
	if err != nil {
		fmt.Printf("Failed to generate JSON: %s", err.Error())
		return
	}
	err = ioutil.WriteFile(c.GlobalString("file"), result, os.FileMode(0644))
	if err != nil {
		fmt.Printf("Failed to save registry file: %s", err.Error())
		return
	}
}

func addDirToLibrary(c *cli.Context, r library.Registrar, dir string) {
	fmt.Printf("Walking components directory: %s\n", dir)
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			fmt.Println(err.Error())
			return nil
		}
		if info.IsDir() {
			return nil
		}
		name := strings.TrimPrefix(strings.TrimPrefix(path, dir), "/")
		name = strings.TrimSuffix(name, ".fbp")
		name = strings.TrimSuffix(name, ".json")
		name = strings.TrimSuffix(name, ".exe")
		err = addFileToLibrary(c, r, path, name)
		if err != nil {
			fmt.Printf("Error adding to registry: %s", err.Error())
		}
		return nil
	})

	fmt.Println("DONE")
}

func addFileToLibrary(c *cli.Context, r library.Registrar, file string, name string) error {
	var entry *library.Entry
	if strings.HasSuffix(file, ".fbp") {
		// adding a compsite component (subgraph) in .fbp format
		data, err := ioutil.ReadFile(file)
		if err != nil {
			return err
		}
		g, err := graph.ParseFBP(data)
		if err != nil {
			return err
		}
		if entry, err = graphToEntry(g, file, r); err != nil {
			return err
		}
		entry.Elementary = false

	} else if strings.HasSuffix(file, ".json") {
		// adding a compsite component (subgraph) in .json format
		data, err := ioutil.ReadFile(file)
		if err != nil {
			return err
		}
		g, err := graph.ParseJSON(data)
		if err != nil {
			return err
		}
		if entry, err = graphToEntry(g, file, r); err != nil {
			return err
		}
		entry.Elementary = false

	} else {
		// adding an elementary component
		c := exec.Command(file, "--json")
		out, err := c.Output()
		if err != nil {
			return fmt.Errorf("Cannot register component %s: %s", name, err.Error())
		}
		if err = json.Unmarshal(out, &entry); err != nil {
			return err
		}
		entry.Elementary = true
	}

	if len(entry.Inports) == 0 && len(entry.Outports) == 0 {
		return fmt.Errorf("Cannot register component %s: inports and outports are empty", name)
	}

	entry.Name = name
	entry.Executable = file
	if r.Exists(name) && !c.Bool("force") {
		fmt.Printf("WARNING \"%s\" already exists and --force is not provided. Ignoring this entry", name)
		fmt.Println("")
	} else {
		r.Add(*entry)
		fmt.Printf("Added \"%s\"", name)
		fmt.Println("")
	}

	return nil
}

func graphToEntry(g *graph.GraphDescription, path string, r library.Registrar) (*library.Entry, error) {
	entry := &library.Entry{
		Executable:  path,
		Description: g.Properties["name"],
		Inports:     []library.EntryPort{},
		Outports:    []library.EntryPort{},
	}

	for _, e := range g.Inports {
		parts := strings.SplitN(e.Private, ".", 2)
		rec, err := r.Get(g.Processes[parts[0]].Component)
		if err != nil {
			return nil, fmt.Errorf("Component %s not found in library", parts[0])
		}
		port, found := rec.FindInport(strings.ToLower(parts[1]))
		if !found {
			return nil, fmt.Errorf("Port %s not found in component %s", parts[1], parts[0])
		}
		port.Name = e.Public
		entry.Inports = append(entry.Inports, port)
	}

	for _, e := range g.Outports {
		parts := strings.SplitN(e.Private, ".", 2)
		rec, err := r.Get(g.Processes[parts[0]].Component)
		if err != nil {
			return nil, fmt.Errorf("Component %s not found in library", parts[0])
		}
		port, found := rec.FindOutport(strings.ToLower(parts[1]))
		if !found {
			return nil, fmt.Errorf("Port %s not found in component %s", parts[1], parts[0])
		}
		port.Name = e.Public
		entry.Outports = append(entry.Outports, port)
	}

	return entry, nil
}
<commit_msg>Fixes to library command to avoid ugly names under windows<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/cascades-fbp/cascades/graph"
	"github.com/cascades-fbp/cascades/library"
	"github.com/codegangsta/cli"
)

// Implements catalog updating command
func addToLibrary(c *cli.Context) {
	if len(c.Args()) != 1 {
		fmt.Printf("Incorrect Usage. You need to provide a directory/file path as argument!\n\n")
		cli.ShowAppHelp(c)
		return
	}

	// read components library file if exists
	data, err := ioutil.ReadFile(c.GlobalString("file"))
	if err != nil && os.IsExist(err) {
		fmt.Printf("Failed to read catalogue file: %s", err.Error())
		return
	}

	// create JSON library (the only implementation for now)
	var db library.JSONLibrary
	if data != nil {
		err = json.Unmarshal(data, &db)
	}
	if err != nil {
		db = library.JSONLibrary{
			Entries: make(map[string]library.Entry),
		}
		db.Name = "Local Components Library"
		db.Created = time.Now()
	}

	info, err := os.Stat(c.Args().First())
	if err != nil {
		fmt.Printf("Failed to access given directory/file: %s", err.Error())
		fmt.Println("")
		return
	}

	if info.IsDir() {
		path, err := filepath.Abs(c.Args().First())
		if err != nil {
			fmt.Printf("Failed to resolve absolute path for %s: %s", c.Args().First(), err.Error())
			fmt.Println("")
			return
		}
		addDirToLibrary(c, db, path)
	} else {
		if c.String("name") == "" {
			fmt.Println("You need to provide a name when adding a component file")
			cli.ShowAppHelp(c)
			fmt.Println("")
			return
		}
		err = addFileToLibrary(c, db, c.Args().First(), c.String("name"))
		if err != nil {
			fmt.Printf("Failed to add a component: %s", err.Error())
			fmt.Println("")
			return
		}
	}

	// write index back or create if not exists
	db.Updated = time.Now()
	result, err := db.JSON()
	if err != nil {
		fmt.Printf("Failed to generate JSON: %s", err.Error())
		return
	}
	err = ioutil.WriteFile(c.GlobalString("file"), result, os.FileMode(0644))
	if err != nil {
		fmt.Printf("Failed to save registry file: %s", err.Error())
		return
	}
}

func addDirToLibrary(c *cli.Context, r library.Registrar, dir string) {
	fmt.Printf("Walking components directory: %s\n", dir)
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			fmt.Println(err.Error())
			return nil
		}
		if info.IsDir() {
			return nil
		}
		sep := string(filepath.Separator)
		name := strings.TrimPrefix(strings.TrimPrefix(path, dir), sep)
		name = strings.TrimPrefix(name, sep)
		name = strings.Replace(name, sep, "/", 1)
		name = strings.TrimSuffix(name, ".fbp")
		name = strings.TrimSuffix(name, ".json")
		name = strings.TrimSuffix(name, ".exe")
		err = addFileToLibrary(c, r, path, name)
		if err != nil {
			fmt.Printf("Error adding to registry: %s", err.Error())
		}
		return nil
	})

	fmt.Println("DONE")
}

func addFileToLibrary(c *cli.Context, r library.Registrar, file string, name string) error {
	var entry *library.Entry
	if strings.HasSuffix(file, ".fbp") {
		// adding a compsite component (subgraph) in .fbp format
		data, err := ioutil.ReadFile(file)
		if err != nil {
			return err
		}
		g, err := graph.ParseFBP(data)
		if err != nil {
			return err
		}
		if entry, err = graphToEntry(g, file, r); err != nil {
			return err
		}
		entry.Elementary = false

	} else if strings.HasSuffix(file, ".json") {
		// adding a compsite component (subgraph) in .json format
		data, err := ioutil.ReadFile(file)
		if err != nil {
			return err
		}
		g, err := graph.ParseJSON(data)
		if err != nil {
			return err
		}
		if entry, err = graphToEntry(g, file, r); err != nil {
			return err
		}
		entry.Elementary = false

	} else {
		// adding an elementary component
		c := exec.Command(file, "--json")
		out, err := c.Output()
		if err != nil {
			return fmt.Errorf("Cannot register component %s: %s", name, err.Error())
		}
		if err = json.Unmarshal(out, &entry); err != nil {
			return err
		}
		entry.Elementary = true
	}

	if len(entry.Inports) == 0 && len(entry.Outports) == 0 {
		return fmt.Errorf("Cannot register component %s: inports and outports are empty", name)
	}

	entry.Name = name
	entry.Executable = file
	if r.Exists(name) && !c.Bool("force") {
		fmt.Printf("WARNING \"%s\" already exists and --force is not provided. Ignoring this entry", name)
		fmt.Println("")
	} else {
		r.Add(*entry)
		fmt.Printf("Added \"%s\"", name)
		fmt.Println("")
	}

	return nil
}

func graphToEntry(g *graph.GraphDescription, path string, r library.Registrar) (*library.Entry, error) {
	entry := &library.Entry{
		Executable:  path,
		Description: g.Properties["name"],
		Inports:     []library.EntryPort{},
		Outports:    []library.EntryPort{},
	}

	for _, e := range g.Inports {
		parts := strings.SplitN(e.Private, ".", 2)
		rec, err := r.Get(g.Processes[parts[0]].Component)
		if err != nil {
			return nil, fmt.Errorf("Component %s not found in library", parts[0])
		}
		port, found := rec.FindInport(strings.ToLower(parts[1]))
		if !found {
			return nil, fmt.Errorf("Port %s not found in component %s", parts[1], parts[0])
		}
		port.Name = e.Public
		entry.Inports = append(entry.Inports, port)
	}

	for _, e := range g.Outports {
		parts := strings.SplitN(e.Private, ".", 2)
		rec, err := r.Get(g.Processes[parts[0]].Component)
		if err != nil {
			return nil, fmt.Errorf("Component %s not found in library", parts[0])
		}
		port, found := rec.FindOutport(strings.ToLower(parts[1]))
		if !found {
			return nil, fmt.Errorf("Port %s not found in component %s", parts[1], parts[0])
		}
		port.Name = e.Public
		entry.Outports = append(entry.Outports, port)
	}

	return entry, nil
}
<|endoftext|>


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

package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"syscall"

	"github.com/ghodss/yaml"
	"github.com/nerdtakula/supervisor"
	"google.golang.org/grpc"

	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/logging/logrus"
	"github.com/ligato/cn-infra/servicelabel"

	"github.com/contiv/vpp/cmd/contiv-stn/model/stn"
	"github.com/contiv/vpp/plugins/contiv"
)

const (
	defaultContivCfgFile  = "/etc/agent/contiv.yaml"
	defaultEtcdCfgFile    = "/etc/etcd/etcd.conf"
	defaultSupervisorPort = 9001
	defaultStnServerPort  = 50051
)

var (
	contivCfgFile  = flag.String("contiv-config", defaultContivCfgFile, "location of the contiv-agent config file")
	etcdCfgFile    = flag.String("etcd-config", defaultEtcdCfgFile, "location of the ETCD config file")
	supervisorPort = flag.Int("supervisor-port", defaultSupervisorPort, "management port of the supervisor process")
	stnServerPort  = flag.Int("stn-server-port", defaultStnServerPort, "port where STN GRPC server listens for connections")
)

var logger logging.Logger // global logger

// init initializes the global logger
func init() {
	logger = logrus.DefaultLogger()
	logger.SetLevel(logging.DebugLevel)
}

// stealNIC requests stealing the specified NIC from the STN GRPC server.
func stealNIC(nicName string) (*stn.STNReply, error) {
	logger.Debugf("Stealing the NIC: %s", nicName)

	// connect to STN GRPC server
	conn, err := grpc.Dial(fmt.Sprintf(":%d", *stnServerPort), grpc.WithInsecure())
	if err != nil {
		logger.Errorf("Unable to connect to STN GRPC: %v", err)
		return nil, err
	}
	defer conn.Close()
	c := stn.NewSTNClient(conn)

	// request stealing the interface
	reply, err := c.StealInterface(context.Background(), &stn.STNRequest{
		InterfaceName: nicName,
	})
	if err != nil {
		logger.Errorf("Error by executing STN GRPC: %v", err)
		return nil, err
	}

	logger.Debug(reply)
	return reply, nil
}

// releaseNIC requests reverting the specified NIC to its original state using the STN GRPC server.
func releaseNIC(nicName string) error {
	logger.Debugf("Releasing the NIC: %s", nicName)

	// connect to STN GRPC server
	conn, err := grpc.Dial(fmt.Sprintf(":%d", *stnServerPort), grpc.WithInsecure())
	if err != nil {
		logger.Errorf("Unable to connect to STN GRPC: %v", err)
		return err
	}
	defer conn.Close()
	c := stn.NewSTNClient(conn)

	// request release of the interface
	reply, err := c.ReleaseInterface(context.Background(), &stn.STNRequest{
		InterfaceName: nicName,
	})
	if err != nil {
		logger.Errorf("Error by executing STN GRPC: %v", err)
		return err
	}

	logger.Debug(reply)
	return nil
}

// parseSTNConfig parses the config file and looks up for STN configuration.
// In case that STN was requested for this node, returns the interface to be stealed and optionally its name on VPP.
func parseSTNConfig() (config *contiv.Config, nicToSteal string, vppIfName string, err error) {

	// read config YAML
	yamlFile, err := ioutil.ReadFile(*contivCfgFile)
	if err != nil {
		logger.Errorf("Error by reading config file %s: %v", *contivCfgFile, err)
		return
	}

	// unmarshall the YAML
	config = &contiv.Config{}
	err = yaml.Unmarshal(yamlFile, config)
	if err != nil {
		logger.Errorf("Error by unmarshaling YAML: %v", err)
		return
	}
	if config.TAPInterfaceVersion == 0 {
		config.TAPInterfaceVersion = 1 // default
	}

	// try to find node config and return STN interface name if found
	nodeName := os.Getenv(servicelabel.MicroserviceLabelEnvVar)
	logger.Debugf("Looking for node '%s' specific config", nodeName)

	for _, nc := range config.NodeConfig {
		if nc.NodeName == nodeName {
			logger.Debugf("Found interface to be stealed: %s", nc.StealInterface)
			nicToSteal = nc.StealInterface
			vppIfName = nc.MainVppInterface.InterfaceName
			return
		}
	}

	return
}

func main() {
	flag.Parse()

	logger.Debugf("Starting contiv-init process")

	// check whether STN is required and get NIC name
	contivCfg, nicToSteal, vppIfName, err := parseSTNConfig()
	if err != nil {
		logger.Errorf("Error by parsing STN config: %v", err)
		os.Exit(-1)
	}

	var stnData *stn.STNReply
	if nicToSteal != "" {
		// steal the NIC
		stnData, err = stealNIC(nicToSteal)
		if err != nil {
			logger.Warnf("Error by stealing the NIC %s: %v", nicToSteal, err)
			// do not fail of STN was not succesfull
			nicToSteal = ""
		}
	} else {
		logger.Debug("STN not requested")
	}

	// connect to supervisor API
	client := supervisor.New("localhost", *supervisorPort, "", "")

	// start VPP
	logger.Debug("Starting VPP")
	_, err = client.StartProcess("vpp", false)
	if err != nil {
		logger.Errorf("Error by starting VPP process: %v", err)
		os.Exit(-1)
	}

	if nicToSteal != "" {
		// configure connectivity on VPP
		vppCfg, err := configureVpp(contivCfg, stnData, vppIfName)
		if err != nil {
			logger.Errorf("Error by configuring VPP: %v", err)
			os.Exit(-1)
		}

		// persist VPP config in ETCD
		err = persistVppConfig(contivCfg, stnData, vppCfg)
		if err != nil {
			logger.Errorf("Error by persisting VPP config in ETCD: %v", err)
			os.Exit(-1)
		}
	}

	// start contiv-agent
	logger.Debugf("Starting contiv-agent")
	_, err = client.StartProcess("contiv-agent", false)
	if err != nil {
		logger.Errorf("Error by starting contiv-agent process: %v", err)
		os.Exit(-1)
	}

	// wait until SIGINT/SIGTERM signal
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	sig := <-sigs
	logger.Debug("%v signal recieved, exiting", sig)

	// request releasing the NIC
	if nicToSteal != "" {
		releaseNIC(nicToSteal)
	}
}
<commit_msg>Fixed change in config options<commit_after>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"syscall"

	"github.com/ghodss/yaml"
	"github.com/nerdtakula/supervisor"
	"google.golang.org/grpc"

	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/logging/logrus"
	"github.com/ligato/cn-infra/servicelabel"

	"github.com/contiv/vpp/cmd/contiv-stn/model/stn"
	"github.com/contiv/vpp/plugins/contiv"
)

const (
	defaultContivCfgFile  = "/etc/agent/contiv.yaml"
	defaultEtcdCfgFile    = "/etc/etcd/etcd.conf"
	defaultSupervisorPort = 9001
	defaultStnServerPort  = 50051
)

var (
	contivCfgFile  = flag.String("contiv-config", defaultContivCfgFile, "location of the contiv-agent config file")
	etcdCfgFile    = flag.String("etcd-config", defaultEtcdCfgFile, "location of the ETCD config file")
	supervisorPort = flag.Int("supervisor-port", defaultSupervisorPort, "management port of the supervisor process")
	stnServerPort  = flag.Int("stn-server-port", defaultStnServerPort, "port where STN GRPC server listens for connections")
)

var logger logging.Logger // global logger

// init initializes the global logger
func init() {
	logger = logrus.DefaultLogger()
	logger.SetLevel(logging.DebugLevel)
}

// stealNIC requests stealing the specified NIC from the STN GRPC server.
func stealNIC(nicName string) (*stn.STNReply, error) {
	logger.Debugf("Stealing the NIC: %s", nicName)

	// connect to STN GRPC server
	conn, err := grpc.Dial(fmt.Sprintf(":%d", *stnServerPort), grpc.WithInsecure())
	if err != nil {
		logger.Errorf("Unable to connect to STN GRPC: %v", err)
		return nil, err
	}
	defer conn.Close()
	c := stn.NewSTNClient(conn)

	// request stealing the interface
	reply, err := c.StealInterface(context.Background(), &stn.STNRequest{
		InterfaceName: nicName,
	})
	if err != nil {
		logger.Errorf("Error by executing STN GRPC: %v", err)
		return nil, err
	}

	logger.Debug(reply)
	return reply, nil
}

// releaseNIC requests reverting the specified NIC to its original state using the STN GRPC server.
func releaseNIC(nicName string) error {
	logger.Debugf("Releasing the NIC: %s", nicName)

	// connect to STN GRPC server
	conn, err := grpc.Dial(fmt.Sprintf(":%d", *stnServerPort), grpc.WithInsecure())
	if err != nil {
		logger.Errorf("Unable to connect to STN GRPC: %v", err)
		return err
	}
	defer conn.Close()
	c := stn.NewSTNClient(conn)

	// request release of the interface
	reply, err := c.ReleaseInterface(context.Background(), &stn.STNRequest{
		InterfaceName: nicName,
	})
	if err != nil {
		logger.Errorf("Error by executing STN GRPC: %v", err)
		return err
	}

	logger.Debug(reply)
	return nil
}

// parseSTNConfig parses the config file and looks up for STN configuration.
// In case that STN was requested for this node, returns the interface to be stealed and optionally its name on VPP.
func parseSTNConfig() (config *contiv.Config, nicToSteal string, vppIfName string, err error) {

	// read config YAML
	yamlFile, err := ioutil.ReadFile(*contivCfgFile)
	if err != nil {
		logger.Errorf("Error by reading config file %s: %v", *contivCfgFile, err)
		return
	}

	// unmarshall the YAML
	config = &contiv.Config{}
	err = yaml.Unmarshal(yamlFile, config)
	if err != nil {
		logger.Errorf("Error by unmarshaling YAML: %v", err)
		return
	}
	if config.TAPInterfaceVersion == 0 {
		config.TAPInterfaceVersion = 1 // default
	}

	// try to find node config and return STN interface name if found
	nodeName := os.Getenv(servicelabel.MicroserviceLabelEnvVar)
	logger.Debugf("Looking for node '%s' specific config", nodeName)

	for _, nc := range config.NodeConfig {
		if nc.NodeName == nodeName {
			logger.Debugf("Found interface to be stealed: %s", nc.StealInterface)
			nicToSteal = nc.StealInterface
			vppIfName = nc.MainVPPInterface.InterfaceName
			return
		}
	}

	return
}

func main() {
	flag.Parse()

	logger.Debugf("Starting contiv-init process")

	// check whether STN is required and get NIC name
	contivCfg, nicToSteal, vppIfName, err := parseSTNConfig()
	if err != nil {
		logger.Errorf("Error by parsing STN config: %v", err)
		os.Exit(-1)
	}

	var stnData *stn.STNReply
	if nicToSteal != "" {
		// steal the NIC
		stnData, err = stealNIC(nicToSteal)
		if err != nil {
			logger.Warnf("Error by stealing the NIC %s: %v", nicToSteal, err)
			// do not fail of STN was not succesfull
			nicToSteal = ""
		}
	} else {
		logger.Debug("STN not requested")
	}

	// connect to supervisor API
	client := supervisor.New("localhost", *supervisorPort, "", "")

	// start VPP
	logger.Debug("Starting VPP")
	_, err = client.StartProcess("vpp", false)
	if err != nil {
		logger.Errorf("Error by starting VPP process: %v", err)
		os.Exit(-1)
	}

	if nicToSteal != "" {
		// configure connectivity on VPP
		vppCfg, err := configureVpp(contivCfg, stnData, vppIfName)
		if err != nil {
			logger.Errorf("Error by configuring VPP: %v", err)
			os.Exit(-1)
		}

		// persist VPP config in ETCD
		err = persistVppConfig(contivCfg, stnData, vppCfg)
		if err != nil {
			logger.Errorf("Error by persisting VPP config in ETCD: %v", err)
			os.Exit(-1)
		}
	}

	// start contiv-agent
	logger.Debugf("Starting contiv-agent")
	_, err = client.StartProcess("contiv-agent", false)
	if err != nil {
		logger.Errorf("Error by starting contiv-agent process: %v", err)
		os.Exit(-1)
	}

	// wait until SIGINT/SIGTERM signal
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	sig := <-sigs
	logger.Debug("%v signal recieved, exiting", sig)

	// request releasing the NIC
	if nicToSteal != "" {
		releaseNIC(nicToSteal)
	}
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gocli"
	"github.com/go-ozzo/ozzo-dbx"
	_ "github.com/go-sql-driver/mysql"
	"github.com/olekukonko/tablewriter"
)

type WhoisAppInfo struct {
	AppId            string `db:"AppId"`
	ApplicationName  string `db:"ApplicationName"`
	ApplicationIntro string `db:"ApplicationIntro"`
	Cluster          string `db:"Cluster"`
	CreateBy         string `db:"CreateBy"`
	CreateTime       string `db:"CreateTime"`
	Status           string `db:"Status"`
}

type WhoisTopicInfo struct {
	AppId      string `db:"AppId"`
	AppName    string
	TopicName  string `db:"TopicName"`
	TopicIntro string `db:"TopicIntro"`
	CreateBy   string `db:"CreateBy"`
	CreateTime string `db:"CreateTime"`
	Status     string `db:"Status"`
}

type WhoisGroupInfo struct {
	AppId      string `db:"AppId"`
	AppName    string
	GroupName  string `db:"GroupName"`
	GroupIntro string `db:"GroupIntro"`
	CreateBy   string `db:"CreateBy"`
	CreateTime string `db:"CreateTime"`
	Status     string `db:"Status"`
}

type Whois struct {
	Ui  cli.Ui
	Cmd string

	zone     string
	app      string
	topic    string
	group    string
	likeMode bool

	appInfos   []WhoisAppInfo
	topicInfos []WhoisTopicInfo
	groupInfos []WhoisGroupInfo
}

func (this *Whois) Run(args []string) (exitCode int) {
	cmdFlags := flag.NewFlagSet("whois", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.StringVar(&this.app, "app", "", "")
	cmdFlags.StringVar(&this.group, "g", "", "")
	cmdFlags.StringVar(&this.topic, "t", "", "")
	cmdFlags.BoolVar(&this.likeMode, "l", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	ensureZoneValid(this.zone)

	mysqlDsns := map[string]string{
		"prod": "user_pubsub:p0nI7mEL6OLW@tcp(m3342.wdds.mysqldb.com:3342)/pubsub?charset=utf8&timeout=10s",
		"sit":  "pubsub:pubsub@tcp(10.209.44.12:10043)/pubsub?charset=utf8&timeout=10s",
		"test": "pubsub:pubsub@tcp(10.209.44.14:10044)/pubsub?charset=utf8&timeout=10s",
	}
	this.loadFromManager(mysqlDsns[this.zone])

	table := tablewriter.NewWriter(os.Stdout)
	switch {
	case this.topic+this.group == "":
		// list apps
		table.SetHeader([]string{"Id", "Name", "Cluster", "Ctime"})
		for _, ai := range this.appInfos {
			table.Append([]string{ai.AppId, ai.ApplicationName, ai.Cluster, ai.CreateTime})
		}

	case this.topic != "":
		table.SetHeader([]string{"topic", "desc", "app", "owner", "ctime"})
		for _, ti := range this.topicInfos {
			table.Append([]string{ti.TopicName, ti.TopicIntro, ti.AppName, ti.CreateBy, ti.CreateTime})
		}

	case this.group != "":
		table.SetHeader([]string{"group", "desc", "app", "owner", "ctime"})
		for _, gi := range this.groupInfos {
			table.Append([]string{gi.GroupName, gi.GroupIntro, gi.AppName, gi.CreateBy, gi.CreateTime})
		}

	case this.topic != "" && this.group != "":
		this.Ui.Error("-t and -g cannot be set at the same time！")
		return 1
	}

	table.Render()

	return
}

func (this *Whois) loadFromManager(dsn string) {
	db, err := dbx.Open("mysql", dsn)
	swallow(err)

	// TODO fetch from topics_version
	sql := "SELECT AppId,ApplicationName,ApplicationIntro,Cluster,CreateBy,CreateTime,Status FROM application"
	if this.app != "" {
		sql += " WHERE AppId IN (" + this.app + ")"
	}
	sql += " ORDER BY AppId"
	q := db.NewQuery(sql)

	swallow(q.All(&this.appInfos))

	op := "="
	if this.likeMode {
		op = "LIKE"
	}
	if this.topic != "" {
		if this.likeMode {
			this.topic = "%" + this.topic + "%"
		}
		sql = fmt.Sprintf("SELECT AppId,TopicName,TopicIntro,CreateBy,CreateTime,Status FROM topics WHERE TopicName %s '%s'",
			op, this.topic)
		q = db.NewQuery(sql)
		swallow(q.All(&this.topicInfos))

		for i, ti := range this.topicInfos {
			this.topicInfos[i].AppName = this.appName(ti.AppId)
		}
	}

	if this.group != "" {
		if this.likeMode {
			this.group = "%" + this.group + "%"
		}
		sql = fmt.Sprintf("SELECT AppId,GroupName,GroupIntro,CreateBy,CreateTime,Status FROM application_group WHERE GroupName %s '%s'",
			op, this.group)
		q = db.NewQuery(sql)
		swallow(q.All(&this.groupInfos))
		for i, gi := range this.groupInfos {
			this.groupInfos[i].AppName = this.appName(gi.AppId)
		}
	}
}

func (this *Whois) appName(appId string) string {
	for _, ai := range this.appInfos {
		if ai.AppId == appId {
			return ai.ApplicationName
		}
	}

	return "NotFound"
}

func (*Whois) Synopsis() string {
	return "Lookup PubSub App Information"
}

func (this *Whois) Help() string {
	help := fmt.Sprintf(`
Usage: %s whois [options]

    Lookup PubSub App Information

Options:

    -z zone

    -app comma seperated appId

    -g group

    -t topic

    -l
      Like mode.
      Pattern wildcard match of group or topic name.

`, this.Cmd)
	return strings.TrimSpace(help)
}
<commit_msg>display app id<commit_after>package command

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gocli"
	"github.com/go-ozzo/ozzo-dbx"
	_ "github.com/go-sql-driver/mysql"
	"github.com/olekukonko/tablewriter"
)

type WhoisAppInfo struct {
	AppId            string `db:"AppId"`
	ApplicationName  string `db:"ApplicationName"`
	ApplicationIntro string `db:"ApplicationIntro"`
	Cluster          string `db:"Cluster"`
	CreateBy         string `db:"CreateBy"`
	CreateTime       string `db:"CreateTime"`
	Status           string `db:"Status"`
}

type WhoisTopicInfo struct {
	AppId      string `db:"AppId"`
	AppName    string
	TopicName  string `db:"TopicName"`
	TopicIntro string `db:"TopicIntro"`
	CreateBy   string `db:"CreateBy"`
	CreateTime string `db:"CreateTime"`
	Status     string `db:"Status"`
}

type WhoisGroupInfo struct {
	AppId      string `db:"AppId"`
	AppName    string
	GroupName  string `db:"GroupName"`
	GroupIntro string `db:"GroupIntro"`
	CreateBy   string `db:"CreateBy"`
	CreateTime string `db:"CreateTime"`
	Status     string `db:"Status"`
}

type Whois struct {
	Ui  cli.Ui
	Cmd string

	zone     string
	app      string
	topic    string
	group    string
	likeMode bool

	appInfos   []WhoisAppInfo
	topicInfos []WhoisTopicInfo
	groupInfos []WhoisGroupInfo
}

func (this *Whois) Run(args []string) (exitCode int) {
	cmdFlags := flag.NewFlagSet("whois", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.StringVar(&this.app, "app", "", "")
	cmdFlags.StringVar(&this.group, "g", "", "")
	cmdFlags.StringVar(&this.topic, "t", "", "")
	cmdFlags.BoolVar(&this.likeMode, "l", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	ensureZoneValid(this.zone)

	mysqlDsns := map[string]string{
		"prod": "user_pubsub:p0nI7mEL6OLW@tcp(m3342.wdds.mysqldb.com:3342)/pubsub?charset=utf8&timeout=10s",
		"sit":  "pubsub:pubsub@tcp(10.209.44.12:10043)/pubsub?charset=utf8&timeout=10s",
		"test": "pubsub:pubsub@tcp(10.209.44.14:10044)/pubsub?charset=utf8&timeout=10s",
	}
	this.loadFromManager(mysqlDsns[this.zone])

	table := tablewriter.NewWriter(os.Stdout)
	switch {
	case this.topic+this.group == "":
		// list apps
		table.SetHeader([]string{"Id", "Name", "Cluster", "Ctime"})
		for _, ai := range this.appInfos {
			table.Append([]string{ai.AppId, ai.ApplicationName, ai.Cluster, ai.CreateTime})
		}

	case this.topic != "":
		table.SetHeader([]string{"topic", "desc", "aid", "app", "owner", "ctime"})
		for _, ti := range this.topicInfos {
			table.Append([]string{ti.TopicName, ti.TopicIntro,
				ti.AppId, ti.AppName, ti.CreateBy, ti.CreateTime})
		}

	case this.group != "":
		table.SetHeader([]string{"group", "desc", "aid", "app", "owner", "ctime"})
		for _, gi := range this.groupInfos {
			table.Append([]string{gi.GroupName, gi.GroupIntro,
				gi.AppId, gi.AppName, gi.CreateBy, gi.CreateTime})
		}

	case this.topic != "" && this.group != "":
		this.Ui.Error("-t and -g cannot be set at the same time！")
		return 1
	}

	table.Render()

	return
}

func (this *Whois) loadFromManager(dsn string) {
	db, err := dbx.Open("mysql", dsn)
	swallow(err)

	// TODO fetch from topics_version
	sql := "SELECT AppId,ApplicationName,ApplicationIntro,Cluster,CreateBy,CreateTime,Status FROM application"
	if this.app != "" {
		sql += " WHERE AppId IN (" + this.app + ")"
	}
	sql += " ORDER BY AppId"
	q := db.NewQuery(sql)

	swallow(q.All(&this.appInfos))

	op := "="
	if this.likeMode {
		op = "LIKE"
	}
	if this.topic != "" {
		if this.likeMode {
			this.topic = "%" + this.topic + "%"
		}
		sql = fmt.Sprintf("SELECT AppId,TopicName,TopicIntro,CreateBy,CreateTime,Status FROM topics WHERE TopicName %s '%s'",
			op, this.topic)
		q = db.NewQuery(sql)
		swallow(q.All(&this.topicInfos))

		for i, ti := range this.topicInfos {
			this.topicInfos[i].AppName = this.appName(ti.AppId)
		}
	}

	if this.group != "" {
		if this.likeMode {
			this.group = "%" + this.group + "%"
		}
		sql = fmt.Sprintf("SELECT AppId,GroupName,GroupIntro,CreateBy,CreateTime,Status FROM application_group WHERE GroupName %s '%s'",
			op, this.group)
		q = db.NewQuery(sql)
		swallow(q.All(&this.groupInfos))
		for i, gi := range this.groupInfos {
			this.groupInfos[i].AppName = this.appName(gi.AppId)
		}
	}
}

func (this *Whois) appName(appId string) string {
	for _, ai := range this.appInfos {
		if ai.AppId == appId {
			return ai.ApplicationName
		}
	}

	return "NotFound"
}

func (*Whois) Synopsis() string {
	return "Lookup PubSub App Information"
}

func (this *Whois) Help() string {
	help := fmt.Sprintf(`
Usage: %s whois [options]

    Lookup PubSub App Information

Options:

    -z zone

    -app comma seperated appId

    -g group

    -t topic

    -l
      Like mode.
      Pattern wildcard match of group or topic name.

`, this.Cmd)
	return strings.TrimSpace(help)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/chromium/hstspreload"
	"github.com/chromium/hstspreload/chromiumpreload"
)

func main() {
	args := os.Args[1:]

	if len(args) < 2 {
		fmt.Printf(`hstspreload is a tool for checking conditions to be added to Chromium 's
HSTS preload list. See hstspreload.appspot.com for more details.

Usage:

  hstspreload command argument

The commands are:

  preloadabledomain (+d) Check the TLS configuration and headers of a domain for
                         preload requirements.
  removabledomain   (-d) Check the headers of a domain for removal requirements.
  preloadableheader (+h) Check an HSTS header for preload requirements
  removableheader   (-h) Check an HSTS header for removal requirements
  status                 Check the preload status of a domain

Examples:

  hstspreload +d wikipedia.org
  hstspreload +h "max-age=10886400; includeSubDomains; preload"
  hstspreload -h "max-age=10886400; includeSubDomains"

Return code:

  0    Passed all checks.
  1    Error (failed at least one requirement).
  2    Had warnings, but passed all requirements.
  3    Invalid commandline arguments
  4    Displayed help

`)
		os.Exit(4)
		return
	}

	var header *string
	var issues hstspreload.Issues

	switch args[0] {
	case "+h", "preloadableheader":
		issues = preloadableHeader(args[1])

	case "-h", "removableheader":
		issues = removableHeader(args[1])

	case "+d", "preloadabledomain":
		header, issues = preloadableDomain(args[1])

	case "-d", "removabledomain":
		header, issues = removableDomain(args[1])

	case "status":
		l, err := chromiumpreload.GetLatest()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err)
		}
		m := chromiumpreload.PreloadEntriesToMap(l)
		fmt.Printf("Status: %v", m[chromiumpreload.Domain(args[1])])
		os.Exit(0)

	default:
		fmt.Printf("Unknown command: %s\n", args[0])
		os.Exit(3)
	}

	// Wrap this in a function to (statically) enforce a return code.
	showResult := func() int {
		if header != nil {
			fmt.Printf("Observed header: %s%s%s\n", bold, *header, reset)
		}

		fmt.Printf("\n")
		switch {
		case len(issues.Errors) > 0:
			return 1

		case len(issues.Warnings) > 0:
			return 2

		default:
			fmt.Printf("%sSatisfies requirements.%s\n\n", green, reset)
			return 0
		}
	}
	exitCode := showResult()

	printList(issues.Errors, "Error", red)
	printList(issues.Warnings, "Warning", yellow)

	os.Exit(exitCode)
}

func preloadableHeader(header string) (issues hstspreload.Issues) {
	expectHeaderOrWarn(header)

	fmt.Printf(
		"Checking header \"%s%s%s\" for preload requirements...\n",
		bold, header, reset)

	return hstspreload.PreloadableHeaderString(header)
}

func removableHeader(header string) (issues hstspreload.Issues) {
	expectHeaderOrWarn(header)

	fmt.Printf(
		"Checking header \"%s%s%s\" for removal requirements...\n",
		bold, header, reset)

	return hstspreload.RemovableHeaderString(header)
}

func preloadableDomain(domain string) (header *string, issues hstspreload.Issues) {
	expectDomainOrExit(domain)

	fmt.Printf(
		"Checking domain %s%s%s for preload requirements...\n",
		underline, domain, reset)

	return hstspreload.PreloadableDomain(domain)
}

func removableDomain(domain string) (header *string, issues hstspreload.Issues) {
	expectDomainOrExit(domain)

	fmt.Printf(
		"Checking domain %s%s%s for removal requirements...\n",
		underline, domain, reset)

	return hstspreload.RemovableDomain(domain)
}

func expectHeaderOrWarn(str string) {
	if probablyURL(str) {
		fmt.Fprintf(os.Stderr,
			"Warning: please supply an HSTS header string (it appears you supplied a URL).\n")
	}
	if probablyDomain(str) {
		fmt.Fprintf(os.Stderr,
			"Warning: please supply an HSTS header string (it appears you supplied a domain).\n")
	}
}

func expectDomainOrExit(str string) {
	if probablyHeader(str) {
		fmt.Fprintf(os.Stderr,
			"Invalid argument: please supply a domain (example.com), not a header string.\n")
		os.Exit(3)
	}

	if probablyURL(str) {
		fmt.Fprintf(os.Stderr,
			"Invalid argument: please supply a domain (example.com) rather than a URL (https://example.com/index.html).\n")
		os.Exit(3)
	}
}

func probablyHeader(str string) bool {
	return strings.Contains(str, ";") || strings.Contains(str, " ")
}

func probablyURL(str string) bool {
	return strings.HasPrefix(str, "http") || strings.Contains(str, ":") || strings.Contains(str, "/")
}

func probablyDomain(str string) bool {
	return strings.Contains(str, ".") && !strings.Contains(str, " ")
}

func printList(list []hstspreload.Issue, title string, fs string) {
	if len(list) == 0 {
		return
	}

	titlePluralized := title
	if len(list) != 1 {
		titlePluralized += "s"
	}
	fmt.Printf("%s%s:%s\n", fs, titlePluralized, reset)

	for i, is := range list {
		fmt.Printf(
			"\n%d. %s%s%s [%s]\n%s\n",
			i+1, fs, is.Summary, reset, is.Code, is.Message)
	}

	fmt.Printf("\n")
}
<commit_msg>cmd/hstspreload: Use more idiomatic names for sanity checks (warnIfNotHeader, mustBeDomain).<commit_after>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/chromium/hstspreload"
	"github.com/chromium/hstspreload/chromiumpreload"
)

func main() {
	args := os.Args[1:]

	if len(args) < 2 {
		fmt.Printf(`hstspreload is a tool for checking conditions to be added to Chromium 's
HSTS preload list. See hstspreload.appspot.com for more details.

Usage:

  hstspreload command argument

The commands are:

  preloadabledomain (+d) Check the TLS configuration and headers of a domain for
                         preload requirements.
  removabledomain   (-d) Check the headers of a domain for removal requirements.
  preloadableheader (+h) Check an HSTS header for preload requirements
  removableheader   (-h) Check an HSTS header for removal requirements
  status                 Check the preload status of a domain

Examples:

  hstspreload +d wikipedia.org
  hstspreload +h "max-age=10886400; includeSubDomains; preload"
  hstspreload -h "max-age=10886400; includeSubDomains"

Return code:

  0    Passed all checks.
  1    Error (failed at least one requirement).
  2    Had warnings, but passed all requirements.
  3    Invalid commandline arguments
  4    Displayed help

`)
		os.Exit(4)
		return
	}

	var header *string
	var issues hstspreload.Issues

	switch args[0] {
	case "+h", "preloadableheader":
		issues = preloadableHeader(args[1])

	case "-h", "removableheader":
		issues = removableHeader(args[1])

	case "+d", "preloadabledomain":
		header, issues = preloadableDomain(args[1])

	case "-d", "removabledomain":
		header, issues = removableDomain(args[1])

	case "status":
		l, err := chromiumpreload.GetLatest()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err)
		}
		m := chromiumpreload.PreloadEntriesToMap(l)
		fmt.Printf("Status: %v", m[chromiumpreload.Domain(args[1])])
		os.Exit(0)

	default:
		fmt.Printf("Unknown command: %s\n", args[0])
		os.Exit(3)
	}

	// Wrap this in a function to (statically) enforce a return code.
	showResult := func() int {
		if header != nil {
			fmt.Printf("Observed header: %s%s%s\n", bold, *header, reset)
		}

		fmt.Printf("\n")
		switch {
		case len(issues.Errors) > 0:
			return 1

		case len(issues.Warnings) > 0:
			return 2

		default:
			fmt.Printf("%sSatisfies requirements.%s\n\n", green, reset)
			return 0
		}
	}
	exitCode := showResult()

	printList(issues.Errors, "Error", red)
	printList(issues.Warnings, "Warning", yellow)

	os.Exit(exitCode)
}

func preloadableHeader(header string) (issues hstspreload.Issues) {
	warnIfNotHeader(header)

	fmt.Printf(
		"Checking header \"%s%s%s\" for preload requirements...\n",
		bold, header, reset)

	return hstspreload.PreloadableHeaderString(header)
}

func removableHeader(header string) (issues hstspreload.Issues) {
	warnIfNotHeader(header)

	fmt.Printf(
		"Checking header \"%s%s%s\" for removal requirements...\n",
		bold, header, reset)

	return hstspreload.RemovableHeaderString(header)
}

func preloadableDomain(domain string) (header *string, issues hstspreload.Issues) {
	mustBeDomain(domain)

	fmt.Printf(
		"Checking domain %s%s%s for preload requirements...\n",
		underline, domain, reset)

	return hstspreload.PreloadableDomain(domain)
}

func removableDomain(domain string) (header *string, issues hstspreload.Issues) {
	mustBeDomain(domain)

	fmt.Printf(
		"Checking domain %s%s%s for removal requirements...\n",
		underline, domain, reset)

	return hstspreload.RemovableDomain(domain)
}

func warnIfNotHeader(str string) {
	if probablyURL(str) {
		fmt.Fprintf(os.Stderr,
			"Warning: please supply an HSTS header string (it appears you supplied a URL).\n")
	}
	if probablyDomain(str) {
		fmt.Fprintf(os.Stderr,
			"Warning: please supply an HSTS header string (it appears you supplied a domain).\n")
	}
}

func mustBeDomain(str string) {
	if probablyHeader(str) {
		fmt.Fprintf(os.Stderr,
			"Invalid argument: please supply a domain (example.com), not a header string.\n")
		os.Exit(3)
	}

	if probablyURL(str) {
		fmt.Fprintf(os.Stderr,
			"Invalid argument: please supply a domain (example.com) rather than a URL (https://example.com/index.html).\n")
		os.Exit(3)
	}
}

func probablyHeader(str string) bool {
	return strings.Contains(str, ";") || strings.Contains(str, " ")
}

func probablyURL(str string) bool {
	return strings.HasPrefix(str, "http") || strings.Contains(str, ":") || strings.Contains(str, "/")
}

func probablyDomain(str string) bool {
	return strings.Contains(str, ".") && !strings.Contains(str, " ")
}

func printList(list []hstspreload.Issue, title string, fs string) {
	if len(list) == 0 {
		return
	}

	titlePluralized := title
	if len(list) != 1 {
		titlePluralized += "s"
	}
	fmt.Printf("%s%s:%s\n", fs, titlePluralized, reset)

	for i, is := range list {
		fmt.Printf(
			"\n%d. %s%s%s [%s]\n%s\n",
			i+1, fs, is.Summary, reset, is.Code, is.Message)
	}

	fmt.Printf("\n")
}
<|endoftext|>


<commit_before>// Copyright 2014, 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package action

import (
	"bytes"
	"fmt"
	"text/tabwriter"

	"github.com/juju/cmd"
	errors "github.com/juju/errors"
	"gopkg.in/juju/names.v2"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/juju/common"
	"github.com/juju/juju/cmd/modelcmd"
)

func NewListCommand() cmd.Command {
	return modelcmd.Wrap(&listCommand{})
}

// listCommand lists actions defined by the charm of a given service.
type listCommand struct {
	ActionCommandBase
	applicationTag names.ApplicationTag
	fullSchema     bool
	out            cmd.Output
}

const listDoc = `
List the actions available to run on the target application, with a short
description.  To show the full schema for the actions, use --schema.

For more information, see also the 'run-ation' command, which executes actions.
`

// Set up the output.
func (c *listCommand) SetFlags(f *gnuflag.FlagSet) {
	c.out.AddFlags(f, "smart", cmd.DefaultFormatters)
	f.BoolVar(&c.fullSchema, "schema", false, "Display the full action schema")
}

func (c *listCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "actions",
		Args:    "<application name>",
		Purpose: "List actions defined for a service.",
		Doc:     listDoc,
		Aliases: []string{"list-actions"},
	}
}

// Init validates the service name and any other options.
func (c *listCommand) Init(args []string) error {
	switch len(args) {
	case 0:
		return errors.New("no application name specified")
	case 1:
		svcName := args[0]
		if !names.IsValidApplication(svcName) {
			return errors.Errorf("invalid application name %q", svcName)
		}
		c.applicationTag = names.NewApplicationTag(svcName)
		return nil
	default:
		return cmd.CheckEmpty(args[1:])
	}
}

// Run grabs the Actions spec from the api.  It then sets up a sensible
// output format for the map.
func (c *listCommand) Run(ctx *cmd.Context) error {
	api, err := c.NewActionAPIClient()
	if err != nil {
		return err
	}
	defer api.Close()

	actions, err := api.ApplicationCharmActions(params.Entity{c.applicationTag.String()})
	if err != nil {
		return err
	}

	if len(actions) == 0 {
		return c.out.Write(ctx, "No actions defined for "+c.applicationTag.Id())
	}

	if c.fullSchema {
		verboseSpecs := make(map[string]interface{})
		for k, v := range actions {
			verboseSpecs[k] = v.Params
		}

		return c.out.Write(ctx, verboseSpecs)
	}

	shortOutput := make(map[string]string)
	var sortedNames []string
	for name, action := range actions {
		shortOutput[name] = action.Description
		if shortOutput[name] == "" {
			shortOutput[name] = "No description"
		}
		sortedNames = append(sortedNames, name)
	}
	sortedNames = common.SortStringsNaturally(sortedNames)
	return c.printTabular(ctx, shortOutput, sortedNames)
}

// printTabular prints the list of actions in tabular format
func (c *listCommand) printTabular(ctx *cmd.Context, actions map[string]string, sortedNames []string) error {
	var out bytes.Buffer
	const (
		// To format things into columns.
		minwidth = 0
		tabwidth = 2
		padding  = 2
		padchar  = ' '
		flags    = 0
	)
	tw := tabwriter.NewWriter(&out, minwidth, tabwidth, padding, padchar, flags)
	for _, name := range sortedNames {
		fmt.Fprintf(tw, "%s\t%s\n", name, actions[name])
	}
	tw.Flush()
	return c.out.Write(ctx, string(out.Bytes()))
}
<commit_msg>add action and description to list-actions and fix newline error<commit_after>// Copyright 2014, 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package action

import (
	"bytes"
	"fmt"
	"strings"
	"text/tabwriter"

	"github.com/juju/cmd"
	errors "github.com/juju/errors"
	"gopkg.in/juju/names.v2"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/juju/common"
	"github.com/juju/juju/cmd/modelcmd"
)

func NewListCommand() cmd.Command {
	return modelcmd.Wrap(&listCommand{})
}

// listCommand lists actions defined by the charm of a given service.
type listCommand struct {
	ActionCommandBase
	applicationTag names.ApplicationTag
	fullSchema     bool
	out            cmd.Output
}

const listDoc = `
List the actions available to run on the target application, with a short
description.  To show the full schema for the actions, use --schema.

For more information, see also the 'run-ation' command, which executes actions.
`

// Set up the output.
func (c *listCommand) SetFlags(f *gnuflag.FlagSet) {
	c.out.AddFlags(f, "smart", cmd.DefaultFormatters)
	f.BoolVar(&c.fullSchema, "schema", false, "Display the full action schema")
}

func (c *listCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "actions",
		Args:    "<application name>",
		Purpose: "List actions defined for a service.",
		Doc:     listDoc,
		Aliases: []string{"list-actions"},
	}
}

// Init validates the service name and any other options.
func (c *listCommand) Init(args []string) error {
	switch len(args) {
	case 0:
		return errors.New("no application name specified")
	case 1:
		svcName := args[0]
		if !names.IsValidApplication(svcName) {
			return errors.Errorf("invalid application name %q", svcName)
		}
		c.applicationTag = names.NewApplicationTag(svcName)
		return nil
	default:
		return cmd.CheckEmpty(args[1:])
	}
}

// Run grabs the Actions spec from the api.  It then sets up a sensible
// output format for the map.
func (c *listCommand) Run(ctx *cmd.Context) error {
	api, err := c.NewActionAPIClient()
	if err != nil {
		return err
	}
	defer api.Close()

	actions, err := api.ApplicationCharmActions(params.Entity{c.applicationTag.String()})
	if err != nil {
		return err
	}

	if len(actions) == 0 {
		return c.out.Write(ctx, "No actions defined for "+c.applicationTag.Id())
	}

	if c.fullSchema {
		verboseSpecs := make(map[string]interface{})
		for k, v := range actions {
			verboseSpecs[k] = v.Params
		}

		return c.out.Write(ctx, verboseSpecs)
	}

	shortOutput := make(map[string]string)
	var sortedNames []string
	for name, action := range actions {
		shortOutput[name] = action.Description
		if shortOutput[name] == "" {
			shortOutput[name] = "No description"
		} else if shortOutput[name] == "\n" {
			break
		}
		sortedNames = append(sortedNames, name)
	}
	sortedNames = common.SortStringsNaturally(sortedNames)
	fmt.Printf("sortedNames\n")
	return c.printTabular(ctx, shortOutput, sortedNames)
}

// printTabular prints the list of actions in tabular format
func (c *listCommand) printTabular(ctx *cmd.Context, actions map[string]string, sortedNames []string) error {
	var out bytes.Buffer
	const (
		// To format things into columns.
		minwidth = 0
		tabwidth = 2
		padding  = 2
		padchar  = ' '
		flags    = 0
	)
	tw := tabwriter.NewWriter(&out, minwidth, tabwidth, padding, padchar, flags)
	fmt.Fprintf(tw, "%s\t%s\n", "ACTION", "DESCRIPTION")
	for _, name := range sortedNames {
		fmt.Fprintf(tw, "%s\t%s\n", name, strings.TrimSpace(actions[name]))
	}
	tw.Flush()
	return c.out.Write(ctx, string(out.Bytes()))
}
<|endoftext|>


<commit_before>/*
Copyright 2020 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 cmd

import (
	"fmt"
	"strings"

	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"k8s.io/release/pkg/anago"
	"k8s.io/release/pkg/github"
	"k8s.io/release/pkg/release"
)

// releaseCmd represents the subcommand for `krel release`
var releaseCmd = &cobra.Command{
	Use:   "release",
	Short: "Release a staged Kubernetes version",
	Long: fmt.Sprintf(`krel release

This subcommand is the second of two necessary steps for cutting Kubernetes
releases. It is intended to be run by users who want to submit a Google Cloud
Build (GCB) job, which does:

1. Check Prerequisites: Verify that a valid %s environment variable is set. It
   also checks for the existence and version of required packages and if
   the correct Google Cloud project is set. A basic hardware check will ensure
   that enough disk space is available, too.

2. Set Build Candidate: Discovers the release branch, parent branch (if
   available) and build version for this release.

3. Prepare Workspace: Verifies that the working directory is in the desired
   state. This means that the staged sources will be downloaded from the bucket
   which should contain a copy of the repository.

4. Push Artifacts: Pushes the generated artifacts to the release bucket and
   Google Container Registry.

5. Push Git Objects: Pushes the new tags and branches to the repository remote
   on GitHub.

6. Announce: Create the release announcement mail and update the GitHub release
   page to contain the artifacts and their checksums.

7. Archive: Copies the release process logs to a bucket and sets private
   permissions on it.
`, github.TokenEnvKey),
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runRelease(releaseOptions)
	},
}

var releaseOptions = anago.DefaultReleaseOptions()

func init() {
	releaseCmd.PersistentFlags().
		StringVar(
			&releaseOptions.ReleaseType,
			"type",
			releaseOptions.ReleaseType,
			fmt.Sprintf("The release type, must be one of: '%s'",
				strings.Join([]string{
					release.ReleaseTypeAlpha,
					release.ReleaseTypeBeta,
					release.ReleaseTypeRC,
					release.ReleaseTypeOfficial,
				}, "', '"),
			))

	releaseCmd.PersistentFlags().
		StringVar(
			&releaseOptions.ReleaseBranch,
			"branch",
			releaseOptions.ReleaseBranch,
			"The release branch for which the release should be build",
		)

	releaseCmd.PersistentFlags().
		StringVar(
			&releaseOptions.BuildVersion,
			buildVersionFlag,
			"",
			"The build version to be released.",
		)

	releaseCmd.PersistentFlags().
		BoolVar(
			&submitJob,
			submitJobFlag,
			true,
			"Submit a Google Cloud Build job",
		)

	releaseCmd.PersistentFlags().
		BoolVar(
			&stream,
			streamFlag,
			false,
			"Run the Google Cloud Build job synchronously",
		)

	if err := releaseCmd.PersistentFlags().MarkHidden(submitJobFlag); err != nil {
		logrus.Fatal(err)
	}

	if err := releaseCmd.MarkPersistentFlagRequired(buildVersionFlag); err != nil {
		logrus.Fatal(err)
	}

	rootCmd.AddCommand(releaseCmd)
}

func runRelease(options *anago.ReleaseOptions) error {
	options.NoMock = rootOpts.nomock
	rel := anago.NewRelease(options)
	if submitJob {
		return rel.Submit(stream)
	}
	return rel.Run()
}
<commit_msg>Perform local options validation before submitting GCB job<commit_after>/*
Copyright 2020 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 cmd

import (
	"fmt"
	"strings"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"k8s.io/release/pkg/anago"
	"k8s.io/release/pkg/github"
	"k8s.io/release/pkg/release"
)

// releaseCmd represents the subcommand for `krel release`
var releaseCmd = &cobra.Command{
	Use:   "release",
	Short: "Release a staged Kubernetes version",
	Long: fmt.Sprintf(`krel release

This subcommand is the second of two necessary steps for cutting Kubernetes
releases. It is intended to be run by users who want to submit a Google Cloud
Build (GCB) job, which does:

1. Check Prerequisites: Verify that a valid %s environment variable is set. It
   also checks for the existence and version of required packages and if
   the correct Google Cloud project is set. A basic hardware check will ensure
   that enough disk space is available, too.

2. Set Build Candidate: Discovers the release branch, parent branch (if
   available) and build version for this release.

3. Prepare Workspace: Verifies that the working directory is in the desired
   state. This means that the staged sources will be downloaded from the bucket
   which should contain a copy of the repository.

4. Push Artifacts: Pushes the generated artifacts to the release bucket and
   Google Container Registry.

5. Push Git Objects: Pushes the new tags and branches to the repository remote
   on GitHub.

6. Announce: Create the release announcement mail and update the GitHub release
   page to contain the artifacts and their checksums.

7. Archive: Copies the release process logs to a bucket and sets private
   permissions on it.
`, github.TokenEnvKey),
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runRelease(releaseOptions)
	},
}

var releaseOptions = anago.DefaultReleaseOptions()

func init() {
	releaseCmd.PersistentFlags().
		StringVar(
			&releaseOptions.ReleaseType,
			"type",
			releaseOptions.ReleaseType,
			fmt.Sprintf("The release type, must be one of: '%s'",
				strings.Join([]string{
					release.ReleaseTypeAlpha,
					release.ReleaseTypeBeta,
					release.ReleaseTypeRC,
					release.ReleaseTypeOfficial,
				}, "', '"),
			))

	releaseCmd.PersistentFlags().
		StringVar(
			&releaseOptions.ReleaseBranch,
			"branch",
			releaseOptions.ReleaseBranch,
			"The release branch for which the release should be build",
		)

	releaseCmd.PersistentFlags().
		StringVar(
			&releaseOptions.BuildVersion,
			buildVersionFlag,
			"",
			"The build version to be released.",
		)

	releaseCmd.PersistentFlags().
		BoolVar(
			&submitJob,
			submitJobFlag,
			true,
			"Submit a Google Cloud Build job",
		)

	releaseCmd.PersistentFlags().
		BoolVar(
			&stream,
			streamFlag,
			false,
			"Run the Google Cloud Build job synchronously",
		)

	if err := releaseCmd.PersistentFlags().MarkHidden(submitJobFlag); err != nil {
		logrus.Fatal(err)
	}

	if err := releaseCmd.MarkPersistentFlagRequired(buildVersionFlag); err != nil {
		logrus.Fatal(err)
	}

	rootCmd.AddCommand(releaseCmd)
}

func runRelease(options *anago.ReleaseOptions) error {
	options.NoMock = rootOpts.nomock
	rel := anago.NewRelease(options)

	if submitJob {
		// Perform a local check of the specified options
		// before launching a Cloud Build job:
		if err := options.Validate(&anago.State{}); err != nil {
			return errors.Wrap(err, "prechecking release options")
		}
		return rel.Submit(stream)
	}
	return rel.Run()
}
<|endoftext|>


<commit_before>package main

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

	"github.com/Sirupsen/logrus"
	"github.com/opencontainers/ocitools/cmd/runtimetest/mount"
	rspec "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/syndtr/gocapability/capability"
	"github.com/urfave/cli"
)

// PR_GET_NO_NEW_PRIVS isn't exposed in Golang so we define it ourselves copying the value from
// the kernel
const PR_GET_NO_NEW_PRIVS = 39

var (
	defaultFS = map[string]string{
		"/proc":    "proc",
		"/sys":     "sysfs",
		"/dev/pts": "devpts",
		"/dev/shm": "tmpfs",
	}

	defaultDevices = []string{
		"/dev/null",
		"/dev/zero",
		"/dev/full",
		"/dev/random",
		"/dev/urandom",
		"/dev/tty",
		"/dev/console",
		"/dev/ptmx",
	}
)

type validation func(*rspec.Spec) error

func loadSpecConfig() (spec *rspec.Spec, err error) {
	cPath := "config.json"
	cf, err := os.Open(cPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("config.json not found")
		}
	}
	defer cf.Close()

	if err = json.NewDecoder(cf).Decode(&spec); err != nil {
		return
	}
	return spec, nil
}

func validateProcess(spec *rspec.Spec) error {
	logrus.Debugf("validating container process")
	uid := os.Getuid()
	if uint32(uid) != spec.Process.User.UID {
		return fmt.Errorf("UID expected: %v, actual: %v", spec.Process.User.UID, uid)
	}
	gid := os.Getgid()
	if uint32(gid) != spec.Process.User.GID {
		return fmt.Errorf("GID expected: %v, actual: %v", spec.Process.User.GID, gid)
	}

	groups, err := os.Getgroups()
	if err != nil {
		return err
	}

	groupsMap := make(map[int]bool)
	for _, g := range groups {
		groupsMap[g] = true
	}

	for _, g := range spec.Process.User.AdditionalGids {
		if !groupsMap[int(g)] {
			return fmt.Errorf("Groups expected: %v, actual (should be superset): %v", spec.Process.User.AdditionalGids, groups)
		}
	}

	if spec.Process.Cwd != "" {
		cwd, err := os.Getwd()
		if err != nil {
			return err
		}
		if cwd != spec.Process.Cwd {
			return fmt.Errorf("Cwd expected: %v, actual: %v", spec.Process.Cwd, cwd)
		}
	}

	cmdlineBytes, err := ioutil.ReadFile("/proc/1/cmdline")
	if err != nil {
		return err
	}

	args := bytes.Split(bytes.Trim(cmdlineBytes, "\x00"), []byte("\x00"))
	if len(args) != len(spec.Process.Args) {
		return fmt.Errorf("Process arguments expected: %v, actual: %v", len(spec.Process.Args), len(args))
	}
	for i, a := range args {
		if string(a) != spec.Process.Args[i] {
			return fmt.Errorf("Process arguments expected: %v, actual: %v", string(a), spec.Process.Args[i])
		}
	}

	for _, env := range spec.Process.Env {
		parts := strings.Split(env, "=")
		key := parts[0]
		expectedValue := parts[1]
		actualValue := os.Getenv(key)
		if actualValue != expectedValue {
			return fmt.Errorf("Env %v expected: %v, actual: %v", key, expectedValue, actualValue)
		}
	}

	ret, _, errno := syscall.Syscall6(syscall.SYS_PRCTL, PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0)
	if errno != 0 {
		return errno
	}
	if spec.Process.NoNewPrivileges && ret != 1 {
		return fmt.Errorf("NoNewPrivileges expected: true, actual: false")
	}
	if !spec.Process.NoNewPrivileges && ret != 0 {
		return fmt.Errorf("NoNewPrivileges expected: false, actual: true")
	}

	return nil
}

func validateCapabilities(spec *rspec.Spec) error {
	logrus.Debugf("validating capabilities")

	last := capability.CAP_LAST_CAP
	// workaround for RHEL6 which has no /proc/sys/kernel/cap_last_cap
	if last == capability.Cap(63) {
		last = capability.CAP_BLOCK_SUSPEND
	}

	processCaps, err := capability.NewPid(1)
	if err != nil {
		return err
	}

	expectedCaps := make(map[string]bool)
	for _, ec := range spec.Process.Capabilities {
		expectedCaps[ec] = true
	}

	for _, cap := range capability.List() {
		if cap > last {
			continue
		}

		capKey := fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))
		expectedSet := expectedCaps[capKey]
		actuallySet := processCaps.Get(capability.EFFECTIVE, cap)
		if expectedSet != actuallySet {
			if expectedSet {
				return fmt.Errorf("Expected Capability %v not set for process", cap.String())
			}
			return fmt.Errorf("Unexpected Capability %v set for process", cap.String())
		}
	}

	return nil
}

func validateHostname(spec *rspec.Spec) error {
	logrus.Debugf("validating hostname")
	hostname, err := os.Hostname()
	if err != nil {
		return err
	}
	if spec.Hostname != "" && hostname != spec.Hostname {
		return fmt.Errorf("Hostname expected: %v, actual: %v", spec.Hostname, hostname)
	}
	return nil
}

func validateRlimits(spec *rspec.Spec) error {
	logrus.Debugf("validating rlimits")
	for _, r := range spec.Process.Rlimits {
		rl, err := strToRlimit(r.Type)
		if err != nil {
			return err
		}

		var rlimit syscall.Rlimit
		if err := syscall.Getrlimit(rl, &rlimit); err != nil {
			return err
		}

		if rlimit.Cur != r.Soft {
			return fmt.Errorf("%v rlimit soft expected: %v, actual: %v", r.Type, r.Soft, rlimit.Cur)
		}
		if rlimit.Max != r.Hard {
			return fmt.Errorf("%v rlimit hard expected: %v, actual: %v", r.Type, r.Hard, rlimit.Max)
		}
	}
	return nil
}

func validateSysctls(spec *rspec.Spec) error {
	logrus.Debugf("validating sysctls")
	for k, v := range spec.Linux.Sysctl {
		keyPath := filepath.Join("/proc/sys", strings.Replace(k, ".", "/", -1))
		vBytes, err := ioutil.ReadFile(keyPath)
		if err != nil {
			return err
		}
		value := strings.TrimSpace(string(bytes.Trim(vBytes, "\x00")))
		if value != v {
			return fmt.Errorf("Sysctl %v value expected: %v, actual: %v", k, v, value)
		}
	}
	return nil
}

func testWriteAccess(path string) error {
	tmpfile, err := ioutil.TempFile(path, "Test")
	if err != nil {
		return err
	}

	tmpfile.Close()
	os.RemoveAll(filepath.Join(path, tmpfile.Name()))

	return nil
}

func validateRootFS(spec *rspec.Spec) error {
	logrus.Debugf("validating root filesystem")
	if spec.Root.Readonly {
		err := testWriteAccess("/")
		if err == nil {
			return fmt.Errorf("Rootfs should be readonly")
		}
	}

	return nil
}

func validateDefaultFS(spec *rspec.Spec) error {
	logrus.Debugf("validating linux default filesystem")

	mountInfos, err := mount.GetMounts()
	if err != nil {
		return err
	}

	mountsMap := make(map[string]string)
	for _, mountInfo := range mountInfos {
		mountsMap[mountInfo.Mountpoint] = mountInfo.Fstype
	}

	for fs, fstype := range defaultFS {
		if !(mountsMap[fs] == fstype) {
			return fmt.Errorf("%v must exist and expected type is %v", fs, fstype)
		}
	}

	return nil
}

func validateDefaultDevices(spec *rspec.Spec) error {
	logrus.Debugf("validating linux default devices")

	for _, device := range defaultDevices {
		fi, err := os.Stat(device)
		if err != nil {
			if os.IsNotExist(err) {
				return fmt.Errorf("device node %v not found", device)
			}
			return err
		} else {
			if fi.Mode()&os.ModeDevice != os.ModeDevice {
				return fmt.Errorf("file %v is not a device as expected", device)
			}
		}
	}

	return nil
}

func validateMaskedPaths(spec *rspec.Spec) error {
	logrus.Debugf("validating maskedPaths")
	for _, maskedPath := range spec.Linux.MaskedPaths {
		f, err := os.Open(maskedPath)
		if err != nil {
			return err
		}
		defer f.Close()
		b := make([]byte, 1)
		_, err = f.Read(b)
		if err != io.EOF {
			return fmt.Errorf("%v should not be readable", maskedPath)
		}
	}
	return nil
}

func validateROPaths(spec *rspec.Spec) error {
	logrus.Debugf("validating readonlyPaths")
	for _, v := range spec.Linux.ReadonlyPaths {
		err := testWriteAccess(v)
		if err == nil {
			return fmt.Errorf("%v should be readonly", v)
		}
	}

	return nil
}

func mountMatch(specMount rspec.Mount, sysMount rspec.Mount) error {
	if specMount.Destination != sysMount.Destination {
		return fmt.Errorf("mount destination expected: %v, actual: %v", specMount.Destination, sysMount.Destination)
	}

	if specMount.Type != sysMount.Type {
		return fmt.Errorf("mount %v type expected: %v, actual: %v", specMount.Destination, specMount.Type, sysMount.Type)
	}

	if specMount.Source != sysMount.Source {
		return fmt.Errorf("mount %v source expected: %v, actual: %v", specMount.Destination, specMount.Source, sysMount.Source)
	}

	return nil
}

func validateMountsExist(spec *rspec.Spec) error {
	logrus.Debugf("validating mounts exist")

	mountInfos, err := mount.GetMounts()
	if err != nil {
		return err
	}

	mountsMap := make(map[string][]rspec.Mount)
	for _, mountInfo := range mountInfos {
		m := rspec.Mount{
			Destination: mountInfo.Mountpoint,
			Type:        mountInfo.Fstype,
			Source:      mountInfo.Source,
		}
		mountsMap[mountInfo.Mountpoint] = append(mountsMap[mountInfo.Mountpoint], m)
	}

	for _, specMount := range spec.Mounts {
		found := false
		for _, sysMount := range mountsMap[specMount.Destination] {
			if err := mountMatch(specMount, sysMount); err == nil {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("Expected mount %v does not exist", specMount)
		}
	}

	return nil
}

func validate(context *cli.Context) error {
	logLevelString := context.String("log-level")
	logLevel, err := logrus.ParseLevel(logLevelString)
	if err != nil {
		return err
	}
	logrus.SetLevel(logLevel)

	spec, err := loadSpecConfig()
	if err != nil {
		return err
	}

	defaultValidations := []validation{
		validateRootFS,
		validateProcess,
		validateCapabilities,
		validateHostname,
		validateRlimits,
		validateMountsExist,
	}

	linuxValidations := []validation{
		validateDefaultFS,
		validateDefaultDevices,
		validateSysctls,
		validateMaskedPaths,
		validateROPaths,
	}

	for _, v := range defaultValidations {
		if err := v(spec); err != nil {
			return err
		}
	}

	if spec.Platform.OS == "linux" {
		for _, v := range linuxValidations {
			if err := v(spec); err != nil {
				return err
			}
		}
	}

	return nil
}

func main() {
	app := cli.NewApp()
	app.Name = "runtimetest"
	app.Version = "0.0.1"
	app.Usage = "Compare the environment with an OCI configuration"
	app.UsageText = "runtimetest [options]"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "log-level",
			Value: "error",
			Usage: "Log level (panic, fatal, error, warn, info, or debug)",
		},
	}

	app.Action = validate
	if err := app.Run(os.Args); err != nil {
		logrus.Fatal(err)
	}
}
<commit_msg>runtimetest: remove unnecessary else<commit_after>package main

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

	"github.com/Sirupsen/logrus"
	"github.com/opencontainers/ocitools/cmd/runtimetest/mount"
	rspec "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/syndtr/gocapability/capability"
	"github.com/urfave/cli"
)

// PR_GET_NO_NEW_PRIVS isn't exposed in Golang so we define it ourselves copying the value from
// the kernel
const PR_GET_NO_NEW_PRIVS = 39

var (
	defaultFS = map[string]string{
		"/proc":    "proc",
		"/sys":     "sysfs",
		"/dev/pts": "devpts",
		"/dev/shm": "tmpfs",
	}

	defaultDevices = []string{
		"/dev/null",
		"/dev/zero",
		"/dev/full",
		"/dev/random",
		"/dev/urandom",
		"/dev/tty",
		"/dev/console",
		"/dev/ptmx",
	}
)

type validation func(*rspec.Spec) error

func loadSpecConfig() (spec *rspec.Spec, err error) {
	cPath := "config.json"
	cf, err := os.Open(cPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("config.json not found")
		}
	}
	defer cf.Close()

	if err = json.NewDecoder(cf).Decode(&spec); err != nil {
		return
	}
	return spec, nil
}

func validateProcess(spec *rspec.Spec) error {
	logrus.Debugf("validating container process")
	uid := os.Getuid()
	if uint32(uid) != spec.Process.User.UID {
		return fmt.Errorf("UID expected: %v, actual: %v", spec.Process.User.UID, uid)
	}
	gid := os.Getgid()
	if uint32(gid) != spec.Process.User.GID {
		return fmt.Errorf("GID expected: %v, actual: %v", spec.Process.User.GID, gid)
	}

	groups, err := os.Getgroups()
	if err != nil {
		return err
	}

	groupsMap := make(map[int]bool)
	for _, g := range groups {
		groupsMap[g] = true
	}

	for _, g := range spec.Process.User.AdditionalGids {
		if !groupsMap[int(g)] {
			return fmt.Errorf("Groups expected: %v, actual (should be superset): %v", spec.Process.User.AdditionalGids, groups)
		}
	}

	if spec.Process.Cwd != "" {
		cwd, err := os.Getwd()
		if err != nil {
			return err
		}
		if cwd != spec.Process.Cwd {
			return fmt.Errorf("Cwd expected: %v, actual: %v", spec.Process.Cwd, cwd)
		}
	}

	cmdlineBytes, err := ioutil.ReadFile("/proc/1/cmdline")
	if err != nil {
		return err
	}

	args := bytes.Split(bytes.Trim(cmdlineBytes, "\x00"), []byte("\x00"))
	if len(args) != len(spec.Process.Args) {
		return fmt.Errorf("Process arguments expected: %v, actual: %v", len(spec.Process.Args), len(args))
	}
	for i, a := range args {
		if string(a) != spec.Process.Args[i] {
			return fmt.Errorf("Process arguments expected: %v, actual: %v", string(a), spec.Process.Args[i])
		}
	}

	for _, env := range spec.Process.Env {
		parts := strings.Split(env, "=")
		key := parts[0]
		expectedValue := parts[1]
		actualValue := os.Getenv(key)
		if actualValue != expectedValue {
			return fmt.Errorf("Env %v expected: %v, actual: %v", key, expectedValue, actualValue)
		}
	}

	ret, _, errno := syscall.Syscall6(syscall.SYS_PRCTL, PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0)
	if errno != 0 {
		return errno
	}
	if spec.Process.NoNewPrivileges && ret != 1 {
		return fmt.Errorf("NoNewPrivileges expected: true, actual: false")
	}
	if !spec.Process.NoNewPrivileges && ret != 0 {
		return fmt.Errorf("NoNewPrivileges expected: false, actual: true")
	}

	return nil
}

func validateCapabilities(spec *rspec.Spec) error {
	logrus.Debugf("validating capabilities")

	last := capability.CAP_LAST_CAP
	// workaround for RHEL6 which has no /proc/sys/kernel/cap_last_cap
	if last == capability.Cap(63) {
		last = capability.CAP_BLOCK_SUSPEND
	}

	processCaps, err := capability.NewPid(1)
	if err != nil {
		return err
	}

	expectedCaps := make(map[string]bool)
	for _, ec := range spec.Process.Capabilities {
		expectedCaps[ec] = true
	}

	for _, cap := range capability.List() {
		if cap > last {
			continue
		}

		capKey := fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))
		expectedSet := expectedCaps[capKey]
		actuallySet := processCaps.Get(capability.EFFECTIVE, cap)
		if expectedSet != actuallySet {
			if expectedSet {
				return fmt.Errorf("Expected Capability %v not set for process", cap.String())
			}
			return fmt.Errorf("Unexpected Capability %v set for process", cap.String())
		}
	}

	return nil
}

func validateHostname(spec *rspec.Spec) error {
	logrus.Debugf("validating hostname")
	hostname, err := os.Hostname()
	if err != nil {
		return err
	}
	if spec.Hostname != "" && hostname != spec.Hostname {
		return fmt.Errorf("Hostname expected: %v, actual: %v", spec.Hostname, hostname)
	}
	return nil
}

func validateRlimits(spec *rspec.Spec) error {
	logrus.Debugf("validating rlimits")
	for _, r := range spec.Process.Rlimits {
		rl, err := strToRlimit(r.Type)
		if err != nil {
			return err
		}

		var rlimit syscall.Rlimit
		if err := syscall.Getrlimit(rl, &rlimit); err != nil {
			return err
		}

		if rlimit.Cur != r.Soft {
			return fmt.Errorf("%v rlimit soft expected: %v, actual: %v", r.Type, r.Soft, rlimit.Cur)
		}
		if rlimit.Max != r.Hard {
			return fmt.Errorf("%v rlimit hard expected: %v, actual: %v", r.Type, r.Hard, rlimit.Max)
		}
	}
	return nil
}

func validateSysctls(spec *rspec.Spec) error {
	logrus.Debugf("validating sysctls")
	for k, v := range spec.Linux.Sysctl {
		keyPath := filepath.Join("/proc/sys", strings.Replace(k, ".", "/", -1))
		vBytes, err := ioutil.ReadFile(keyPath)
		if err != nil {
			return err
		}
		value := strings.TrimSpace(string(bytes.Trim(vBytes, "\x00")))
		if value != v {
			return fmt.Errorf("Sysctl %v value expected: %v, actual: %v", k, v, value)
		}
	}
	return nil
}

func testWriteAccess(path string) error {
	tmpfile, err := ioutil.TempFile(path, "Test")
	if err != nil {
		return err
	}

	tmpfile.Close()
	os.RemoveAll(filepath.Join(path, tmpfile.Name()))

	return nil
}

func validateRootFS(spec *rspec.Spec) error {
	logrus.Debugf("validating root filesystem")
	if spec.Root.Readonly {
		err := testWriteAccess("/")
		if err == nil {
			return fmt.Errorf("Rootfs should be readonly")
		}
	}

	return nil
}

func validateDefaultFS(spec *rspec.Spec) error {
	logrus.Debugf("validating linux default filesystem")

	mountInfos, err := mount.GetMounts()
	if err != nil {
		return err
	}

	mountsMap := make(map[string]string)
	for _, mountInfo := range mountInfos {
		mountsMap[mountInfo.Mountpoint] = mountInfo.Fstype
	}

	for fs, fstype := range defaultFS {
		if !(mountsMap[fs] == fstype) {
			return fmt.Errorf("%v must exist and expected type is %v", fs, fstype)
		}
	}

	return nil
}

func validateDefaultDevices(spec *rspec.Spec) error {
	logrus.Debugf("validating linux default devices")

	for _, device := range defaultDevices {
		fi, err := os.Stat(device)
		if err != nil {
			if os.IsNotExist(err) {
				return fmt.Errorf("device node %v not found", device)
			}
			return err
		}
		if fi.Mode()&os.ModeDevice != os.ModeDevice {
			return fmt.Errorf("file %v is not a device as expected", device)
		}
	}

	return nil
}

func validateMaskedPaths(spec *rspec.Spec) error {
	logrus.Debugf("validating maskedPaths")
	for _, maskedPath := range spec.Linux.MaskedPaths {
		f, err := os.Open(maskedPath)
		if err != nil {
			return err
		}
		defer f.Close()
		b := make([]byte, 1)
		_, err = f.Read(b)
		if err != io.EOF {
			return fmt.Errorf("%v should not be readable", maskedPath)
		}
	}
	return nil
}

func validateROPaths(spec *rspec.Spec) error {
	logrus.Debugf("validating readonlyPaths")
	for _, v := range spec.Linux.ReadonlyPaths {
		err := testWriteAccess(v)
		if err == nil {
			return fmt.Errorf("%v should be readonly", v)
		}
	}

	return nil
}

func mountMatch(specMount rspec.Mount, sysMount rspec.Mount) error {
	if specMount.Destination != sysMount.Destination {
		return fmt.Errorf("mount destination expected: %v, actual: %v", specMount.Destination, sysMount.Destination)
	}

	if specMount.Type != sysMount.Type {
		return fmt.Errorf("mount %v type expected: %v, actual: %v", specMount.Destination, specMount.Type, sysMount.Type)
	}

	if specMount.Source != sysMount.Source {
		return fmt.Errorf("mount %v source expected: %v, actual: %v", specMount.Destination, specMount.Source, sysMount.Source)
	}

	return nil
}

func validateMountsExist(spec *rspec.Spec) error {
	logrus.Debugf("validating mounts exist")

	mountInfos, err := mount.GetMounts()
	if err != nil {
		return err
	}

	mountsMap := make(map[string][]rspec.Mount)
	for _, mountInfo := range mountInfos {
		m := rspec.Mount{
			Destination: mountInfo.Mountpoint,
			Type:        mountInfo.Fstype,
			Source:      mountInfo.Source,
		}
		mountsMap[mountInfo.Mountpoint] = append(mountsMap[mountInfo.Mountpoint], m)
	}

	for _, specMount := range spec.Mounts {
		found := false
		for _, sysMount := range mountsMap[specMount.Destination] {
			if err := mountMatch(specMount, sysMount); err == nil {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("Expected mount %v does not exist", specMount)
		}
	}

	return nil
}

func validate(context *cli.Context) error {
	logLevelString := context.String("log-level")
	logLevel, err := logrus.ParseLevel(logLevelString)
	if err != nil {
		return err
	}
	logrus.SetLevel(logLevel)

	spec, err := loadSpecConfig()
	if err != nil {
		return err
	}

	defaultValidations := []validation{
		validateRootFS,
		validateProcess,
		validateCapabilities,
		validateHostname,
		validateRlimits,
		validateMountsExist,
	}

	linuxValidations := []validation{
		validateDefaultFS,
		validateDefaultDevices,
		validateSysctls,
		validateMaskedPaths,
		validateROPaths,
	}

	for _, v := range defaultValidations {
		if err := v(spec); err != nil {
			return err
		}
	}

	if spec.Platform.OS == "linux" {
		for _, v := range linuxValidations {
			if err := v(spec); err != nil {
				return err
			}
		}
	}

	return nil
}

func main() {
	app := cli.NewApp()
	app.Name = "runtimetest"
	app.Version = "0.0.1"
	app.Usage = "Compare the environment with an OCI configuration"
	app.UsageText = "runtimetest [options]"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "log-level",
			Value: "error",
			Usage: "Log level (panic, fatal, error, warn, info, or debug)",
		},
	}

	app.Action = validate
	if err := app.Run(os.Args); err != nil {
		logrus.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/nelhage/taktician/canonicalize"
	"github.com/nelhage/taktician/logs"
	"github.com/nelhage/taktician/ptn"
	"github.com/nelhage/taktician/tak"
)

var (
	logDir = flag.String("games", "games", "game log directory")
)

var (
	size      = flag.Int("size", 5, "what size to analyze")
	minRating = flag.Int("rating", 1600, "minimum rating to consider")
	minCount  = flag.Int("count", 100, "render games with >= [this many] moves")
	maxDepth  = flag.Int("depth", 8, "track tree to this many plies")
)

func main() {
	flag.Parse()

	repo, e := logs.Open(flag.Arg(0))
	if e != nil {
		log.Fatalf("parse %s: %v", flag.Arg(0), e)
	}
	defer repo.Close()
	sql := repo.DB()

	rows, e := sql.Query(
		`
SELECT day, id
FROM games g, rankings r1, rankings r2
WHERE r1.name = g.player1
 AND r2.name = g.player2
 AND r1.rating >= ?
 AND r2.rating >= ?
 AND g.size = ?
`, *minRating, *minRating, *size)
	defer rows.Close()

	tree := &tree{}

	for rows.Next() {
		var day string
		var id int
		e = rows.Scan(&day, &id)
		if e != nil {
			panic(e)
		}

		ptnPath := path.Join(*logDir, day, fmt.Sprintf("%d.ptn", id))
		g, e := ptn.ParseFile(ptnPath)
		if e != nil {
			log.Printf("parse %s: %v", ptnPath, e)
			continue
		}
		p, e := g.PositionAtMove(0, tak.NoColor)
		if e != nil {
			log.Printf("parse %s: %v", ptnPath, e)
			continue
		}

		var ms []tak.Move
		for _, o := range g.Ops {
			if m, ok := o.(*ptn.Move); ok {
				ms = append(ms, m.Move)
				if len(ms) >= 10 {
					break
				}
			}
		}
		ms, e = canonicalize.Canonical(p.Size(), ms)
		if e != nil {
			log.Printf("%s: %v", ptnPath, e)
			continue
		}

		if len(ms) > *maxDepth {
			ms = ms[:*maxDepth]
		}

		result := ptn.Result{Result: g.FindTag("Result")}

		insertTree(tree, ms, result.Winner())
	}

	bs, _ := json.Marshal(tree)
	ioutil.WriteFile("gametree.json", bs, 0644)
	f, e := os.Create("gametree.dot")
	defer f.Close()
	writeTree(f, tree)
}

type tree struct {
	id int

	Move     string  `json:",omitempty"`
	Children []*tree `json:",omitempty"`
	Count    int
	White    int
	Black    int
}

var nextID = 1

func insertTree(t *tree, ms []tak.Move, winner tak.Color) {
	t.Count++
	switch winner {
	case tak.White:
		t.White++
	case tak.Black:
		t.Black++
	}
	if len(ms) == 0 {
		return
	}
	var child *tree
	m := ptn.FormatMove(&ms[0])
	for _, ch := range t.Children {
		if ch.Move == m {
			child = ch
			break
		}
	}
	if child == nil {
		child = &tree{Move: m, id: nextID}
		nextID++
		t.Children = append(t.Children, child)
	}
	insertTree(child, ms[1:], winner)
}

func writeTree(f io.Writer, t *tree) {
	fmt.Fprintf(f, "digraph G {\n")
	writeTreeNode(0, f, t)
	fmt.Fprintf(f, "}\n")
}

func writeTreeNode(ply int, f io.Writer, t *tree) {
	var mno string
	if ply > 0 {
		move := (ply + 1) / 2
		if ply%2 == 1 {
			mno = fmt.Sprintf("%d. ", move)
		} else {
			mno = fmt.Sprintf("%d. .. ", move)
		}
	}
	fmt.Fprintf(f, `  n%d [shape=box, label="%s %d-%d/%0.2f%%"]`,
		t.id, t.Move, t.White, t.Black, 100*float64(t.White)/float64(t.Count))
	fmt.Fprintln(f)
	for _, ch := range t.Children {
		if ch.Count < *minCount {
			continue
		}
		fmt.Fprintf(f, `  n%d -> n%d [label="%s%s %d/%0.2f%%"]`,
			t.id, ch.id, mno, ch.Move,
			ch.Count, 100*float64(ch.Count)/float64(t.Count))
		fmt.Fprintln(f)
		writeTreeNode(ply+1, f, ch)
	}
}
<commit_msg>fix opening ply notation<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/nelhage/taktician/canonicalize"
	"github.com/nelhage/taktician/logs"
	"github.com/nelhage/taktician/ptn"
	"github.com/nelhage/taktician/tak"
)

var (
	logDir = flag.String("games", "games", "game log directory")
)

var (
	size      = flag.Int("size", 5, "what size to analyze")
	minRating = flag.Int("rating", 1600, "minimum rating to consider")
	minCount  = flag.Int("count", 100, "render games with >= [this many] moves")
	maxDepth  = flag.Int("depth", 8, "track tree to this many plies")
)

func main() {
	flag.Parse()

	repo, e := logs.Open(flag.Arg(0))
	if e != nil {
		log.Fatalf("parse %s: %v", flag.Arg(0), e)
	}
	defer repo.Close()
	sql := repo.DB()

	rows, e := sql.Query(
		`
SELECT day, id
FROM games g, rankings r1, rankings r2
WHERE r1.name = g.player1
 AND r2.name = g.player2
 AND r1.rating >= ?
 AND r2.rating >= ?
 AND g.size = ?
`, *minRating, *minRating, *size)
	defer rows.Close()

	tree := &tree{}

	for rows.Next() {
		var day string
		var id int
		e = rows.Scan(&day, &id)
		if e != nil {
			panic(e)
		}

		ptnPath := path.Join(*logDir, day, fmt.Sprintf("%d.ptn", id))
		g, e := ptn.ParseFile(ptnPath)
		if e != nil {
			log.Printf("parse %s: %v", ptnPath, e)
			continue
		}
		p, e := g.PositionAtMove(0, tak.NoColor)
		if e != nil {
			log.Printf("parse %s: %v", ptnPath, e)
			continue
		}

		var ms []tak.Move
		for _, o := range g.Ops {
			if m, ok := o.(*ptn.Move); ok {
				ms = append(ms, m.Move)
				if len(ms) >= 10 {
					break
				}
			}
		}
		ms, e = canonicalize.Canonical(p.Size(), ms)
		if e != nil {
			log.Printf("%s: %v", ptnPath, e)
			continue
		}

		if len(ms) > *maxDepth {
			ms = ms[:*maxDepth]
		}

		result := ptn.Result{Result: g.FindTag("Result")}

		insertTree(tree, ms, result.Winner())
	}

	bs, _ := json.Marshal(tree)
	ioutil.WriteFile("gametree.json", bs, 0644)
	f, e := os.Create("gametree.dot")
	defer f.Close()
	writeTree(f, tree)
}

type tree struct {
	id int

	Move     string  `json:",omitempty"`
	Children []*tree `json:",omitempty"`
	Count    int
	White    int
	Black    int
}

var nextID = 1

func insertTree(t *tree, ms []tak.Move, winner tak.Color) {
	t.Count++
	switch winner {
	case tak.White:
		t.White++
	case tak.Black:
		t.Black++
	}
	if len(ms) == 0 {
		return
	}
	var child *tree
	m := ptn.FormatMove(&ms[0])
	for _, ch := range t.Children {
		if ch.Move == m {
			child = ch
			break
		}
	}
	if child == nil {
		child = &tree{Move: m, id: nextID}
		nextID++
		t.Children = append(t.Children, child)
	}
	insertTree(child, ms[1:], winner)
}

func writeTree(f io.Writer, t *tree) {
	fmt.Fprintf(f, "digraph G {\n")
	writeTreeNode(0, f, t)
	fmt.Fprintf(f, "}\n")
}

func writeTreeNode(ply int, f io.Writer, t *tree) {
	var mno string
	move := ply/2 + 1
	if ply%2 == 0 {
		mno = fmt.Sprintf("%d. ", move)
	} else {
		mno = fmt.Sprintf("%d. .. ", move)
	}

	fmt.Fprintf(f, `  n%d [shape=box, label="%s %d-%d/%0.2f%%"]`,
		t.id, t.Move, t.White, t.Black, 100*float64(t.White)/float64(t.Count))
	fmt.Fprintln(f)
	for _, ch := range t.Children {
		if ch.Count < *minCount {
			continue
		}
		fmt.Fprintf(f, `  n%d -> n%d [label="%s%s %d/%0.2f%%"]`,
			t.id, ch.id, mno, ch.Move,
			ch.Count, 100*float64(ch.Count)/float64(t.Count))
		fmt.Fprintln(f)
		writeTreeNode(ply+1, f, ch)
	}
}
<|endoftext|>