<commit_before>package vbox

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	pipeline "github.com/mattn/go-pipeline"
	virtualbox "github.com/riobard/go-virtualbox"
	"github.com/xshellinc/iotit/lib"
	"github.com/xshellinc/iotit/lib/repo"
	"github.com/xshellinc/isaax-cli/dialogs"
	"github.com/xshellinc/tools/constants"
	"github.com/xshellinc/tools/lib/help"
	"gopkg.in/cheggaaa/pb.v1"
)

// Stop stops VM
func Stop(name string) error {
	m, err := virtualbox.GetMachine(name)
	if err != nil {
		return err
	}

	if dialogs.YesNoDialog("Would you like to stop the virtual machine?") {
		fmt.Println("[+] Stopping virtual machine")
		if err := m.Poweroff(); err != nil {
			return err
		}
	}

	return nil
}

// CheckMachine checks if any vbox is running with the ability to power-off
// After that imports and runs the vbox image according to the selected device type
func CheckMachine(machine, device string) error {
	bars := make([]*pb.ProgressBar, 0)
	var wg sync.WaitGroup
	var repository repo.Repository
	var path = getPath()
	var machinePath = filepath.Join(path, machine, machine+".vbox")

	fmt.Println("[+] Checking virtual machine")
	// checking file location
	if !fileExists(machinePath) {
		if device == constants.DEVICE_TYPE_EDISON {
			repository = repo.VirtualBoxRepositoryEdison()
		} else {
			repository = repo.VirtualBoxRepository()
		}

		// checking local repository
		if repository.GetURL() == "" {
			return errors.New("Url is not set for downloading VBox image")
		}

		dst := filepath.Join(repository.Dir(), repository.GetVersion())
		fileName := repository.Name()

		// download virtual machine
		if !fileExists(filepath.Join(dst, fileName)) {
			fmt.Println("[+] Starting virtual machine download")
			var bar1 *pb.ProgressBar
			var err error

			fileName, bar1, err = repo.DownloadAsync(repository, &wg)
			if err != nil {
				return err
			}

			bar1.Prefix(fmt.Sprintf("[+] Download %-15s", fileName))
			if bar1.Total > 0 {
				bars = append(bars, bar1)
			}
			pool, err := pb.StartPool(bars...)
			if err != nil {
				return err
			}
			wg.Wait()
			pool.Stop()

			time.Sleep(time.Second * 2)
		}

		// unzip virtual machine
		err := help.Unzip(filepath.Join(dst, fileName), path)
		if err != nil {
			return err
		}

	}
	if !isActive(machine) {
		fmt.Printf("[+] Registering %s\n", machine)
		_, err := help.ExecCmd("VBoxManage",
			[]string{
				"registervm",
				fmt.Sprintf("%s", machinePath),
			})
		if err != nil {
			return err
		}
		fmt.Println("[+] Done")
	}
	fmt.Println("[+] No problem!")
	return nil
}

// Update virtualbox image
func Update(typeFlag string) (err error) {
	log.Debug("Virtual Machine Update func()")

	switch typeFlag {
	case "sd":
		update(lib.VBoxTemplateSD)
	case "edison":
		update(lib.VBoxTemplateEdison)
	}
	return nil
}

// CheckDeps checks for virtualbox dependencies
func CheckDeps(pkg string) error {
	err := exec.Command("which", pkg).Run()
	if err != nil {
		log.Error("Error while running `which` : ", err.Error())
		return fmt.Errorf("[-] Could not find virtualbox, please install virtualbox, try")
	}
	out, _ := exec.Command("VBoxManage", "list", "extpacks").Output()

	match, _ := regexp.MatchString(".*Oracle VM VirtualBox Extension Pack.*", string(out))
	if !match {
		return fmt.Errorf("[-] Could not find virtualbox extension pack, please install virtualbox extension pack, try")
	}
	return nil
}

// CheckUpdate checks for virtualbox image updates
func CheckUpdate(typeFlag string) (string, bool) {
	log.Debug("Check Update func()")

	switch typeFlag {
	case "sd":
		b, _ := checkUpdate(lib.VBoxTemplateSD)
		if !b {
			fmt.Println("[+] Current virtual machine is latest version")
			fmt.Println("[+] Done")
			return typeFlag, false
		}
	case "edison":
		b, _ := checkUpdate(lib.VBoxTemplateEdison)
		if !b {
			fmt.Println("[+] Current virtual machine is latest version")
			fmt.Println("[+] Done")
			return typeFlag, false
		}
	default:
		fmt.Println("[-] Unknown image ", typeFlag)
		os.Exit(1)
	}

	return typeFlag, true
}

// StopMachines stops running machines
func StopMachines() error {
	machines, err := virtualbox.ListMachines()
	if err != nil {
		return err
	}
	fmt.Println("[+] Checking running virtual machine")
	for _, m := range machines {
		if m.State == virtualbox.Running {
			var nameStr string
			if m.Description != "" {
				nameStr = m.Description
			} else {
				nameStr = "default"
			}

			if dialogs.YesNoDialog(fmt.Sprintf("\x1b[34m%s (%s)\x1b[0m is running, would you stop this virtual machine?", m.Name, nameStr)) {
				if err = m.Poweroff(); err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func getPath() string {
	out, err := virtualbox.SystemProperties()
	if err != nil {
		return err.Error()
	}
	re := regexp.MustCompile(`Default machine folder:(.*)`)
	result := re.FindStringSubmatch(string(out))
	return strings.Trim(result[1], " ")

}

func fileExists(name string) bool {
	_, err := os.Stat(name)
	return err == nil
}

func isActive(name string) bool {
	_, err := virtualbox.GetMachine(name)
	return err == nil
}

func checkUpdate(machine string) (bool, error) {
	err := CheckDeps("VBoxManage")
	help.ExitOnError(err)

	var baseDir = filepath.Join(help.UserHomeDir(), ".iotit")
	var vboxDir = filepath.Join(baseDir, "virtualbox")
	var repository repo.Repository
	var currentVersion string
	var comparison = func(s string, width int) (int64, error) {
		strList := strings.Split(s, ".")
		format := fmt.Sprintf("%%s%%0%ds", width)
		v := ""
		for _, value := range strList {
			v = fmt.Sprintf(format, v, value)
		}
		var result int64
		var err error
		result, err = strconv.ParseInt(v, 10, 64)
		if err != nil {
			return 0, err
		}
		return result, nil
	}

	if machine == lib.VBoxTemplateEdison {
		repo, err := repo.NewRepositoryVM(lib.VBoxTemplateEdison)
		if err != nil {
			return false, err
		}
		repository = repo
		if !fileExists(repository.Dir()) {
			fmt.Println("[+] could not find the virtual machine, lease execute `iotit`")
		}
	} else {
		repo, err := repo.NewRepositoryVM(lib.VBoxTemplateSD)
		if err != nil {
			return false, err
		}
		repository = repo
		if !fileExists(repository.Dir()) {
			fmt.Println("[+] could not find the virtual machine, lease execute `iotit`")
		}
	}

	newVersion := repository.GetVersion()
	if machine == lib.VBoxTemplateEdison {
		out, err := pipeline.Output(
			[]string{"ls", filepath.Join(vboxDir, "edison")},
			[]string{"sort", "-n"},
			[]string{"tail", "-1"},
		)
		if err != nil {
			return false, err
		}
		currentVersion = strings.Trim(string(out), "\n")
	} else {
		out, err := pipeline.Output(
			[]string{"ls", filepath.Join(vboxDir, "sd")},
			[]string{"sort", "-n"},
			[]string{"tail", "-1"},
		)
		if err != nil {
			return false, err
		}
		currentVersion = strings.Trim(string(out), "\n")
	}

	c, err := comparison(currentVersion, 3)
	if err != nil {
		return false, err
	}
	n, err := comparison(newVersion, 3)
	if err != nil {
		return false, err
	}

	switch {
	case c > n:
		return false, nil
	case c < n:
		return true, nil
	}
	return false, nil
}

func update(machine string) {
	var (
		wg         sync.WaitGroup
		repository repo.Repository
	)

	bars := make([]*pb.ProgressBar, 0)
	err := CheckDeps("VBoxManage")
	help.ExitOnError(err)

	if machine == lib.VBoxTemplateEdison {
		repo, err := repo.NewRepositoryVM(lib.VBoxTemplateEdison)
		if err != nil {
			help.ExitOnError(err)
		}
		repository = repo
		if !fileExists(repository.Dir()) {
			fmt.Println("[+] could not find the virtual machine, lease execute `iotit`")
		}
	} else {
		repo, err := repo.NewRepositoryVM(lib.VBoxTemplateSD)
		if err != nil {
			help.ExitOnError(err)
		}
		repository = repo
		if !fileExists(repository.Dir()) {
			fmt.Println("[+] could not find the virtual machine, lease execute `iotit`")
		}
	}

	fmt.Println(strings.Repeat("*", 100))
	fmt.Println("*\t\t WARNNING!!  \t\t\t\t\t\t\t\t\t   *")
	fmt.Println("*\t\t THIS COMMAND WILL INITIALIZE YOUR VBOX SETTINGS!  \t\t\t\t   *")
	fmt.Println("*\t\t IF IT IS OKAY, UPDATE VIRTUAL MACHINE! \t\t\t\t\t   *")
	fmt.Println(strings.Repeat("*", 100))

	if dialogs.YesNoDialog("Would you update virtual machine?") {
		boolean, err := checkUpdate(machine)
		if err != nil {
			help.ExitOnError(err)
		}

		if !boolean { // @todo why
			fmt.Println("[+] Current virtual machine is latest version")
			fmt.Println("[+] Done")
			os.Exit(0)
		}

		var path = getPath()
		var machinePath = filepath.Join(path, machine, machine+".vbox")

		fmt.Println("[+] Unregistering old virtual machine")
		if isActive(machine) {
			m, err := virtualbox.GetMachine(machine)
			if err != nil {
				help.ExitOnError(err)
			}
			if m.State == virtualbox.Running {
				err = m.Poweroff()
				if err != nil {
					help.ExitOnError(err)
				}
			}
			help.ExecCmd("VBoxManage",
				[]string{
					"unregistervm",
					fmt.Sprintf("%s", machinePath),
				})
			fmt.Println("[+] Done")
		}
		// remove old virtual machine
		err = os.RemoveAll(filepath.Join(path, machine))
		if err != nil {
			// rollback virtual machine
			out, err := pipeline.Output(
				[]string{"ls", repository.Dir()},
				[]string{"sort", "-n"},
				[]string{"tail", "-1"},
			)
			if err != nil {
				help.ExitOnError(err)
			}
			currentVersion := strings.Trim(string(out), "\n")

			err = help.Unzip(filepath.Join(repository.Dir(), repository.GetVersion(), currentVersion, machine+".zip"), path)
			if err != nil {
				help.ExitOnError(err)
			}
			_, err = help.ExecCmd("VBoxManage",
				[]string{
					"registervm",
					fmt.Sprintf("%s", machinePath),
				})
			if err != nil {
				help.ExitOnError(err)
			}
			help.ExitOnError(err)
		}

		// download virtual machine
		fmt.Println("[+] Starting virtual machine download")
		fileName, bar1, err := repo.DownloadAsync(repository, &wg)
		if err != nil {
			help.ExitOnError(err)
		}
		dst := filepath.Join(repository.Dir(), repository.GetVersion())
		bar1.Prefix(fmt.Sprintf("[+] Download %-15s", fileName))
		if bar1.Total > 0 {
			bars = append(bars, bar1)
		}
		pool, err := pb.StartPool(bars...)
		if err != nil {
			help.ExitOnError(err)
		}
		wg.Wait()
		pool.Stop()
		time.Sleep(time.Second * 2)

		err = help.Unzip(filepath.Join(dst, fileName), path)
		if err != nil {
			help.ExitOnError(err)
		}

		fmt.Println("[+] Registering new virtual machine")
		_, err = help.ExecCmd("VBoxManage",
			[]string{
				"registervm",
				fmt.Sprintf("%s", machinePath),
			})
		if err != nil {
			help.ExitOnError(err)
		} else {
			fmt.Println("[+] Done")
		}

		conf := filepath.Join(help.UserHomeDir(), ".iotit", "virtualbox", "iotit-vbox.json")
		os.Remove(conf)
	}
}
<commit_msg>Fix type in the import<commit_after>package vbox

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	pipeline "github.com/mattn/go-pipeline"
	virtualbox "github.com/riobard/go-virtualbox"
	"github.com/xshellinc/iotit/lib"
	"github.com/xshellinc/iotit/lib/repo"
	"github.com/xshellinc/tools/constants"
	"github.com/xshellinc/tools/dialogs"
	"github.com/xshellinc/tools/lib/help"
	"gopkg.in/cheggaaa/pb.v1"
)

// Stop stops VM
func Stop(name string) error {
	m, err := virtualbox.GetMachine(name)
	if err != nil {
		return err
	}

	if dialogs.YesNoDialog("Would you like to stop the virtual machine?") {
		fmt.Println("[+] Stopping virtual machine")
		if err := m.Poweroff(); err != nil {
			return err
		}
	}

	return nil
}

// CheckMachine checks if any vbox is running with the ability to power-off
// After that imports and runs the vbox image according to the selected device type
func CheckMachine(machine, device string) error {
	bars := make([]*pb.ProgressBar, 0)
	var wg sync.WaitGroup
	var repository repo.Repository
	var path = getPath()
	var machinePath = filepath.Join(path, machine, machine+".vbox")

	fmt.Println("[+] Checking virtual machine")
	// checking file location
	if !fileExists(machinePath) {
		if device == constants.DEVICE_TYPE_EDISON {
			repository = repo.VirtualBoxRepositoryEdison()
		} else {
			repository = repo.VirtualBoxRepository()
		}

		// checking local repository
		if repository.GetURL() == "" {
			return errors.New("Url is not set for downloading VBox image")
		}

		dst := filepath.Join(repository.Dir(), repository.GetVersion())
		fileName := repository.Name()

		// download virtual machine
		if !fileExists(filepath.Join(dst, fileName)) {
			fmt.Println("[+] Starting virtual machine download")
			var bar1 *pb.ProgressBar
			var err error

			fileName, bar1, err = repo.DownloadAsync(repository, &wg)
			if err != nil {
				return err
			}

			bar1.Prefix(fmt.Sprintf("[+] Download %-15s", fileName))
			if bar1.Total > 0 {
				bars = append(bars, bar1)
			}
			pool, err := pb.StartPool(bars...)
			if err != nil {
				return err
			}
			wg.Wait()
			pool.Stop()

			time.Sleep(time.Second * 2)
		}

		// unzip virtual machine
		err := help.Unzip(filepath.Join(dst, fileName), path)
		if err != nil {
			return err
		}

	}
	if !isActive(machine) {
		fmt.Printf("[+] Registering %s\n", machine)
		_, err := help.ExecCmd("VBoxManage",
			[]string{
				"registervm",
				fmt.Sprintf("%s", machinePath),
			})
		if err != nil {
			return err
		}
		fmt.Println("[+] Done")
	}
	fmt.Println("[+] No problem!")
	return nil
}

// Update virtualbox image
func Update(typeFlag string) (err error) {
	log.Debug("Virtual Machine Update func()")

	switch typeFlag {
	case "sd":
		update(lib.VBoxTemplateSD)
	case "edison":
		update(lib.VBoxTemplateEdison)
	}
	return nil
}

// CheckDeps checks for virtualbox dependencies
func CheckDeps(pkg string) error {
	err := exec.Command("which", pkg).Run()
	if err != nil {
		log.Error("Error while running `which` : ", err.Error())
		return fmt.Errorf("[-] Could not find virtualbox, please install virtualbox, try")
	}
	out, _ := exec.Command("VBoxManage", "list", "extpacks").Output()

	match, _ := regexp.MatchString(".*Oracle VM VirtualBox Extension Pack.*", string(out))
	if !match {
		return fmt.Errorf("[-] Could not find virtualbox extension pack, please install virtualbox extension pack, try")
	}
	return nil
}

// CheckUpdate checks for virtualbox image updates
func CheckUpdate(typeFlag string) (string, bool) {
	log.Debug("Check Update func()")

	switch typeFlag {
	case "sd":
		b, _ := checkUpdate(lib.VBoxTemplateSD)
		if !b {
			fmt.Println("[+] Current virtual machine is latest version")
			fmt.Println("[+] Done")
			return typeFlag, false
		}
	case "edison":
		b, _ := checkUpdate(lib.VBoxTemplateEdison)
		if !b {
			fmt.Println("[+] Current virtual machine is latest version")
			fmt.Println("[+] Done")
			return typeFlag, false
		}
	default:
		fmt.Println("[-] Unknown image ", typeFlag)
		os.Exit(1)
	}

	return typeFlag, true
}

// StopMachines stops running machines
func StopMachines() error {
	machines, err := virtualbox.ListMachines()
	if err != nil {
		return err
	}
	fmt.Println("[+] Checking running virtual machine")
	for _, m := range machines {
		if m.State == virtualbox.Running {
			var nameStr string
			if m.Description != "" {
				nameStr = m.Description
			} else {
				nameStr = "default"
			}

			if dialogs.YesNoDialog(fmt.Sprintf("\x1b[34m%s (%s)\x1b[0m is running, would you stop this virtual machine?", m.Name, nameStr)) {
				if err = m.Poweroff(); err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func getPath() string {
	out, err := virtualbox.SystemProperties()
	if err != nil {
		return err.Error()
	}
	re := regexp.MustCompile(`Default machine folder:(.*)`)
	result := re.FindStringSubmatch(string(out))
	return strings.Trim(result[1], " ")

}

func fileExists(name string) bool {
	_, err := os.Stat(name)
	return err == nil
}

func isActive(name string) bool {
	_, err := virtualbox.GetMachine(name)
	return err == nil
}

func checkUpdate(machine string) (bool, error) {
	err := CheckDeps("VBoxManage")
	help.ExitOnError(err)

	var baseDir = filepath.Join(help.UserHomeDir(), ".iotit")
	var vboxDir = filepath.Join(baseDir, "virtualbox")
	var repository repo.Repository
	var currentVersion string
	var comparison = func(s string, width int) (int64, error) {
		strList := strings.Split(s, ".")
		format := fmt.Sprintf("%%s%%0%ds", width)
		v := ""
		for _, value := range strList {
			v = fmt.Sprintf(format, v, value)
		}
		var result int64
		var err error
		result, err = strconv.ParseInt(v, 10, 64)
		if err != nil {
			return 0, err
		}
		return result, nil
	}

	if machine == lib.VBoxTemplateEdison {
		repo, err := repo.NewRepositoryVM(lib.VBoxTemplateEdison)
		if err != nil {
			return false, err
		}
		repository = repo
		if !fileExists(repository.Dir()) {
			fmt.Println("[+] could not find the virtual machine, lease execute `iotit`")
		}
	} else {
		repo, err := repo.NewRepositoryVM(lib.VBoxTemplateSD)
		if err != nil {
			return false, err
		}
		repository = repo
		if !fileExists(repository.Dir()) {
			fmt.Println("[+] could not find the virtual machine, lease execute `iotit`")
		}
	}

	newVersion := repository.GetVersion()
	if machine == lib.VBoxTemplateEdison {
		out, err := pipeline.Output(
			[]string{"ls", filepath.Join(vboxDir, "edison")},
			[]string{"sort", "-n"},
			[]string{"tail", "-1"},
		)
		if err != nil {
			return false, err
		}
		currentVersion = strings.Trim(string(out), "\n")
	} else {
		out, err := pipeline.Output(
			[]string{"ls", filepath.Join(vboxDir, "sd")},
			[]string{"sort", "-n"},
			[]string{"tail", "-1"},
		)
		if err != nil {
			return false, err
		}
		currentVersion = strings.Trim(string(out), "\n")
	}

	c, err := comparison(currentVersion, 3)
	if err != nil {
		return false, err
	}
	n, err := comparison(newVersion, 3)
	if err != nil {
		return false, err
	}

	switch {
	case c > n:
		return false, nil
	case c < n:
		return true, nil
	}
	return false, nil
}

func update(machine string) {
	var (
		wg         sync.WaitGroup
		repository repo.Repository
	)

	bars := make([]*pb.ProgressBar, 0)
	err := CheckDeps("VBoxManage")
	help.ExitOnError(err)

	if machine == lib.VBoxTemplateEdison {
		repo, err := repo.NewRepositoryVM(lib.VBoxTemplateEdison)
		if err != nil {
			help.ExitOnError(err)
		}
		repository = repo
		if !fileExists(repository.Dir()) {
			fmt.Println("[+] could not find the virtual machine, lease execute `iotit`")
		}
	} else {
		repo, err := repo.NewRepositoryVM(lib.VBoxTemplateSD)
		if err != nil {
			help.ExitOnError(err)
		}
		repository = repo
		if !fileExists(repository.Dir()) {
			fmt.Println("[+] could not find the virtual machine, lease execute `iotit`")
		}
	}

	fmt.Println(strings.Repeat("*", 100))
	fmt.Println("*\t\t WARNNING!!  \t\t\t\t\t\t\t\t\t   *")
	fmt.Println("*\t\t THIS COMMAND WILL INITIALIZE YOUR VBOX SETTINGS!  \t\t\t\t   *")
	fmt.Println("*\t\t IF IT IS OKAY, UPDATE VIRTUAL MACHINE! \t\t\t\t\t   *")
	fmt.Println(strings.Repeat("*", 100))

	if dialogs.YesNoDialog("Would you update virtual machine?") {
		boolean, err := checkUpdate(machine)
		if err != nil {
			help.ExitOnError(err)
		}

		if !boolean { // @todo why
			fmt.Println("[+] Current virtual machine is latest version")
			fmt.Println("[+] Done")
			os.Exit(0)
		}

		var path = getPath()
		var machinePath = filepath.Join(path, machine, machine+".vbox")

		fmt.Println("[+] Unregistering old virtual machine")
		if isActive(machine) {
			m, err := virtualbox.GetMachine(machine)
			if err != nil {
				help.ExitOnError(err)
			}
			if m.State == virtualbox.Running {
				err = m.Poweroff()
				if err != nil {
					help.ExitOnError(err)
				}
			}
			help.ExecCmd("VBoxManage",
				[]string{
					"unregistervm",
					fmt.Sprintf("%s", machinePath),
				})
			fmt.Println("[+] Done")
		}
		// remove old virtual machine
		err = os.RemoveAll(filepath.Join(path, machine))
		if err != nil {
			// rollback virtual machine
			out, err := pipeline.Output(
				[]string{"ls", repository.Dir()},
				[]string{"sort", "-n"},
				[]string{"tail", "-1"},
			)
			if err != nil {
				help.ExitOnError(err)
			}
			currentVersion := strings.Trim(string(out), "\n")

			err = help.Unzip(filepath.Join(repository.Dir(), repository.GetVersion(), currentVersion, machine+".zip"), path)
			if err != nil {
				help.ExitOnError(err)
			}
			_, err = help.ExecCmd("VBoxManage",
				[]string{
					"registervm",
					fmt.Sprintf("%s", machinePath),
				})
			if err != nil {
				help.ExitOnError(err)
			}
			help.ExitOnError(err)
		}

		// download virtual machine
		fmt.Println("[+] Starting virtual machine download")
		fileName, bar1, err := repo.DownloadAsync(repository, &wg)
		if err != nil {
			help.ExitOnError(err)
		}
		dst := filepath.Join(repository.Dir(), repository.GetVersion())
		bar1.Prefix(fmt.Sprintf("[+] Download %-15s", fileName))
		if bar1.Total > 0 {
			bars = append(bars, bar1)
		}
		pool, err := pb.StartPool(bars...)
		if err != nil {
			help.ExitOnError(err)
		}
		wg.Wait()
		pool.Stop()
		time.Sleep(time.Second * 2)

		err = help.Unzip(filepath.Join(dst, fileName), path)
		if err != nil {
			help.ExitOnError(err)
		}

		fmt.Println("[+] Registering new virtual machine")
		_, err = help.ExecCmd("VBoxManage",
			[]string{
				"registervm",
				fmt.Sprintf("%s", machinePath),
			})
		if err != nil {
			help.ExitOnError(err)
		} else {
			fmt.Println("[+] Done")
		}

		conf := filepath.Join(help.UserHomeDir(), ".iotit", "virtualbox", "iotit-vbox.json")
		os.Remove(conf)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/SermoDigital/protoc-gen-endpoint/tables"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/protoc-gen-go/descriptor"

	eproto "github.com/SermoDigital/protoc-gen-endpoint/proto"
	plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
	options "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"
)

func main() {
	log.SetFlags(255)

	b, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		log.Fatalln(err)
	}

	req := new(plugin.CodeGeneratorRequest)
	err = proto.Unmarshal(b, req)
	if err != nil {
		log.Fatalln(err)
	}

	err = writeEndpoints(os.Stdout, req)
	if err != nil {
		log.Fatalln(err)
	}
}

func writeEndpoints(w io.Writer, req *plugin.CodeGeneratorRequest) error {
	infos, err := getInfo(req)
	if err != nil {
		return err
	}
	t, err := template.New("tmpl").Parse(templ)
	if err != nil {
		return err
	}

	var files []*plugin.CodeGeneratorResponse_File
	var buf bytes.Buffer
	for _, info := range infos {
		buf.Reset()
		err = t.Execute(&buf, info)
		if err != nil {
			return err
		}
		fname := fmt.Sprintf("%s/%s.pb.ep.go", info.PkgName, info.PkgName)
		files = append(files, &plugin.CodeGeneratorResponse_File{
			Name:    proto.String(fname),
			Content: proto.String(buf.String()),
		})
	}

	b, err := proto.Marshal(&plugin.CodeGeneratorResponse{File: files})
	if err != nil {
		return err
	}
	_, err = w.Write(b)
	return err
}

type Info struct {
	PkgName string
	Table   tables.Table
}

func getInfo(req *plugin.CodeGeneratorRequest) (ifs []Info, err error) {
	for _, pf := range req.GetProtoFile() {
		i := Info{
			PkgName: pkgName(pf),
			Table:   make(tables.Table),
		}
		for _, srv := range pf.GetService() {
			for _, meth := range srv.GetMethod() {
				if meth.Options == nil ||
					!proto.HasExtension(meth.Options, options.E_Http) {
					continue
				}

				ext, err := proto.GetExtension(meth.Options, options.E_Http)
				if err != nil {
					return nil, err
				}
				http, ok := ext.(*options.HttpRule)
				if !ok {
					return nil, fmt.Errorf("got %T, wanted *options.HttpRule", ext)
				}

				ext, _ = proto.GetExtension(meth.Options, eproto.E_Endpoint)
				endp, ok := ext.(*eproto.Endpoint)
				unauth := ok && endp.Unauthenticated

				prefix := strings.TrimSuffix(i.PkgName, "pb")
				action := prefix + "." + *meth.Name

				err = parseTuple(http, i.Table, unauth, action)
				if err != nil {
					return nil, err
				}

				for _, http := range http.AdditionalBindings {
					err := parseTuple(http, i.Table, unauth, action)
					if err != nil {
						return nil, err
					}
				}
			}
		}
		if len(i.Table) != 0 {
			ifs = append(ifs, i)
		}
	}
	return ifs, nil
}

// pkgName returns a suitable package name from file.
//
// Mostly borrowed from grpc-gateway.
func pkgName(file *descriptor.FileDescriptorProto) string {
	if file.Options != nil && file.Options.GoPackage != nil {
		gopkg := file.Options.GetGoPackage()
		i := strings.LastIndexByte(gopkg, '/')
		if i < 0 {
			return gopkg
		}
		return strings.Replace(gopkg[i+1:], ".", "_", -1)
	}

	if file.Package == nil {
		base := filepath.Base(file.GetName())
		ext := filepath.Ext(base)
		return strings.TrimSuffix(base, ext)
	}
	return strings.Replace(file.GetPackage(), ".", "_", -1)
}

// parseTuple parses a new tables.Endpoint from http and adds it to table.
func parseTuple(http *options.HttpRule, tbl tables.Table, unauth bool, action string) error {
	var (
		url    string
		method string
	)
	switch v := http.Pattern.(type) {
	case *options.HttpRule_Get:
		url = v.Get
		method = "GET"
	case *options.HttpRule_Put:
		url = v.Put
		method = "PUT"
	case *options.HttpRule_Post:
		url = v.Post
		method = "POST"
	case *options.HttpRule_Delete:
		url = v.Delete
		method = "DELETE"
	case *options.HttpRule_Patch:
		url = v.Patch
		method = "PATCH"
	case *options.HttpRule_Custom:
		url = v.Custom.Path
		method = v.Custom.Kind
	default:
		return fmt.Errorf("unknown http.Patten: %T", http.Pattern)
	}

	eps := tbl[url]
	eps = append(eps, tables.Endpoint{
		Method:          method,
		Unauthenticated: unauth,
		Action:          action,
	})
	tbl[url] = eps
	return nil
}

const templ = `// Package {{ .PkgName }} creates a (URL, HTTP method) -> action lookup table
package {{ .PkgName }}

import "github.com/SermoDigital/protoc-gen-endpoint/tables"

// Table returns a tables.Table containing the endpoints within a gRPC package.
func Table() tables.Table {
	return tables.Table{
		{{- range $url, $eps := .Table }}
		{{ $url | printf "%q" }}: []tables.Endpoint{
			{{ range $ep := $eps -}}
			{
				Method: {{- $ep.Method | printf "%q" }},
				Unauthenticated: {{ $ep.Unauthenticated }},
				Action: {{ $ep.Action | printf "%q" -}},
			},
			{{- end }}
		},
		{{- end  }}
	}
}
`
<commit_msg>style changes<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/SermoDigital/protoc-gen-endpoint/tables"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/protoc-gen-go/descriptor"

	eproto "github.com/SermoDigital/protoc-gen-endpoint/proto"
	plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
	options "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"
)

func main() {
	log.SetFlags(255)

	b, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		log.Fatalln(err)
	}

	req := new(plugin.CodeGeneratorRequest)
	err = proto.Unmarshal(b, req)
	if err != nil {
		log.Fatalln(err)
	}

	err = writeEndpoints(os.Stdout, req)
	if err != nil {
		log.Fatalln(err)
	}
}

func writeEndpoints(w io.Writer, req *plugin.CodeGeneratorRequest) error {
	infos, err := getInfo(req)
	if err != nil {
		return err
	}
	t, err := template.New("tmpl").Parse(templ)
	if err != nil {
		return err
	}

	var files []*plugin.CodeGeneratorResponse_File
	var buf bytes.Buffer
	for _, info := range infos {
		buf.Reset()
		err = t.Execute(&buf, info)
		if err != nil {
			return err
		}
		fname := fmt.Sprintf("%s/%s.pb.ep.go", info.PkgName, info.PkgName)
		files = append(files, &plugin.CodeGeneratorResponse_File{
			Name:    proto.String(fname),
			Content: proto.String(buf.String()),
		})
	}

	b, err := proto.Marshal(&plugin.CodeGeneratorResponse{File: files})
	if err != nil {
		return err
	}
	_, err = w.Write(b)
	return err
}

type Info struct {
	PkgName string
	Table   tables.Table
}

func getInfo(req *plugin.CodeGeneratorRequest) (ifs []Info, err error) {
	for _, pf := range req.GetProtoFile() {
		i := Info{
			PkgName: pkgName(pf),
			Table:   make(tables.Table),
		}
		for _, srv := range pf.GetService() {
			for _, meth := range srv.GetMethod() {
				if meth.Options == nil ||
					!proto.HasExtension(meth.Options, options.E_Http) {
					continue
				}

				ext, err := proto.GetExtension(meth.Options, options.E_Http)
				if err != nil {
					return nil, err
				}
				http, ok := ext.(*options.HttpRule)
				if !ok {
					return nil, fmt.Errorf("got %T, wanted *options.HttpRule", ext)
				}

				ext, _ = proto.GetExtension(meth.Options, eproto.E_Endpoint)
				endp, ok := ext.(*eproto.Endpoint)
				unauth := ok && endp.Unauthenticated

				prefix := strings.TrimSuffix(i.PkgName, "pb")
				action := prefix + "." + *meth.Name

				err = parseTuple(http, i.Table, unauth, action)
				if err != nil {
					return nil, err
				}

				for _, http := range http.AdditionalBindings {
					err := parseTuple(http, i.Table, unauth, action)
					if err != nil {
						return nil, err
					}
				}
			}
		}
		if len(i.Table) != 0 {
			ifs = append(ifs, i)
		}
	}
	return ifs, nil
}

// pkgName returns a suitable package name from file.
//
// Mostly borrowed from grpc-gateway.
func pkgName(file *descriptor.FileDescriptorProto) string {
	if file.Options != nil && file.Options.GoPackage != nil {
		gopkg := file.Options.GetGoPackage()
		i := strings.LastIndexByte(gopkg, '/')
		if i < 0 {
			return gopkg
		}
		return strings.Replace(gopkg[i+1:], ".", "_", -1)
	}

	if file.Package == nil {
		base := filepath.Base(file.GetName())
		ext := filepath.Ext(base)
		return strings.TrimSuffix(base, ext)
	}
	return strings.Replace(file.GetPackage(), ".", "_", -1)
}

// parseTuple parses a new tables.Endpoint from http and adds it to table.
func parseTuple(http *options.HttpRule, tbl tables.Table, unauth bool, action string) error {
	var url string
	ep := tables.Endpoint{Unauthenticated: unauth, Action: action}
	switch v := http.Pattern.(type) {
	case *options.HttpRule_Get:
		url = v.Get
		ep.Method = "GET"
	case *options.HttpRule_Put:
		url = v.Put
		ep.Method = "PUT"
	case *options.HttpRule_Post:
		url = v.Post
		ep.Method = "POST"
	case *options.HttpRule_Delete:
		url = v.Delete
		ep.Method = "DELETE"
	case *options.HttpRule_Patch:
		url = v.Patch
		ep.Method = "PATCH"
	case *options.HttpRule_Custom:
		url = v.Custom.Path
		ep.Method = v.Custom.Kind
	default:
		return fmt.Errorf("unknown http.Patten: %T", http.Pattern)
	}

	tbl[url] = append(tbl[url], ep)
	return nil
}

const templ = `// Package {{ .PkgName }} creates a (URL, HTTP method) -> action lookup table
package {{ .PkgName }}

import "github.com/SermoDigital/protoc-gen-endpoint/tables"

// Table returns a tables.Table containing the endpoints within a gRPC package.
func Table() tables.Table {
	return tables.Table{
		{{- range $url, $eps := .Table }}
		{{ $url | printf "%q" }}: []tables.Endpoint{
			{{ range $ep := $eps -}}
			{
				Method: {{- $ep.Method | printf "%q" }},
				Unauthenticated: {{ $ep.Unauthenticated }},
				Action: {{ $ep.Action | printf "%q" -}},
			},
			{{- end }}
		},
		{{- end  }}
	}
}
`
<|endoftext|>


<commit_before>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2017] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package service

import (
	"context"
	"github.com/lastbackend/lastbackend/pkg/apis/types"
	ctx "github.com/lastbackend/lastbackend/pkg/daemon/context"
	"github.com/lastbackend/lastbackend/pkg/daemon/node"
	"github.com/lastbackend/lastbackend/pkg/daemon/service/routes/request"
	"github.com/lastbackend/lastbackend/pkg/util/validator"
	"github.com/satori/go.uuid"
	"strings"
	"time"
)

type service struct {
	Context   context.Context
	Namespace types.Meta
}

func New(ctx context.Context, namespace types.Meta) *service {
	return &service{
		Context:   ctx,
		Namespace: namespace,
	}
}

func (s *service) List() (*types.ServiceList, error) {
	var storage = ctx.Get().GetStorage()
	return storage.Service().ListByNamespace(s.Context, s.Namespace.ID)
}

func (s *service) Get(service string) (*types.Service, error) {

	var (
		err     error
		log     = ctx.Get().GetLogger()
		storage = ctx.Get().GetStorage()
		svc     *types.Service
	)

	if validator.IsUUID(service) {
		svc, err = storage.Service().GetByID(s.Context, s.Namespace.ID, service)
	} else {
		svc, err = storage.Service().GetByName(s.Context, s.Namespace.ID, service)
	}

	if err != nil {
		log.Error("Error: find service by name", err.Error())
		return svc, err
	}

	return svc, nil
}

func (s *service) Create(rq *request.RequestServiceCreateS) (*types.Service, error) {

	var (
		log     = ctx.Get().GetLogger()
		storage = ctx.Get().GetStorage()
		svc     = new(types.Service)
	)

	log.Debug("Service: create new service")

	svc.Meta = types.ServiceMeta{}
	svc.Meta.ID = uuid.NewV4().String()
	svc.Meta.Name = rq.Name
	svc.Meta.Region = rq.Region
	svc.Meta.Namespace = s.Namespace.Name
	svc.Meta.Description = rq.Description
	svc.Meta.Updated = time.Now()
	svc.Meta.Created = time.Now()

	svc.Meta.Replicas = 1

	if rq.Replicas != nil && *rq.Replicas > 0 {
		svc.Meta.Replicas = *rq.Replicas
	}

	config, err := createConfig(rq.Config)
	if err != nil {
		log.Errorf("Error: create config from request opts : %s", err.Error())
		return svc, err
	}

	svc.Config = *config

	log.Debugf("Service: Create: add pods : %d", svc.Meta.Replicas)
	for i := 0; i < svc.Meta.Replicas; i++ {
		log.Debug("Service: Create: add new pod")
		if err := s.AddPod(svc); err != nil {
			log.Errorf("Service: Create: add new pod error: %s", err.Error())
			return svc, err
		}
	}

	svc, err = storage.Service().Insert(s.Context, svc)
	if err != nil {
		log.Errorf("Error: insert service to db : %s", err.Error())
		return svc, err
	}

	return svc, nil
}

func (s *service) Update(service *types.Service, rq *request.RequestServiceUpdateS) (*types.Service, error) {

	var (
		err     error
		log     = ctx.Get().GetLogger()
		storage = ctx.Get().GetStorage()
		svc     *types.Service
	)

	log.Debug("Service: update service info and config")

	if rq.Name != "" {
		service.Meta.Name = rq.Name
	}

	if rq.Description != "" {
		service.Meta.Description = rq.Description
	}

	if rq.Domains != nil {
		service.Domains = rq.Domains
	}

	if rq.Replicas != nil {
		log.Debugf("Service: Update: set replicas: %d", *rq.Replicas)
		service.Meta.Replicas = *rq.Replicas
	}

	if rq.Config != nil {
		if err := updateConfig(rq.Config, &service.Config); err != nil {
			log.Error("Error: update service config from request opts", err)
			return svc, err
		}

		// Update pod spec
		spec := s.GenerateSpec(service)

		log.Debugf("Service: Update: pods count: %d", len(service.Pods))
		for _, pod := range service.Pods {
			log.Debugf("Service: Update: pod %s update", pod.Meta.ID)
			pod.Spec = spec
		}

	}

	s.Scale(s.Context, service)

	svc, err = storage.Service().Update(s.Context, service)
	if err != nil {
		log.Error("Error: insert service to db", err)
		return svc, err
	}

	return svc, nil
}

func (s *service) Remove(service *types.Service) error {
	var (
		log     = ctx.Get().GetLogger()
		storage = ctx.Get().GetStorage()
	)

	service.Meta.State.State = "deleting"

	for _, pod := range service.Pods {
		pod.Meta.State.State = "deleting"
	}

	if _, err := storage.Service().Update(s.Context, service); err != nil {
		log.Error("Error: insert service to db", err)
		return err
	}
	return nil
}

func (s *service) AddPod(service *types.Service) error {

	var (
		log = ctx.Get().GetLogger()
	)

	log.Debug("Create new pod state on service")

	pod := new(types.Pod)
	pod.Meta.State.State = "running"
	pod.Meta.ID = uuid.NewV4().String()
	pod.Meta.Created = time.Now()
	pod.Meta.Updated = time.Now()

	if len(service.Pods) > 0 {
		for _, p := range service.Pods {
			pod.Spec = p.Spec
			break
		}
	} else {
		pod.Spec = s.GenerateSpec(service)
	}

	n, err := node.New().Allocate(s.Context, pod.Spec)
	if err != nil {
		return err
	}

	log.Debugf("Service: Add pod: Node meta: %s", n.Meta)
	pod.Meta.Hostname = n.Meta.Hostname
	service.Pods[pod.Meta.ID] = pod

	return nil
}

func (s *service) DelPod(service *types.Service) error {

	var (
		log = ctx.Get().GetLogger()
		pod *types.Pod
	)

	log.Debug("Create new pod state on service")

	for _, pod = range service.Pods {
		if pod.Meta.State.State != "deleting" {
			pod.Meta.State.State = "deleting"
			break
		}
	}

	return nil
}

func (s *service) SetPods(c context.Context, pods []types.Pod) error {
	var (
		log     = ctx.Get().GetLogger()
		storage = ctx.Get().GetStorage()
	)

	for _, pod := range pods {

		svc, err := storage.Service().GetByPodID(c, pod.Meta.ID)
		if err != nil {
			log.Errorf("Error: get pod from db: %s", err)
			return err
		}

		if svc == nil {
			log.Debug("Serice not found")
			continue
		}

		p, e := storage.Pod().GetByID(c, svc.Meta.Namespace, svc.Meta.ID, pod.Meta.ID)

		if e != nil {
			log.Errorf("Error: get pod from db: %s", e.Error())
			return err
		}

		if p == nil {
			log.Warnf("Pod not found, skip setting: %s", pod.Meta.ID)
		}

		p.Containers = pod.Containers
		p.Meta.State = pod.Meta.State

		if p.Meta.State.State == "deleted" {
			log.Debugf("Service: Set pods: remove deleted pod: %s", p.Meta.ID)
			if err := storage.Pod().Remove(c, svc.Meta.Namespace, svc.Meta.ID, p); err != nil {
				log.Errorf("Error: set pod to db: %s", err)
				return err
			}
			delete(svc.Pods, p.Meta.ID)

			if len(svc.Pods) == 0 && svc.Meta.State.State == "deleting" {
				storage.Service().Remove(c, svc)
			}

			return nil
		}

		if err := storage.Pod().Update(c, svc.Meta.Namespace, svc.Meta.ID, p); err != nil {
			log.Errorf("Error: set pod to db: %s", err)
			return err
		}
	}

	return nil
}

func (s *service) Scale(c context.Context, service *types.Service) error {
	var (
		log      = ctx.Get().GetLogger()
		pod      *types.Pod
		replicas int
	)

	for _, pod = range service.Pods {
		if pod.Meta.State.State != "deleting" {
			replicas++
		}
	}

	log.Debugf("Service: Scale: current replicas: %d", replicas)

	if replicas == service.Meta.Replicas {
		log.Debug("Service: Replicas not needed, replicas equal")
		return nil
	}

	if replicas < service.Meta.Replicas {
		log.Debug("Service: Replicas: create a new replicas")
		for i := 0; i < (service.Meta.Replicas - replicas); i++ {
			if err := s.AddPod(service); err != nil {
				return err
			}
		}
	}

	if replicas > service.Meta.Replicas {
		log.Debug("Service: Replicas: remove  unneeded replicas")
		for i := 0; i < (replicas - service.Meta.Replicas); i++ {
			if err := s.DelPod(service); err != nil {
				return err
			}
		}
	}

	return nil
}

func (s *service) GenerateSpec(service *types.Service) types.PodSpec {

	var (
		log = ctx.Get().GetLogger()
	)

	log.Debug("Generate new node pod spec")
	var spec = types.PodSpec{}
	spec.ID = uuid.NewV4().String()
	spec.Created = time.Now()
	spec.Updated = time.Now()

	cs := new(types.ContainerSpec)
	cs.Image = types.ImageSpec{
		Name: service.Config.Image,
		Pull: true,
	}

	for _, port := range service.Config.Ports {
		cs.Ports = append(cs.Ports, types.ContainerPortSpec{
			ContainerPort: port.Container,
			Protocol:      port.Protocol,
		})
	}

	cs.Command = service.Config.Command
	cs.Entrypoint = service.Config.Entrypoint
	cs.Envs = service.Config.EnvVars
	cs.Quota = types.ContainerQuotaSpec{
		Memory: service.Config.Memory,
	}

	cs.RestartPolicy = types.ContainerRestartPolicySpec{
		Name:    "always",
		Attempt: 0,
	}

	spec.Containers = append(spec.Containers, cs)

	var state = new(types.PodState)
	state.State = service.Meta.State.State

	return spec
}

func createConfig(opts *request.ServiceConfig) (*types.ServiceConfig, error) {
	config := new(types.ServiceConfig)
	if err := patchConfig(opts, config); err != nil {
		return nil, err
	}
	return config, nil
}

func updateConfig(opts *request.ServiceConfig, config *types.ServiceConfig) error {
	if config == nil {
		config = new(types.ServiceConfig)
	}
	return patchConfig(opts, config)
}

func patchConfig(opts *request.ServiceConfig, config *types.ServiceConfig) error {

	config.Memory = int64(32)

	if opts.Memory != nil {
		config.Memory = *opts.Memory
	}

	if opts.Command != nil {
		config.Command = strings.Split(*opts.Command, " ")
	}

	if opts.Image != nil {
		config.Image = *opts.Image
	}

	if opts.Entrypoint != nil {
		config.Entrypoint = strings.Split(*opts.Entrypoint, " ")
	}

	if opts.EnvVars != nil {
		config.EnvVars = *opts.EnvVars
	}

	if opts.Ports != nil {
		config.Ports = []types.Port{}
		for _, val := range *opts.Ports {
			config.Ports = append(config.Ports, types.Port{
				Protocol:  val.Protocol,
				Container: val.Internal,
				Host:      val.External,
				Published: val.Published,
			})
		}
	}

	return nil
}
<commit_msg>Update services creation<commit_after>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2017] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package service

import (
	"context"
	"github.com/lastbackend/lastbackend/pkg/apis/types"
	ctx "github.com/lastbackend/lastbackend/pkg/daemon/context"
	"github.com/lastbackend/lastbackend/pkg/daemon/node"
	"github.com/lastbackend/lastbackend/pkg/daemon/service/routes/request"
	"github.com/lastbackend/lastbackend/pkg/util/validator"
	"github.com/satori/go.uuid"
	"strings"
	"time"
)

type service struct {
	Context   context.Context
	Namespace types.Meta
}

func New(ctx context.Context, namespace types.Meta) *service {
	return &service{
		Context:   ctx,
		Namespace: namespace,
	}
}

func (s *service) List() (*types.ServiceList, error) {
	var storage = ctx.Get().GetStorage()
	return storage.Service().ListByNamespace(s.Context, s.Namespace.ID)
}

func (s *service) Get(service string) (*types.Service, error) {

	var (
		err     error
		log     = ctx.Get().GetLogger()
		storage = ctx.Get().GetStorage()
		svc     *types.Service
	)

	if validator.IsUUID(service) {
		svc, err = storage.Service().GetByID(s.Context, s.Namespace.ID, service)
	} else {
		svc, err = storage.Service().GetByName(s.Context, s.Namespace.ID, service)
	}

	if err != nil {
		log.Error("Error: find service by name", err.Error())
		return svc, err
	}

	return svc, nil
}

func (s *service) Create(rq *request.RequestServiceCreateS) (*types.Service, error) {

	var (
		log     = ctx.Get().GetLogger()
		storage = ctx.Get().GetStorage()
		svc     = new(types.Service)
	)

	log.Debug("Service: create new service")

	svc.Meta = types.ServiceMeta{}
	svc.Meta.ID = uuid.NewV4().String()
	svc.Meta.Name = rq.Name
	svc.Meta.Region = rq.Region
	svc.Meta.Namespace = s.Namespace.Name
	svc.Meta.Description = rq.Description
	svc.Meta.Updated = time.Now()
	svc.Meta.Created = time.Now()

	svc.Meta.Replicas = 1
	svc.Pods = make(map[string]*types.Pod)

	if rq.Replicas != nil && *rq.Replicas > 0 {
		svc.Meta.Replicas = *rq.Replicas
	}

	config, err := createConfig(rq.Config)
	if err != nil {
		log.Errorf("Error: create config from request opts : %s", err.Error())
		return svc, err
	}

	svc.Config = *config

	log.Debugf("Service: Create: add pods : %d", svc.Meta.Replicas)
	for i := 0; i < svc.Meta.Replicas; i++ {
		log.Debug("Service: Create: add new pod")
		if err := s.AddPod(svc); err != nil {
			log.Errorf("Service: Create: add new pod error: %s", err.Error())
			return svc, err
		}
	}

	svc, err = storage.Service().Insert(s.Context, svc)
	if err != nil {
		log.Errorf("Error: insert service to db : %s", err.Error())
		return svc, err
	}

	return svc, nil
}

func (s *service) Update(service *types.Service, rq *request.RequestServiceUpdateS) (*types.Service, error) {

	var (
		err     error
		log     = ctx.Get().GetLogger()
		storage = ctx.Get().GetStorage()
		svc     *types.Service
	)

	log.Debug("Service: update service info and config")

	if rq.Name != "" {
		service.Meta.Name = rq.Name
	}

	if rq.Description != "" {
		service.Meta.Description = rq.Description
	}

	if rq.Domains != nil {
		service.Domains = rq.Domains
	}

	if rq.Replicas != nil {
		log.Debugf("Service: Update: set replicas: %d", *rq.Replicas)
		service.Meta.Replicas = *rq.Replicas
	}

	if rq.Config != nil {
		if err := updateConfig(rq.Config, &service.Config); err != nil {
			log.Error("Error: update service config from request opts", err)
			return svc, err
		}

		// Update pod spec
		spec := s.GenerateSpec(service)

		log.Debugf("Service: Update: pods count: %d", len(service.Pods))
		for _, pod := range service.Pods {
			log.Debugf("Service: Update: pod %s update", pod.Meta.ID)
			pod.Spec = spec
		}

	}

	s.Scale(s.Context, service)

	svc, err = storage.Service().Update(s.Context, service)
	if err != nil {
		log.Error("Error: insert service to db", err)
		return svc, err
	}

	return svc, nil
}

func (s *service) Remove(service *types.Service) error {
	var (
		log     = ctx.Get().GetLogger()
		storage = ctx.Get().GetStorage()
	)

	service.Meta.State.State = "deleting"

	if len(service.Pods) == 0 {
		if err := storage.Service().Remove(s.Context, service); err != nil {
			log.Error("Error: insert service to db", err)
			return err
		}
		return nil
	}

	for _, pod := range service.Pods {
		pod.Meta.State.State = "deleting"
	}

	if _, err := storage.Service().Update(s.Context, service); err != nil {
		log.Error("Error: insert service to db", err)
		return err
	}
	return nil
}

func (s *service) AddPod(service *types.Service) error {

	var (
		log = ctx.Get().GetLogger()
	)

	log.Debug("Create new pod state on service")

	pod := new(types.Pod)
	pod.Meta.State.State = "running"
	pod.Meta.ID = uuid.NewV4().String()
	pod.Meta.Created = time.Now()
	pod.Meta.Updated = time.Now()

	if len(service.Pods) > 0 {
		for _, p := range service.Pods {
			pod.Spec = p.Spec
			break
		}
	} else {
		pod.Spec = s.GenerateSpec(service)
	}

	n, err := node.New().Allocate(s.Context, pod.Spec)
	if err != nil {
		return err
	}

	log.Debugf("Service: Add pod: Node meta: %s", n.Meta)
	pod.Meta.Hostname = n.Meta.Hostname
	service.Pods[pod.Meta.ID] = pod

	return nil
}

func (s *service) DelPod(service *types.Service) error {

	var (
		log = ctx.Get().GetLogger()
		pod *types.Pod
	)

	log.Debug("Create new pod state on service")

	for _, pod = range service.Pods {
		if pod.Meta.State.State != "deleting" {
			pod.Meta.State.State = "deleting"
			break
		}
	}

	return nil
}

func (s *service) SetPods(c context.Context, pods []types.Pod) error {
	var (
		log     = ctx.Get().GetLogger()
		storage = ctx.Get().GetStorage()
	)

	for _, pod := range pods {

		svc, err := storage.Service().GetByPodID(c, pod.Meta.ID)
		if err != nil {
			log.Errorf("Error: get pod from db: %s", err)
			return err
		}

		if svc == nil {
			log.Debug("Serice not found")
			continue
		}

		p, e := storage.Pod().GetByID(c, svc.Meta.Namespace, svc.Meta.ID, pod.Meta.ID)

		if e != nil {
			log.Errorf("Error: get pod from db: %s", e.Error())
			return err
		}

		if p == nil {
			log.Warnf("Pod not found, skip setting: %s", pod.Meta.ID)
		}

		p.Containers = pod.Containers
		p.Meta.State = pod.Meta.State

		if p.Meta.State.State == "deleted" {
			log.Debugf("Service: Set pods: remove deleted pod: %s", p.Meta.ID)
			if err := storage.Pod().Remove(c, svc.Meta.Namespace, svc.Meta.ID, p); err != nil {
				log.Errorf("Error: set pod to db: %s", err)
				return err
			}
			delete(svc.Pods, p.Meta.ID)

			if len(svc.Pods) == 0 && svc.Meta.State.State == "deleting" {
				storage.Service().Remove(c, svc)
			}

			return nil
		}

		if err := storage.Pod().Update(c, svc.Meta.Namespace, svc.Meta.ID, p); err != nil {
			log.Errorf("Error: set pod to db: %s", err)
			return err
		}
	}

	return nil
}

func (s *service) Scale(c context.Context, service *types.Service) error {
	var (
		log      = ctx.Get().GetLogger()
		pod      *types.Pod
		replicas int
	)

	for _, pod = range service.Pods {
		if pod.Meta.State.State != "deleting" {
			replicas++
		}
	}

	log.Debugf("Service: Scale: current replicas: %d", replicas)

	if replicas == service.Meta.Replicas {
		log.Debug("Service: Replicas not needed, replicas equal")
		return nil
	}

	if replicas < service.Meta.Replicas {
		log.Debug("Service: Replicas: create a new replicas")
		for i := 0; i < (service.Meta.Replicas - replicas); i++ {
			if err := s.AddPod(service); err != nil {
				return err
			}
		}
	}

	if replicas > service.Meta.Replicas {
		log.Debug("Service: Replicas: remove  unneeded replicas")
		for i := 0; i < (replicas - service.Meta.Replicas); i++ {
			if err := s.DelPod(service); err != nil {
				return err
			}
		}
	}

	return nil
}

func (s *service) GenerateSpec(service *types.Service) types.PodSpec {

	var (
		log = ctx.Get().GetLogger()
	)

	log.Debug("Generate new node pod spec")
	var spec = types.PodSpec{}
	spec.ID = uuid.NewV4().String()
	spec.Created = time.Now()
	spec.Updated = time.Now()

	cs := new(types.ContainerSpec)
	cs.Image = types.ImageSpec{
		Name: service.Config.Image,
		Pull: true,
	}

	for _, port := range service.Config.Ports {
		cs.Ports = append(cs.Ports, types.ContainerPortSpec{
			ContainerPort: port.Container,
			Protocol:      port.Protocol,
		})
	}

	cs.Command = service.Config.Command
	cs.Entrypoint = service.Config.Entrypoint
	cs.Envs = service.Config.EnvVars
	cs.Quota = types.ContainerQuotaSpec{
		Memory: service.Config.Memory,
	}

	cs.RestartPolicy = types.ContainerRestartPolicySpec{
		Name:    "always",
		Attempt: 0,
	}

	spec.Containers = append(spec.Containers, cs)

	var state = new(types.PodState)
	state.State = service.Meta.State.State

	return spec
}

func createConfig(opts *request.ServiceConfig) (*types.ServiceConfig, error) {
	config := new(types.ServiceConfig)
	if err := patchConfig(opts, config); err != nil {
		return nil, err
	}
	return config, nil
}

func updateConfig(opts *request.ServiceConfig, config *types.ServiceConfig) error {
	if config == nil {
		config = new(types.ServiceConfig)
	}
	return patchConfig(opts, config)
}

func patchConfig(opts *request.ServiceConfig, config *types.ServiceConfig) error {

	config.Memory = int64(32)

	if opts.Memory != nil {
		config.Memory = *opts.Memory
	}

	if opts.Command != nil {
		config.Command = strings.Split(*opts.Command, " ")
	}

	if opts.Image != nil {
		config.Image = *opts.Image
	}

	if opts.Entrypoint != nil {
		config.Entrypoint = strings.Split(*opts.Entrypoint, " ")
	}

	if opts.EnvVars != nil {
		config.EnvVars = *opts.EnvVars
	}

	if opts.Ports != nil {
		config.Ports = []types.Port{}
		for _, val := range *opts.Ports {
			config.Ports = append(config.Ports, types.Port{
				Protocol:  val.Protocol,
				Container: val.Internal,
				Host:      val.External,
				Published: val.Published,
			})
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Escalator
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

type SimpleChaincode struct {
}

type Ticket struct {
	TicketID        string
	Timestamp       string // time of ticket creation
	Trainstation    string
	Platform        string
	Device          string // the device in need of repairs (i.e. left upwards escalator)
	Status          string // current ticket status (not repair status), i.e. "OPEN". TODO: rework as some sort of enum to limit input.
	TechPart        string // representing the defective part of the escalator
	ErrorID         string
	ErrorMessage    string
	ServiceProvider string // the assigned service provider thats commissioned to do the repairs
	SpEmployee      string // repairman assigned by service_provider
	SpeCommentary   string // additional commentary, optionally to be filled out by the sp_employee
	EstRepairTime   string
	RepairStatus    string
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	err := stub.PutState("counter", []byte("0"))
	if err != nil {
		return nil, err
	}
	return nil, nil
}

//Invoke is the entry point for all other asset altering functions called by an CC invocation
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	switch function {
	case "createTicket":
		return t.createTicket(stub, args)
	case "assignTicket":
		return t.assignTicket(stub, args)
	case "acceptTicket":
		return t.acceptTicket(stub, args)
	case "assignMechanic":
		return t.assignMechanic(stub, args)
	case "startJourney":
		return t.startJourney(stub, args)
	case "onArrival":
		return t.onArrival(stub, args)
	case "startRepair":
		return t.startRepair(stub, args)
	case "finishRepair":
		return t.finishRepair(stub, args)
	case "createDefaultTicket":
		return t.createDefaultTicket(stub, args)
	}

	return nil, errors.New("Received unknown function invocation: " + function)

}
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	if function == "getFullTicket" {
		return t.getFullTicket(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Received unknown function query")
}

func (t *SimpleChaincode) getFullTicket(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Wrong number of arguments. Must be (1): TicketID")
	}

	//	var ticketAsByteArr []byte

	ticketAsByteArr, err := stub.GetState(args[0])

	if err != nil {
		return nil, errors.New("Query failure for getFullTicket")
	}

	return ticketAsByteArr, nil
}

// Create a new ticket and store it on the ledger with ticket_id as key.
// args should have length 4: The fields TicketID, Timestamp, Device, TechPart, and ErrorID have to be initialised
//
func (t *SimpleChaincode) createTicket(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 8 {
		return nil, errors.New("Wrong number of arguments, must be 7: Timestamp, Trainstation, Platform, Device, TechPart, ErrorID and ErrorMessage")
	}

	idAsBytes, _ := stub.GetState("counter") // get highest current ticket id number from worldstate, increment, and set as
	str := string(idAsBytes[:])              //	TicketID for newly created Ticket & update highest running ticket number.
	idAsInt, _ := strconv.Atoi(str)          // TODO This seems unnecessarily complicated.
	idAsInt++                                //
	idAsString := strconv.Itoa(idAsInt)
	err := stub.PutState("counter", []byte(idAsString))

	var ticket = Ticket{
		TicketID:     idAsString,
		Timestamp:    args[0],
		Trainstation: args[1],
		Platform:     args[2],
		Device:       args[3],
		Status:       "EINGETROFFEN",
		TechPart:     args[4],
		ErrorID:      args[5],
		ErrorMessage: args[6],
	}

	state, _ := json.Marshal(ticket)

	stub.PutState(args[0], []byte(state))
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// Creates a default ticket. Maybe change to just call createTicket with arguments.
//
func (t *SimpleChaincode) createDefaultTicket(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {

	idAsBytes, _ := stub.GetState("counter") // get highest current ticket id number from worldstate, increment, and set as
	str := string(idAsBytes[:])              //	TicketID for newly created Ticket & update highest running ticket number.
	idAsInt, _ := strconv.Atoi(str)          // TODO This seems unnecessarily complicated.
	idAsInt++                                //
	idAsString := strconv.Itoa(idAsInt)
	err := stub.PutState("counter", []byte(idAsString))

	var ticket = Ticket{
		TicketID:     idAsString,
		Timestamp:    "2017-12-03T12:35:00.000Z",
		Trainstation: "Bonn Hbf",
		Platform:     "Gleis 5",
		Device:       "Rolltreppe nach oben",
		Status:       "Eingetroffen",
		TechPart:     "Motor RTM-X 64",
		ErrorID:      "#2356-102",
		ErrorMessage: "Totalausfall",
	}
	state, _ := json.Marshal(ticket)

	stub.PutState(args[0], []byte(state))
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// Assign an existing Ticket to a ServiceProvider. Arguments should be TicketID and the name of the serviceprovider that the ticket gets assigned to.
func (t *SimpleChaincode) assignTicket(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 2 {
		return nil, errors.New("Wrong number of arguments, must be 2: TicketID and ServiceProvider")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0]) //get ticket from world state as byte array
	if err != nil {
		return nil, err
	}
	ticket := new(Ticket)
	json.Unmarshal(state, ticket)    // translate back to struct (well, to "pointer to struct" actually
	ticket.ServiceProvider = args[1] //set new  ServiceProvider
	ticket.Status = "ZUGEWIESEN"     //update status to "assigned"
	ticket.RepairStatus = "Wird geprueft"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}

func (t *SimpleChaincode) acceptTicket(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Wrong number of arguments, must be 1: TicketID ")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.Status = "ZUGEWIESEN"
	ticket.RepairStatus = "Auftrag angenommen"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}

func (t *SimpleChaincode) assignMechanic(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 2 {
		return nil, errors.New("Wrong number of arguments, must be 2: TicketID and SpEmployee ")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.SpEmployee = args[1]
	ticket.RepairStatus = "Ticket erhalten"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}

func (t *SimpleChaincode) startJourney(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Wrong number of arguments, must be 1: TicketID")
	}

	var state []byte
	var err error
	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.RepairStatus = "Techniker in Anfahrt"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}

func (t *SimpleChaincode) onArrival(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 3 {
		return nil, errors.New("Wrong number of arguments, must be 2: TicketID,EstRepairTime and SpeCommentary ")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.SpeCommentary = args[1]
	ticket.EstRepairTime = args[2]
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}
func (t *SimpleChaincode) startRepair(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Wrong number of arguments, must be 1: TicketID")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.RepairStatus = "Reparatur begonnen"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}

func (t *SimpleChaincode) finishRepair(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Wrong number of arguments, must be 1: TicketID")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.RepairStatus = "Reparatur abgeschlossen"
	ticket.Status = "ERLEDIGT"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}
<commit_msg>add counter<commit_after>// Escalator
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

type SimpleChaincode struct {
}

type Ticket struct {
	TicketID        string
	Timestamp       string // time of ticket creation
	Trainstation    string
	Platform        string
	Device          string // the device in need of repairs (i.e. left upwards escalator)
	Status          string // current ticket status (not repair status), i.e. "OPEN". TODO: rework as some sort of enum to limit input.
	TechPart        string // representing the defective part of the escalator
	ErrorID         string
	ErrorMessage    string
	ServiceProvider string // the assigned service provider thats commissioned to do the repairs
	SpEmployee      string // repairman assigned by service_provider
	SpeCommentary   string // additional commentary, optionally to be filled out by the sp_employee
	EstRepairTime   string
	RepairStatus    string
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	err := stub.PutState("counter", []byte("0"))
	if err != nil {
		return nil, err
	}
	return nil, nil
}

//Invoke is the entry point for all other asset altering functions called by an CC invocation
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	switch function {
	case "createTicket":
		return t.createTicket(stub, args)
	case "assignTicket":
		return t.assignTicket(stub, args)
	case "acceptTicket":
		return t.acceptTicket(stub, args)
	case "assignMechanic":
		return t.assignMechanic(stub, args)
	case "startJourney":
		return t.startJourney(stub, args)
	case "onArrival":
		return t.onArrival(stub, args)
	case "startRepair":
		return t.startRepair(stub, args)
	case "finishRepair":
		return t.finishRepair(stub, args)
	case "createDefaultTicket":
		return t.createDefaultTicket(stub, args)
	}

	return nil, errors.New("Received unknown function invocation: " + function)

}
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	if function == "getFullTicket" {
		return t.getFullTicket(stub, args)
	}
	if function == "getCounter" {
		return t.getCounter(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Received unknown function query")
}

func (t *SimpleChaincode) getCounter(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	counterAsByteArr, err := stub.GetState("counter")
	if err != nil {
		return nil, errors.New("Query failure for getCounter")
	}
	return counterAsByteArr, nil
}

func (t *SimpleChaincode) getFullTicket(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Wrong number of arguments. Must be (1): TicketID")
	}

	//	var ticketAsByteArr []byte

	ticketAsByteArr, err := stub.GetState(args[0])

	if err != nil {
		return nil, errors.New("Query failure for getFullTicket")
	}

	return ticketAsByteArr, nil
}

// Create a new ticket and store it on the ledger with ticket_id as key.
// args should have length 4: The fields TicketID, Timestamp, Device, TechPart, and ErrorID have to be initialised
//
func (t *SimpleChaincode) createTicket(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 8 {
		return nil, errors.New("Wrong number of arguments, must be 7: Timestamp, Trainstation, Platform, Device, TechPart, ErrorID and ErrorMessage")
	}

	idAsBytes, _ := stub.GetState("counter") // get highest current ticket id number from worldstate, increment, and set as
	str := string(idAsBytes[:])              //	TicketID for newly created Ticket & update highest running ticket number.
	idAsInt, _ := strconv.Atoi(str)          // TODO This seems unnecessarily complicated.
	idAsInt++                                //
	idAsString := strconv.Itoa(idAsInt)
	err := stub.PutState("counter", []byte(idAsString))

	var ticket = Ticket{
		TicketID:     idAsString,
		Timestamp:    args[0],
		Trainstation: args[1],
		Platform:     args[2],
		Device:       args[3],
		Status:       "EINGETROFFEN",
		TechPart:     args[4],
		ErrorID:      args[5],
		ErrorMessage: args[6],
	}

	state, _ := json.Marshal(ticket)

	stub.PutState(args[0], []byte(state))
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// Creates a default ticket. Maybe change to just call createTicket with arguments.
//
func (t *SimpleChaincode) createDefaultTicket(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {

	idAsBytes, _ := stub.GetState("counter") // get highest current ticket id number from worldstate, increment, and set as
	str := string(idAsBytes[:])              //	TicketID for newly created Ticket & update highest running ticket number.
	idAsInt, _ := strconv.Atoi(str)          // TODO This seems unnecessarily complicated.
	idAsInt++                                //
	idAsString := strconv.Itoa(idAsInt)
	err := stub.PutState("counter", []byte(idAsString))

	var ticket = Ticket{
		TicketID:     idAsString,
		Timestamp:    "2017-12-03T12:35:00.000Z",
		Trainstation: "Bonn Hbf",
		Platform:     "Gleis 5",
		Device:       "Rolltreppe nach oben",
		Status:       "Eingetroffen",
		TechPart:     "Motor RTM-X 64",
		ErrorID:      "#2356-102",
		ErrorMessage: "Totalausfall",
	}
	state, _ := json.Marshal(ticket)

	stub.PutState(args[0], []byte(state))
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// Assign an existing Ticket to a ServiceProvider. Arguments should be TicketID and the name of the serviceprovider that the ticket gets assigned to.
func (t *SimpleChaincode) assignTicket(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 2 {
		return nil, errors.New("Wrong number of arguments, must be 2: TicketID and ServiceProvider")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0]) //get ticket from world state as byte array
	if err != nil {
		return nil, err
	}
	ticket := new(Ticket)
	json.Unmarshal(state, ticket)    // translate back to struct (well, to "pointer to struct" actually
	ticket.ServiceProvider = args[1] //set new  ServiceProvider
	ticket.Status = "ZUGEWIESEN"     //update status to "assigned"
	ticket.RepairStatus = "Wird geprueft"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}

func (t *SimpleChaincode) acceptTicket(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Wrong number of arguments, must be 1: TicketID ")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.Status = "ZUGEWIESEN"
	ticket.RepairStatus = "Auftrag angenommen"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}

func (t *SimpleChaincode) assignMechanic(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 2 {
		return nil, errors.New("Wrong number of arguments, must be 2: TicketID and SpEmployee ")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.SpEmployee = args[1]
	ticket.RepairStatus = "Ticket erhalten"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}

func (t *SimpleChaincode) startJourney(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Wrong number of arguments, must be 1: TicketID")
	}

	var state []byte
	var err error
	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.RepairStatus = "Techniker in Anfahrt"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}

func (t *SimpleChaincode) onArrival(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 3 {
		return nil, errors.New("Wrong number of arguments, must be 2: TicketID,EstRepairTime and SpeCommentary ")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.SpeCommentary = args[1]
	ticket.EstRepairTime = args[2]
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}
func (t *SimpleChaincode) startRepair(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Wrong number of arguments, must be 1: TicketID")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.RepairStatus = "Reparatur begonnen"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}

func (t *SimpleChaincode) finishRepair(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Wrong number of arguments, must be 1: TicketID")
	}

	var state []byte
	var err error

	state, err = stub.GetState(args[0])
	if err != nil {
		return nil, err
	}

	ticket := new(Ticket)
	json.Unmarshal(state, ticket)
	ticket.RepairStatus = "Reparatur abgeschlossen"
	ticket.Status = "ERLEDIGT"
	state, err = json.Marshal(ticket)
	if err != nil {
		return nil, err
	}
	stub.PutState(args[0], state) //write updated ticket to world state again

	return nil, nil
}
<|endoftext|>


<commit_before>// Copyright 2012 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package provision provides interfaces that need to be satisfied in order to
// implement a new provisioner on tsuru.
package provision

import (
	"fmt"
	"io"
)

const (
	StatusStarted    = "started"
	StatusPending    = "pending"
	StatusDown       = "down"
	StatusError      = "error"
	StatusInstalling = "installing"
	StatusCreating   = "creating"
)

// Unit represents a provision unit. Can be a machine, container or anything
// IP-addressable.
type Unit struct {
	Name    string
	AppName string
	Type    string
	Machine int
	Ip      string
	Status  string
}

// AppUnit represents a unit in an app.
type AppUnit interface {
	// Returns the name of the unit.
	GetName() string

	// Returns the number of the unit.
	GetMachine() int
}

// App represents a tsuru app.
//
// It contains only relevant information for provisioning.
type App interface {
	// Log should be used to log messages in the app.
	Log(message, source string) error

	// GetName returns the name of the app.
	GetName() string

	// GetFramework returns the framework (type) of the app. It is equivalent
	// to the Unit `Type` field.
	GetFramework() string

	// GetUnits returns all units of the app, in a slice.
	GetUnits() []AppUnit
}

// Provisioner is the basic interface of this package.
//
// Any tsuru provisioner must implement this interface in order to provision
// tsuru apps.
//
// Tsuru comes with a default provisioner: juju. One can add other provisioners
// by satisfying this interface and registering it using the function Register.
type Provisioner interface {
	// Provision is called when tsuru is creating the app.
	Provision(App) *Error

	// Destroy is called when tsuru is destroying the app.
	Destroy(App) *Error

	// ExecuteCommand runs a command in all units of the app.
	ExecuteCommand(io.Writer, App, string, ...string) error

	// CollectStatus returns information about all provisioned units. It's used
	// by tsuru collector when updating the status of apps in the database.
	CollectStatus() ([]Unit, *Error)
}

var provisioners = make(map[string]Provisioner)

// Register registers a new provisioner in the Provisioner registry.
func Register(name string, p Provisioner) {
	provisioners[name] = p
}

// Get gets the named provisioner from the registry.
func Get(name string) (Provisioner, error) {
	p, ok := provisioners[name]
	if !ok {
		return nil, fmt.Errorf("Unknown provisioner: %q.", name)
	}
	return p, nil
}

type Error struct {
	Reason string
	Err    error
}

func (e *Error) Error() string {
	var err string
	if e.Err != nil {
		err = e.Err.Error() + ": " + e.Reason
	} else {
		err = e.Reason
	}
	return err
}
<commit_msg>provision: rename method in App interface<commit_after>// Copyright 2012 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package provision provides interfaces that need to be satisfied in order to
// implement a new provisioner on tsuru.
package provision

import (
	"fmt"
	"io"
)

const (
	StatusStarted    = "started"
	StatusPending    = "pending"
	StatusDown       = "down"
	StatusError      = "error"
	StatusInstalling = "installing"
	StatusCreating   = "creating"
)

// Unit represents a provision unit. Can be a machine, container or anything
// IP-addressable.
type Unit struct {
	Name    string
	AppName string
	Type    string
	Machine int
	Ip      string
	Status  string
}

// AppUnit represents a unit in an app.
type AppUnit interface {
	// Returns the name of the unit.
	GetName() string

	// Returns the number of the unit.
	GetMachine() int
}

// App represents a tsuru app.
//
// It contains only relevant information for provisioning.
type App interface {
	// Log should be used to log messages in the app.
	Log(message, source string) error

	// GetName returns the name of the app.
	GetName() string

	// GetFramework returns the framework (type) of the app. It is equivalent
	// to the Unit `Type` field.
	GetFramework() string

	// GetUnits returns all units of the app, in a slice.
	GetProvisionUnits() []AppUnit
}

// Provisioner is the basic interface of this package.
//
// Any tsuru provisioner must implement this interface in order to provision
// tsuru apps.
//
// Tsuru comes with a default provisioner: juju. One can add other provisioners
// by satisfying this interface and registering it using the function Register.
type Provisioner interface {
	// Provision is called when tsuru is creating the app.
	Provision(App) *Error

	// Destroy is called when tsuru is destroying the app.
	Destroy(App) *Error

	// ExecuteCommand runs a command in all units of the app.
	ExecuteCommand(io.Writer, App, string, ...string) error

	// CollectStatus returns information about all provisioned units. It's used
	// by tsuru collector when updating the status of apps in the database.
	CollectStatus() ([]Unit, *Error)
}

var provisioners = make(map[string]Provisioner)

// Register registers a new provisioner in the Provisioner registry.
func Register(name string, p Provisioner) {
	provisioners[name] = p
}

// Get gets the named provisioner from the registry.
func Get(name string) (Provisioner, error) {
	p, ok := provisioners[name]
	if !ok {
		return nil, fmt.Errorf("Unknown provisioner: %q.", name)
	}
	return p, nil
}

type Error struct {
	Reason string
	Err    error
}

func (e *Error) Error() string {
	var err string
	if e.Err != nil {
		err = e.Err.Error() + ": " + e.Reason
	} else {
		err = e.Reason
	}
	return err
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 features

import (
	"k8s.io/apimachinery/pkg/util/runtime"

	utilfeature "k8s.io/apiserver/pkg/util/feature"
	"k8s.io/component-base/featuregate"
)

const (
	// Every feature gate should add method here following this template:
	//
	// // owner: @username
	// // alpha: v1.4
	// MyFeature() bool

	// owner: @tallclair
	// alpha: v1.5
	// beta: v1.6
	// deprecated: v1.18
	//
	// StreamingProxyRedirects controls whether the apiserver should intercept (and follow)
	// redirects from the backend (Kubelet) for streaming requests (exec/attach/port-forward).
	//
	// This feature is deprecated, and will be removed in v1.22.
	StreamingProxyRedirects featuregate.Feature = "StreamingProxyRedirects"

	// owner: @tallclair
	// alpha: v1.12
	// beta: v1.14
	//
	// ValidateProxyRedirects controls whether the apiserver should validate that redirects are only
	// followed to the same host. Only used if StreamingProxyRedirects is enabled.
	ValidateProxyRedirects featuregate.Feature = "ValidateProxyRedirects"

	// owner: @tallclair
	// alpha: v1.7
	// beta: v1.8
	// GA: v1.12
	//
	// AdvancedAuditing enables a much more general API auditing pipeline, which includes support for
	// pluggable output backends and an audit policy specifying how different requests should be
	// audited.
	AdvancedAuditing featuregate.Feature = "AdvancedAuditing"

	// owner: @ilackams
	// alpha: v1.7
	// beta: v1.16
	//
	// Enables compression of REST responses (GET and LIST only)
	APIResponseCompression featuregate.Feature = "APIResponseCompression"

	// owner: @smarterclayton
	// alpha: v1.8
	// beta: v1.9
	//
	// Allow API clients to retrieve resource lists in chunks rather than
	// all at once.
	APIListChunking featuregate.Feature = "APIListChunking"

	// owner: @apelisse
	// alpha: v1.12
	// beta: v1.13
	// stable: v1.18
	//
	// Allow requests to be processed but not stored, so that
	// validation, merging, mutation can be tested without
	// committing.
	DryRun featuregate.Feature = "DryRun"

	// owner: @caesarxuchao
	// alpha: v1.15
	// beta: v1.16
	//
	// Allow apiservers to show a count of remaining items in the response
	// to a chunking list request.
	RemainingItemCount featuregate.Feature = "RemainingItemCount"

	// owner: @apelisse, @lavalamp
	// alpha: v1.14
	// beta: v1.16
	//
	// Server-side apply. Merging happens on the server.
	ServerSideApply featuregate.Feature = "ServerSideApply"

	// owner: @caesarxuchao
	// alpha: v1.14
	// beta: v1.15
	//
	// Allow apiservers to expose the storage version hash in the discovery
	// document.
	StorageVersionHash featuregate.Feature = "StorageVersionHash"

	// owner: @caesarxuchao @roycaihw
	// alpha: v1.20
	//
	// Enable the storage version API.
	StorageVersionAPI featuregate.Feature = "StorageVersionAPI"

	// owner: @wojtek-t
	// alpha: v1.15
	// beta: v1.16
	// GA: v1.17
	//
	// Enables support for watch bookmark events.
	WatchBookmark featuregate.Feature = "WatchBookmark"

	// owner: @MikeSpreitzer @yue9944882
	// alpha: v1.15
	//
	//
	// Enables managing request concurrency with prioritization and fairness at each server
	APIPriorityAndFairness featuregate.Feature = "APIPriorityAndFairness"

	// owner: @wojtek-t
	// alpha: v1.16
	// beta: v1.20
	//
	// Deprecates and removes SelfLink from ObjectMeta and ListMeta.
	RemoveSelfLink featuregate.Feature = "RemoveSelfLink"

	// owner: @shaloulcy, @wojtek-t
	// alpha: v1.18
	// beta: v1.19
	// GA: v1.20
	//
	// Allows label and field based indexes in apiserver watch cache to accelerate list operations.
	SelectorIndex featuregate.Feature = "SelectorIndex"

	// owner: @liggitt
	// beta: v1.19
	//
	// Allows sending warning headers in API responses.
	WarningHeaders featuregate.Feature = "WarningHeaders"

	// owner: @wojtek-t
	// alpha: v1.20
	//
	// Allows for updating watchcache resource version with progress notify events.
	EfficientWatchResumption featuregate.Feature = "EfficientWatchResumption"

	// owner: @roycaihw
	// alpha: v1.20
	//
	// Assigns each kube-apiserver an ID in a cluster.
	APIServerIdentity featuregate.Feature = "APIServerIdentity"
)

func init() {
	runtime.Must(utilfeature.DefaultMutableFeatureGate.Add(defaultKubernetesFeatureGates))
}

// defaultKubernetesFeatureGates consists of all known Kubernetes-specific feature keys.
// To add a new feature, define a key for it above and add it here. The features will be
// available throughout Kubernetes binaries.
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
	StreamingProxyRedirects:  {Default: true, PreRelease: featuregate.Deprecated},
	ValidateProxyRedirects:   {Default: true, PreRelease: featuregate.Beta},
	AdvancedAuditing:         {Default: true, PreRelease: featuregate.GA},
	APIResponseCompression:   {Default: true, PreRelease: featuregate.Beta},
	APIListChunking:          {Default: true, PreRelease: featuregate.Beta},
	DryRun:                   {Default: true, PreRelease: featuregate.GA},
	RemainingItemCount:       {Default: true, PreRelease: featuregate.Beta},
	ServerSideApply:          {Default: true, PreRelease: featuregate.Beta},
	StorageVersionHash:       {Default: true, PreRelease: featuregate.Beta},
	StorageVersionAPI:        {Default: false, PreRelease: featuregate.Alpha},
	WatchBookmark:            {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
	APIPriorityAndFairness:   {Default: true, PreRelease: featuregate.Beta},
	RemoveSelfLink:           {Default: true, PreRelease: featuregate.Beta},
	SelectorIndex:            {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
	WarningHeaders:           {Default: true, PreRelease: featuregate.Beta},
	EfficientWatchResumption: {Default: false, PreRelease: featuregate.Alpha},
	APIServerIdentity:        {Default: false, PreRelease: featuregate.Alpha},
}
<commit_msg>Promote efficient watch resumption to beta<commit_after>/*
Copyright 2017 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 features

import (
	"k8s.io/apimachinery/pkg/util/runtime"

	utilfeature "k8s.io/apiserver/pkg/util/feature"
	"k8s.io/component-base/featuregate"
)

const (
	// Every feature gate should add method here following this template:
	//
	// // owner: @username
	// // alpha: v1.4
	// MyFeature() bool

	// owner: @tallclair
	// alpha: v1.5
	// beta: v1.6
	// deprecated: v1.18
	//
	// StreamingProxyRedirects controls whether the apiserver should intercept (and follow)
	// redirects from the backend (Kubelet) for streaming requests (exec/attach/port-forward).
	//
	// This feature is deprecated, and will be removed in v1.22.
	StreamingProxyRedirects featuregate.Feature = "StreamingProxyRedirects"

	// owner: @tallclair
	// alpha: v1.12
	// beta: v1.14
	//
	// ValidateProxyRedirects controls whether the apiserver should validate that redirects are only
	// followed to the same host. Only used if StreamingProxyRedirects is enabled.
	ValidateProxyRedirects featuregate.Feature = "ValidateProxyRedirects"

	// owner: @tallclair
	// alpha: v1.7
	// beta: v1.8
	// GA: v1.12
	//
	// AdvancedAuditing enables a much more general API auditing pipeline, which includes support for
	// pluggable output backends and an audit policy specifying how different requests should be
	// audited.
	AdvancedAuditing featuregate.Feature = "AdvancedAuditing"

	// owner: @ilackams
	// alpha: v1.7
	// beta: v1.16
	//
	// Enables compression of REST responses (GET and LIST only)
	APIResponseCompression featuregate.Feature = "APIResponseCompression"

	// owner: @smarterclayton
	// alpha: v1.8
	// beta: v1.9
	//
	// Allow API clients to retrieve resource lists in chunks rather than
	// all at once.
	APIListChunking featuregate.Feature = "APIListChunking"

	// owner: @apelisse
	// alpha: v1.12
	// beta: v1.13
	// stable: v1.18
	//
	// Allow requests to be processed but not stored, so that
	// validation, merging, mutation can be tested without
	// committing.
	DryRun featuregate.Feature = "DryRun"

	// owner: @caesarxuchao
	// alpha: v1.15
	// beta: v1.16
	//
	// Allow apiservers to show a count of remaining items in the response
	// to a chunking list request.
	RemainingItemCount featuregate.Feature = "RemainingItemCount"

	// owner: @apelisse, @lavalamp
	// alpha: v1.14
	// beta: v1.16
	//
	// Server-side apply. Merging happens on the server.
	ServerSideApply featuregate.Feature = "ServerSideApply"

	// owner: @caesarxuchao
	// alpha: v1.14
	// beta: v1.15
	//
	// Allow apiservers to expose the storage version hash in the discovery
	// document.
	StorageVersionHash featuregate.Feature = "StorageVersionHash"

	// owner: @caesarxuchao @roycaihw
	// alpha: v1.20
	//
	// Enable the storage version API.
	StorageVersionAPI featuregate.Feature = "StorageVersionAPI"

	// owner: @wojtek-t
	// alpha: v1.15
	// beta: v1.16
	// GA: v1.17
	//
	// Enables support for watch bookmark events.
	WatchBookmark featuregate.Feature = "WatchBookmark"

	// owner: @MikeSpreitzer @yue9944882
	// alpha: v1.15
	//
	//
	// Enables managing request concurrency with prioritization and fairness at each server
	APIPriorityAndFairness featuregate.Feature = "APIPriorityAndFairness"

	// owner: @wojtek-t
	// alpha: v1.16
	// beta: v1.20
	//
	// Deprecates and removes SelfLink from ObjectMeta and ListMeta.
	RemoveSelfLink featuregate.Feature = "RemoveSelfLink"

	// owner: @shaloulcy, @wojtek-t
	// alpha: v1.18
	// beta: v1.19
	// GA: v1.20
	//
	// Allows label and field based indexes in apiserver watch cache to accelerate list operations.
	SelectorIndex featuregate.Feature = "SelectorIndex"

	// owner: @liggitt
	// beta: v1.19
	//
	// Allows sending warning headers in API responses.
	WarningHeaders featuregate.Feature = "WarningHeaders"

	// owner: @wojtek-t
	// alpha: v1.20
	// beta: v1.21
	//
	// Allows for updating watchcache resource version with progress notify events.
	EfficientWatchResumption featuregate.Feature = "EfficientWatchResumption"

	// owner: @roycaihw
	// alpha: v1.20
	//
	// Assigns each kube-apiserver an ID in a cluster.
	APIServerIdentity featuregate.Feature = "APIServerIdentity"
)

func init() {
	runtime.Must(utilfeature.DefaultMutableFeatureGate.Add(defaultKubernetesFeatureGates))
}

// defaultKubernetesFeatureGates consists of all known Kubernetes-specific feature keys.
// To add a new feature, define a key for it above and add it here. The features will be
// available throughout Kubernetes binaries.
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
	StreamingProxyRedirects:  {Default: true, PreRelease: featuregate.Deprecated},
	ValidateProxyRedirects:   {Default: true, PreRelease: featuregate.Beta},
	AdvancedAuditing:         {Default: true, PreRelease: featuregate.GA},
	APIResponseCompression:   {Default: true, PreRelease: featuregate.Beta},
	APIListChunking:          {Default: true, PreRelease: featuregate.Beta},
	DryRun:                   {Default: true, PreRelease: featuregate.GA},
	RemainingItemCount:       {Default: true, PreRelease: featuregate.Beta},
	ServerSideApply:          {Default: true, PreRelease: featuregate.Beta},
	StorageVersionHash:       {Default: true, PreRelease: featuregate.Beta},
	StorageVersionAPI:        {Default: false, PreRelease: featuregate.Alpha},
	WatchBookmark:            {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
	APIPriorityAndFairness:   {Default: true, PreRelease: featuregate.Beta},
	RemoveSelfLink:           {Default: true, PreRelease: featuregate.Beta},
	SelectorIndex:            {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
	WarningHeaders:           {Default: true, PreRelease: featuregate.Beta},
	EfficientWatchResumption: {Default: true, PreRelease: featuregate.Beta},
	APIServerIdentity:        {Default: false, PreRelease: featuregate.Alpha},
}
<|endoftext|>