<commit_before>/*
Copyright 2018 The Ceph-CSI 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 rbd

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/ceph/ceph-csi/pkg/util"

	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/klog"
)

const (
	rbdTonbd  = "rbd-nbd"
	moduleNbd = "nbd"

	accessTypeKRbd = "krbd"
	accessTypeNbd  = "nbd"

	rbd = "rbd"

	// Output strings returned during invocation of "rbd unmap --device-type... <imageSpec>" when
	// image is not found to be mapped. Used to ignore errors when attempting to unmap such images.
	// The %s format specifier should contain the <imageSpec> string
	// NOTE: When using devicePath instead of imageSpec, the error strings are different
	rbdUnmapCmdkRbdMissingMap = "rbd: %s: not a mapped image or snapshot"
	rbdUnmapCmdNbdMissingMap  = "rbd-nbd: %s is not mapped"
)

var hasNBD = false

func init() {
	hasNBD = checkRbdNbdTools()
}

// rbdDeviceInfo strongly typed JSON spec for rbd device list output (of type krbd)
type rbdDeviceInfo struct {
	ID     string `json:"id"`
	Pool   string `json:"pool"`
	Name   string `json:"name"`
	Device string `json:"device"`
}

// nbdDeviceInfo strongly typed JSON spec for rbd-nbd device list output (of type nbd)
// NOTE: There is a bug in rbd output that returns id as number for nbd, and string for krbd, thus
// requiring 2 different JSON structures to unmarshal the output.
// NOTE: image key is "name" in krbd output and "image" in nbd output, which is another difference
type nbdDeviceInfo struct {
	ID     int64  `json:"id"`
	Pool   string `json:"pool"`
	Name   string `json:"image"`
	Device string `json:"device"`
}

// rbdGetDeviceList queries rbd about mapped devices and returns a list of rbdDeviceInfo
// It will selectively list devices mapped using krbd or nbd as specified by accessType
func rbdGetDeviceList(accessType string) ([]rbdDeviceInfo, error) {
	// rbd device list --format json --device-type [krbd|nbd]
	var (
		rbdDeviceList []rbdDeviceInfo
		nbdDeviceList []nbdDeviceInfo
	)

	stdout, _, err := util.ExecCommand(rbd, "device", "list", "--format="+"json", "--device-type", accessType)
	if err != nil {
		return nil, fmt.Errorf("error getting device list from rbd for devices of type (%s): (%v)", accessType, err)
	}

	if accessType == accessTypeKRbd {
		err = json.Unmarshal(stdout, &rbdDeviceList)
	} else {
		err = json.Unmarshal(stdout, &nbdDeviceList)
	}
	if err != nil {
		return nil, fmt.Errorf("error to parse JSON output of device list for devices of type (%s): (%v)", accessType, err)
	}

	// convert output to a rbdDeviceInfo list for consumers
	if accessType == accessTypeNbd {
		for _, device := range nbdDeviceList {
			rbdDeviceList = append(
				rbdDeviceList,
				rbdDeviceInfo{
					ID:     strconv.FormatInt(device.ID, 10),
					Pool:   device.Pool,
					Name:   device.Name,
					Device: device.Device,
				})
		}
	}

	return rbdDeviceList, nil
}

// findDeviceMappingImage finds a devicePath, if available, based on image spec (pool/image) on the node.
func findDeviceMappingImage(pool, image string, useNbdDriver bool) (string, bool) {
	accessType := accessTypeKRbd
	if useNbdDriver {
		accessType = accessTypeNbd
	}

	rbdDeviceList, err := rbdGetDeviceList(accessType)
	if err != nil {
		klog.Warningf("failed to determine if image (%s/%s) is mapped to a device (%v)", pool, image, err)
		return "", false
	}

	for _, device := range rbdDeviceList {
		if device.Name == image && device.Pool == pool {
			return device.Device, true
		}
	}

	return "", false
}

// Stat a path, if it doesn't exist, retry maxRetries times.
func waitForPath(pool, image string, maxRetries int, useNbdDriver bool) (string, bool) {
	for i := 0; i < maxRetries; i++ {
		if i != 0 {
			time.Sleep(time.Second)
		}

		device, found := findDeviceMappingImage(pool, image, useNbdDriver)
		if found {
			return device, found
		}
	}

	return "", false
}

// Check if rbd-nbd tools are installed.
func checkRbdNbdTools() bool {
	_, err := execCommand("modprobe", []string{moduleNbd})
	if err != nil {
		klog.V(3).Infof("rbd-nbd: nbd modprobe failed with error %v", err)
		return false
	}
	if _, err := execCommand(rbdTonbd, []string{"--version"}); err != nil {
		klog.V(3).Infof("rbd-nbd: running rbd-nbd --version failed with error %v", err)
		return false
	}
	klog.V(3).Infof("rbd-nbd tools were found.")
	return true
}

func attachRBDImage(volOptions *rbdVolume, cr *util.Credentials) (string, error) {
	var err error

	image := volOptions.RbdImageName
	useNBD := false
	if volOptions.Mounter == rbdTonbd && hasNBD {
		useNBD = true
	}

	devicePath, found := waitForPath(volOptions.Pool, image, 1, useNBD)
	if !found {
		backoff := wait.Backoff{
			Duration: rbdImageWatcherInitDelay,
			Factor:   rbdImageWatcherFactor,
			Steps:    rbdImageWatcherSteps,
		}

		err = waitForrbdImage(backoff, volOptions, cr)

		if err != nil {
			return "", err
		}
		devicePath, err = createPath(volOptions, cr)
	}

	return devicePath, err
}

func createPath(volOpt *rbdVolume, cr *util.Credentials) (string, error) {
	image := volOpt.RbdImageName
	imagePath := fmt.Sprintf("%s/%s", volOpt.Pool, image)

	klog.V(5).Infof("rbd: map mon %s", volOpt.Monitors)

	// Map options
	mapOptions := []string{
		"--id", cr.ID,
		"-m", volOpt.Monitors,
		"--keyfile=" + cr.KeyFile,
		"map", imagePath,
	}

	// Choose access protocol
	accessType := accessTypeKRbd
	if volOpt.Mounter == rbdTonbd && hasNBD {
		accessType = accessTypeNbd
	}

	// Update options with device type selection
	mapOptions = append(mapOptions, "--device-type", accessType)

	// Execute map
	output, err := execCommand(rbd, mapOptions)
	if err != nil {
		klog.Warningf("rbd: map error %v, rbd output: %s", err, string(output))
		return "", fmt.Errorf("rbd: map failed %v, rbd output: %s", err, string(output))
	}
	devicePath := strings.TrimSuffix(string(output), "\n")

	return devicePath, nil
}

func waitForrbdImage(backoff wait.Backoff, volOptions *rbdVolume, cr *util.Credentials) error {
	image := volOptions.RbdImageName
	imagePath := fmt.Sprintf("%s/%s", volOptions.Pool, image)

	err := wait.ExponentialBackoff(backoff, func() (bool, error) {
		used, rbdOutput, err := rbdStatus(volOptions, cr)
		if err != nil {
			return false, fmt.Errorf("fail to check rbd image status with: (%v), rbd output: (%s)", err, rbdOutput)
		}
		if (volOptions.DisableInUseChecks) && (used) {
			klog.V(2).Info("valid multi-node attach requested, ignoring watcher in-use result")
			return used, nil
		}
		return !used, nil
	})
	// return error if rbd image has not become available for the specified timeout
	if err == wait.ErrWaitTimeout {
		return fmt.Errorf("rbd image %s is still being used", imagePath)
	}
	// return error if any other errors were encountered during waiting for the image to become available
	return err
}

func detachRBDDevice(devicePath string) error {
	nbdType := false
	if strings.HasPrefix(devicePath, "/dev/nbd") {
		nbdType = true
	}

	return detachRBDImageOrDeviceSpec(devicePath, false, nbdType)
}

// detachRBDImageOrDeviceSpec detaches an rbd imageSpec or devicePath, with additional checking
// when imageSpec is used to decide if image is already unmapped
func detachRBDImageOrDeviceSpec(imageOrDeviceSpec string, isImageSpec, ndbType bool) error {
	var err error
	var output []byte

	accessType := accessTypeKRbd
	if ndbType {
		accessType = accessTypeNbd
	}
	options := []string{"unmap", "--device-type", accessType, imageOrDeviceSpec}

	output, err = execCommand(rbd, options)
	if err != nil {
		// Messages for krbd and nbd differ, hence checking either of them for missing mapping
		// This is not applicable when a device path is passed in
		if isImageSpec &&
			(strings.Contains(string(output), fmt.Sprintf(rbdUnmapCmdkRbdMissingMap, imageOrDeviceSpec)) ||
				strings.Contains(string(output), fmt.Sprintf(rbdUnmapCmdNbdMissingMap, imageOrDeviceSpec))) {
			// Devices found not to be mapped are treated as a successful detach
			klog.Infof("image or device spec (%s) not mapped", imageOrDeviceSpec)
			return nil
		}
		return fmt.Errorf("rbd: unmap for spec (%s) failed (%v): (%s)", imageOrDeviceSpec, err, string(output))
	}

	return nil
}
<commit_msg>unmap rbd image if connection timeout.<commit_after>/*
Copyright 2018 The Ceph-CSI 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 rbd

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/ceph/ceph-csi/pkg/util"

	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/klog"
)

const (
	rbdTonbd  = "rbd-nbd"
	moduleNbd = "nbd"

	accessTypeKRbd = "krbd"
	accessTypeNbd  = "nbd"

	rbd = "rbd"

	// Output strings returned during invocation of "rbd unmap --device-type... <imageSpec>" when
	// image is not found to be mapped. Used to ignore errors when attempting to unmap such images.
	// The %s format specifier should contain the <imageSpec> string
	// NOTE: When using devicePath instead of imageSpec, the error strings are different
	rbdUnmapCmdkRbdMissingMap = "rbd: %s: not a mapped image or snapshot"
	rbdUnmapCmdNbdMissingMap  = "rbd-nbd: %s is not mapped"
	rbdMapConnectionTimeout   = "Connection timed out"
)

var hasNBD = false

func init() {
	hasNBD = checkRbdNbdTools()
}

// rbdDeviceInfo strongly typed JSON spec for rbd device list output (of type krbd)
type rbdDeviceInfo struct {
	ID     string `json:"id"`
	Pool   string `json:"pool"`
	Name   string `json:"name"`
	Device string `json:"device"`
}

// nbdDeviceInfo strongly typed JSON spec for rbd-nbd device list output (of type nbd)
// NOTE: There is a bug in rbd output that returns id as number for nbd, and string for krbd, thus
// requiring 2 different JSON structures to unmarshal the output.
// NOTE: image key is "name" in krbd output and "image" in nbd output, which is another difference
type nbdDeviceInfo struct {
	ID     int64  `json:"id"`
	Pool   string `json:"pool"`
	Name   string `json:"image"`
	Device string `json:"device"`
}

// rbdGetDeviceList queries rbd about mapped devices and returns a list of rbdDeviceInfo
// It will selectively list devices mapped using krbd or nbd as specified by accessType
func rbdGetDeviceList(accessType string) ([]rbdDeviceInfo, error) {
	// rbd device list --format json --device-type [krbd|nbd]
	var (
		rbdDeviceList []rbdDeviceInfo
		nbdDeviceList []nbdDeviceInfo
	)

	stdout, _, err := util.ExecCommand(rbd, "device", "list", "--format="+"json", "--device-type", accessType)
	if err != nil {
		return nil, fmt.Errorf("error getting device list from rbd for devices of type (%s): (%v)", accessType, err)
	}

	if accessType == accessTypeKRbd {
		err = json.Unmarshal(stdout, &rbdDeviceList)
	} else {
		err = json.Unmarshal(stdout, &nbdDeviceList)
	}
	if err != nil {
		return nil, fmt.Errorf("error to parse JSON output of device list for devices of type (%s): (%v)", accessType, err)
	}

	// convert output to a rbdDeviceInfo list for consumers
	if accessType == accessTypeNbd {
		for _, device := range nbdDeviceList {
			rbdDeviceList = append(
				rbdDeviceList,
				rbdDeviceInfo{
					ID:     strconv.FormatInt(device.ID, 10),
					Pool:   device.Pool,
					Name:   device.Name,
					Device: device.Device,
				})
		}
	}

	return rbdDeviceList, nil
}

// findDeviceMappingImage finds a devicePath, if available, based on image spec (pool/image) on the node.
func findDeviceMappingImage(pool, image string, useNbdDriver bool) (string, bool) {
	accessType := accessTypeKRbd
	if useNbdDriver {
		accessType = accessTypeNbd
	}

	rbdDeviceList, err := rbdGetDeviceList(accessType)
	if err != nil {
		klog.Warningf("failed to determine if image (%s/%s) is mapped to a device (%v)", pool, image, err)
		return "", false
	}

	for _, device := range rbdDeviceList {
		if device.Name == image && device.Pool == pool {
			return device.Device, true
		}
	}

	return "", false
}

// Stat a path, if it doesn't exist, retry maxRetries times.
func waitForPath(pool, image string, maxRetries int, useNbdDriver bool) (string, bool) {
	for i := 0; i < maxRetries; i++ {
		if i != 0 {
			time.Sleep(time.Second)
		}

		device, found := findDeviceMappingImage(pool, image, useNbdDriver)
		if found {
			return device, found
		}
	}

	return "", false
}

// Check if rbd-nbd tools are installed.
func checkRbdNbdTools() bool {
	_, err := execCommand("modprobe", []string{moduleNbd})
	if err != nil {
		klog.V(3).Infof("rbd-nbd: nbd modprobe failed with error %v", err)
		return false
	}
	if _, err := execCommand(rbdTonbd, []string{"--version"}); err != nil {
		klog.V(3).Infof("rbd-nbd: running rbd-nbd --version failed with error %v", err)
		return false
	}
	klog.V(3).Infof("rbd-nbd tools were found.")
	return true
}

func attachRBDImage(volOptions *rbdVolume, cr *util.Credentials) (string, error) {
	var err error

	image := volOptions.RbdImageName
	useNBD := false
	if volOptions.Mounter == rbdTonbd && hasNBD {
		useNBD = true
	}

	devicePath, found := waitForPath(volOptions.Pool, image, 1, useNBD)
	if !found {
		backoff := wait.Backoff{
			Duration: rbdImageWatcherInitDelay,
			Factor:   rbdImageWatcherFactor,
			Steps:    rbdImageWatcherSteps,
		}

		err = waitForrbdImage(backoff, volOptions, cr)

		if err != nil {
			return "", err
		}
		devicePath, err = createPath(volOptions, cr)
	}

	return devicePath, err
}

func createPath(volOpt *rbdVolume, cr *util.Credentials) (string, error) {
	isNbd := false
	image := volOpt.RbdImageName
	imagePath := fmt.Sprintf("%s/%s", volOpt.Pool, image)

	klog.V(5).Infof("rbd: map mon %s", volOpt.Monitors)

	// Map options
	mapOptions := []string{
		"--id", cr.ID,
		"-m", volOpt.Monitors,
		"--keyfile=" + cr.KeyFile,
		"map", imagePath,
	}

	// Choose access protocol
	accessType := accessTypeKRbd
	if volOpt.Mounter == rbdTonbd && hasNBD {
		isNbd = true
		accessType = accessTypeNbd
	}

	// Update options with device type selection
	mapOptions = append(mapOptions, "--device-type", accessType)

	// Execute map
	output, err := execCommand(rbd, mapOptions)
	if err != nil {
		klog.Warningf("rbd: map error %v, rbd output: %s", err, string(output))
		// unmap rbd image if connection timeout
		if strings.Contains(err.Error(), rbdMapConnectionTimeout) {
			detErr := detachRBDImageOrDeviceSpec(imagePath, true, isNbd)
			if detErr != nil {
				klog.Warningf("rbd: %s unmap error %v", imagePath, detErr)
			}
		}
		return "", fmt.Errorf("rbd: map failed %v, rbd output: %s", err, string(output))
	}
	devicePath := strings.TrimSuffix(string(output), "\n")

	return devicePath, nil
}

func waitForrbdImage(backoff wait.Backoff, volOptions *rbdVolume, cr *util.Credentials) error {
	image := volOptions.RbdImageName
	imagePath := fmt.Sprintf("%s/%s", volOptions.Pool, image)

	err := wait.ExponentialBackoff(backoff, func() (bool, error) {
		used, rbdOutput, err := rbdStatus(volOptions, cr)
		if err != nil {
			return false, fmt.Errorf("fail to check rbd image status with: (%v), rbd output: (%s)", err, rbdOutput)
		}
		if (volOptions.DisableInUseChecks) && (used) {
			klog.V(2).Info("valid multi-node attach requested, ignoring watcher in-use result")
			return used, nil
		}
		return !used, nil
	})
	// return error if rbd image has not become available for the specified timeout
	if err == wait.ErrWaitTimeout {
		return fmt.Errorf("rbd image %s is still being used", imagePath)
	}
	// return error if any other errors were encountered during waiting for the image to become available
	return err
}

func detachRBDDevice(devicePath string) error {
	nbdType := false
	if strings.HasPrefix(devicePath, "/dev/nbd") {
		nbdType = true
	}

	return detachRBDImageOrDeviceSpec(devicePath, false, nbdType)
}

// detachRBDImageOrDeviceSpec detaches an rbd imageSpec or devicePath, with additional checking
// when imageSpec is used to decide if image is already unmapped
func detachRBDImageOrDeviceSpec(imageOrDeviceSpec string, isImageSpec, ndbType bool) error {
	var err error
	var output []byte

	accessType := accessTypeKRbd
	if ndbType {
		accessType = accessTypeNbd
	}
	options := []string{"unmap", "--device-type", accessType, imageOrDeviceSpec}

	output, err = execCommand(rbd, options)
	if err != nil {
		// Messages for krbd and nbd differ, hence checking either of them for missing mapping
		// This is not applicable when a device path is passed in
		if isImageSpec &&
			(strings.Contains(string(output), fmt.Sprintf(rbdUnmapCmdkRbdMissingMap, imageOrDeviceSpec)) ||
				strings.Contains(string(output), fmt.Sprintf(rbdUnmapCmdNbdMissingMap, imageOrDeviceSpec))) {
			// Devices found not to be mapped are treated as a successful detach
			klog.Infof("image or device spec (%s) not mapped", imageOrDeviceSpec)
			return nil
		}
		return fmt.Errorf("rbd: unmap for spec (%s) failed (%v): (%s)", imageOrDeviceSpec, err, string(output))
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	. "github.com/limetext/text"
	"os"
	"reflect"
	"testing"
)

func TestOnSelectionModified(t *testing.T) {
	var res *RegionSet

	callCount := 0

	w := GetEditor().NewWindow()
	defer w.Close()

	v := w.NewFile()
	defer func() {
		v.SetScratch(true)
		v.Close()
	}()

	OnSelectionModified.Add(func(v *View) {
		res = v.Sel()
		callCount++
	})

	edit := v.BeginEdit()
	v.Insert(edit, 0, "abcd")
	v.EndEdit(edit)
	if callCount != 1 {
		t.Fatalf("%d != 1", callCount)
	}
	if !reflect.DeepEqual(res.Regions(), []Region{{4, 4}}) {
		t.Errorf("%v", res.Regions())
	}

	edit = v.BeginEdit()
	v.Sel().Adjust(4, -1)
	v.EndEdit(edit)

	if callCount != 2 {
		t.Fatalf("%d != 2", callCount)
	}
	if !reflect.DeepEqual(res.Regions(), []Region{{3, 3}}) {
		t.Errorf("%v", res.Regions())
	}

	edit = v.BeginEdit()
	v.EndEdit(edit)

	if callCount != 2 {
		t.Fatalf("%d != 2", callCount)
	}
	if !reflect.DeepEqual(res.Regions(), []Region{{3, 3}}) {
		t.Errorf("%v", res.Regions())
	}
}

func TestOnPreSave(t *testing.T) {
	testfile := "testdata/test_event.txt"
	callCount := 0

	w := GetEditor().NewWindow()
	defer w.Close()

	v := w.NewFile()
	defer v.Close()

	OnPreSave.Add(func(v *View) {
		callCount++
	})
	edit := v.BeginEdit()
	v.Insert(edit, 0, "abcd")
	v.EndEdit(edit)
	if err := v.SaveAs(testfile); err != nil {
		t.Fatal("Could not save the view")
	}
	if callCount != 1 {
		t.Fatalf("%d != 1", callCount)
	}
	v.Buffer().SetFileName(testfile)
	if err := v.Save(); err != nil {
		t.Fatalf("Could not save the view %s", err)
	}
	if callCount != 2 {
		t.Fatalf("%d != 2", callCount)
	}
	if err := os.Remove(testfile); err != nil {
		t.Errorf("Couldn't remove test file %s", testfile)
	}
}

func TestOnPostSave(t *testing.T) {
	testfile := "testdata/test_event.txt"
	callCount := 0

	w := GetEditor().NewWindow()
	defer w.Close()

	v := w.NewFile()
	defer v.Close()

	OnPostSave.Add(func(v *View) {
		callCount++
	})
	edit := v.BeginEdit()
	v.Insert(edit, 0, "abcd")
	v.EndEdit(edit)
	if err := v.SaveAs(testfile); err != nil {
		t.Fatal("Could not save the view")
	}
	if callCount != 1 {
		t.Fatalf("%d != 1", callCount)
	}
	if err := v.Save(); err != nil {
		t.Fatalf("Could not save the view: %s", err)
	}
	if callCount != 2 {
		t.Fatalf("%d != 2", callCount)
	}
	if err := os.Remove(testfile); err != nil {
		t.Errorf("Couldn't remove test file %s", testfile)
	}
}
<commit_msg>add a test case for events.go<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	. "github.com/limetext/text"
	"os"
	"reflect"
	"testing"
)

func TestOnSelectionModified(t *testing.T) {
	var res *RegionSet

	callCount := 0

	w := GetEditor().NewWindow()
	defer w.Close()

	v := w.NewFile()
	defer func() {
		v.SetScratch(true)
		v.Close()
	}()

	OnSelectionModified.Add(func(v *View) {
		res = v.Sel()
		callCount++
	})

	edit := v.BeginEdit()
	v.Insert(edit, 0, "abcd")
	v.EndEdit(edit)
	if callCount != 1 {
		t.Fatalf("%d != 1", callCount)
	}
	if !reflect.DeepEqual(res.Regions(), []Region{{4, 4}}) {
		t.Errorf("%v", res.Regions())
	}

	edit = v.BeginEdit()
	v.Sel().Adjust(4, -1)
	v.EndEdit(edit)

	if callCount != 2 {
		t.Fatalf("%d != 2", callCount)
	}
	if !reflect.DeepEqual(res.Regions(), []Region{{3, 3}}) {
		t.Errorf("%v", res.Regions())
	}

	edit = v.BeginEdit()
	v.EndEdit(edit)

	if callCount != 2 {
		t.Fatalf("%d != 2", callCount)
	}
	if !reflect.DeepEqual(res.Regions(), []Region{{3, 3}}) {
		t.Errorf("%v", res.Regions())
	}
}

func TestOnPreSave(t *testing.T) {
	testfile := "testdata/test_event.txt"
	callCount := 0

	w := GetEditor().NewWindow()
	defer w.Close()

	v := w.NewFile()
	defer v.Close()

	OnPreSave.Add(func(v *View) {
		callCount++
	})
	edit := v.BeginEdit()
	v.Insert(edit, 0, "abcd")
	v.EndEdit(edit)
	if err := v.SaveAs(testfile); err != nil {
		t.Fatal("Could not save the view")
	}
	if callCount != 1 {
		t.Fatalf("%d != 1", callCount)
	}
	v.Buffer().SetFileName(testfile)
	if err := v.Save(); err != nil {
		t.Fatalf("Could not save the view %s", err)
	}
	if callCount != 2 {
		t.Fatalf("%d != 2", callCount)
	}
	if err := os.Remove(testfile); err != nil {
		t.Errorf("Couldn't remove test file %s", testfile)
	}
}

func TestOnPostSave(t *testing.T) {
	testfile := "testdata/test_event.txt"
	callCount := 0

	w := GetEditor().NewWindow()
	defer w.Close()

	v := w.NewFile()
	defer v.Close()

	OnPostSave.Add(func(v *View) {
		callCount++
	})
	edit := v.BeginEdit()
	v.Insert(edit, 0, "abcd")
	v.EndEdit(edit)
	if err := v.SaveAs(testfile); err != nil {
		t.Fatal("Could not save the view")
	}
	if callCount != 1 {
		t.Fatalf("%d != 1", callCount)
	}
	if err := v.Save(); err != nil {
		t.Fatalf("Could not save the view: %s", err)
	}
	if callCount != 2 {
		t.Fatalf("%d != 2", callCount)
	}
	if err := os.Remove(testfile); err != nil {
		t.Errorf("Couldn't remove test file %s", testfile)
	}
}

func TestOnNewWindow(t *testing.T) {
	callCount := 0

	OnNewWindow.Add(func(w *Window) {
		callCount++
	})

	w := GetEditor().NewWindow()
	defer w.Close()

	if callCount != 1 {
		t.Fatalf("%d != 1", callCount)
	}
}
<|endoftext|>


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

import (
	"archive/zip"
	"fmt"
	"golang.org/x/net/context"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"
)

var pathReplacer *strings.Replacer

func EscapPath(msg string) string {
	return pathReplacer.Replace(msg)
}

func removeFile(path string) error {
	fileInfo, err := os.Stat(path)
	if err != nil {
		return err
	}
	if fileInfo.IsDir() {
		return nil
	}
	err = os.Remove(path)
	if err != nil {
		return err
	}
	return nil
}

func removeExceededFiles(path string, baseFileName string,
	maxKeptCount int, rotateStage string) {
	if maxKeptCount < 0 {
		return
	}
	fileList := make([]string, 0, 2*maxKeptCount)
	var pat string
	if rotateStage == "rollover" {
		//rotated file, svc.log.20060102150405000
		pat = fmt.Sprintf(`%s\.[0-9]{1,17}$`, baseFileName)
	} else if rotateStage == "backup" {
		//backup compressed file, svc.log.20060102150405000.zip
		pat = fmt.Sprintf(`%s\.[0-9]{17}\.zip$`, baseFileName)
	} else {
		return
	}
	fileList, err := FilterFileList(path, pat)
	if err != nil {
		Logger().Error("filepath.Walk() "+EscapPath(path)+" failed", err)
		return
	}
	sort.Strings(fileList)
	if len(fileList) <= maxKeptCount {
		return
	}
	//remove exceeded files, keep file count below maxBackupCount
	for len(fileList) > maxKeptCount {
		filePath := fileList[0]
		Logger().Warn("remove "+EscapPath(filePath), nil)
		err := removeFile(filePath)
		if err != nil {
			Logger().Error("remove "+EscapPath(filePath)+" failed", err)
			break
		}
		//remove the first element of a list
		fileList = append(fileList[:0], fileList[1:]...)
	}
}

//filePath: file full path, like ${_APP_LOG_DIR}/svc.log.1
//fileBaseName: rollover file base name, like svc.log
//replaceTimestamp: whether or not to replace the num. of a rolled file
func compressFile(filePath, fileBaseName string, replaceTimestamp bool) error {
	ifp, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer ifp.Close()

	var zipFilePath string
	if replaceTimestamp {
		//svc.log.1 -> svc.log.20060102150405000.zip
		zipFileBase := fileBaseName + "." + getTimeStamp() + "." + "zip"
		zipFilePath = filepath.Dir(filePath) + "/" + zipFileBase
	} else {
		zipFilePath = filePath + ".zip"
	}
	zipFile, err := os.OpenFile(zipFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0440)
	if err != nil {
		return err
	}
	defer zipFile.Close()

	zipWriter := zip.NewWriter(zipFile)
	defer zipWriter.Close()

	ofp, err := zipWriter.Create(filepath.Base(filePath))
	if err != nil {
		return err
	}

	_, err = io.Copy(ofp, ifp)
	if err != nil {
		return err
	}

	return nil
}

func shouldRollover(fPath string, MaxFileSize int) bool {
	if MaxFileSize <= 0 {
		return false
	}

	fileInfo, err := os.Stat(fPath)
	if err != nil {
		Logger().Error("state "+EscapPath(fPath)+" failed", err)
		return false
	}

	if fileInfo.Size() > int64(MaxFileSize*1024*1024) {
		return true
	}
	return false
}

func doRollover(fPath string, MaxFileSize int, MaxBackupCount int) {
	if !shouldRollover(fPath, MaxFileSize) {
		return
	}

	timeStamp := getTimeStamp()
	//absolute path
	rotateFile := fPath + "." + timeStamp
	err := CopyFile(fPath, rotateFile)
	if err != nil {
		Logger().Error("copy "+EscapPath(fPath)+" failed", err)
	}

	//truncate the file
	f, err := os.OpenFile(fPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
	if err != nil {
		Logger().Error("truncate "+EscapPath(fPath)+" failed", err)
		return
	}
	f.Close()

	//remove exceeded rotate files
	removeExceededFiles(filepath.Dir(fPath), filepath.Base(fPath), MaxBackupCount, "rollover")
}

func doBackup(fPath string, MaxBackupCount int) {
	if MaxBackupCount <= 0 {
		return
	}
	pat := fmt.Sprintf(`%s\.[0-9]{1,17}$`, filepath.Base(fPath))
	rotateFileList, err := FilterFileList(filepath.Dir(fPath), pat)
	if err != nil {
		Logger().Error("walk"+EscapPath(fPath)+" failed", err)
		return
	}

	for _, file := range rotateFileList {
		var err error
		p := fmt.Sprintf(`%s\.[0-9]{17}$`, filepath.Base(fPath))
		if ret, _ := regexp.MatchString(p, file); ret {
			//svc.log.20060102150405000, not replace Timestamp
			err = compressFile(file, filepath.Base(fPath), false)
		} else {
			//svc.log.1, replace Timestamp
			err = compressFile(file, filepath.Base(fPath), true)
		}
		if err != nil {
			Logger().Error("compress"+EscapPath(file)+" failed", err)
			continue
		}
		err = removeFile(file)
		if err != nil {
			Logger().Error("remove"+EscapPath(file)+" failed", err)
		}
	}

	//remove exceeded backup files
	removeExceededFiles(filepath.Dir(fPath), filepath.Base(fPath), MaxBackupCount, "backup")
}

func LogRotateFile(file string, MaxFileSize int, MaxBackupCount int) {
	defer func() {
		if e := recover(); e != nil {
			Logger().Errorf(nil, "LogRotate file %s catch an exception, err: %v.", EscapPath(file), e)
		}
	}()

	doRollover(file, MaxFileSize, MaxBackupCount)
	doBackup(file, MaxBackupCount)
}

//path:			where log files need rollover
//MaxFileSize: 		MaxSize of a file before rotate. By M Bytes.
//MaxBackupCount: 	Max counts to keep of a log's backup files.
func LogRotate(path string, MaxFileSize int, MaxBackupCount int) {
	//filter .log .trace files
	defer func() {
		if e := recover(); e != nil {
			Logger().Errorf(nil, "LogRotate catch an exception, err: %v.", e)
		}
	}()

	pat := `.(\.log|\.trace|\.out)$`
	fileList, err := FilterFileList(path, pat)
	if err != nil {
		Logger().Error("filepath.Walk() "+EscapPath(path)+" failed", err)
		return
	}

	for _, file := range fileList {
		LogRotateFile(file, MaxFileSize, MaxBackupCount)
	}
}

func isSkip(f os.FileInfo) bool {
	//dir or non write permission,skip
	return f.IsDir() || (f.Mode()&0200 == 0000)
}

//path : where the file will be filtered
//pat  : regexp pattern to filter the matched file
func FilterFileList(path, pat string) ([]string, error) {
	capacity := 10
	//initialize a fileName slice, len=0, cap=10
	fileList := make([]string, 0, capacity)

	err := filepath.Walk(path,
		func(pathName string, f os.FileInfo, e error) error {
			if f == nil {
				return e
			}
			if isSkip(f) {
				return nil
			}
			if pat != "" {
				ret, _ := regexp.MatchString(pat, f.Name())
				if !ret {
					return nil
				}
			}
			fileList = append(fileList, pathName)
			return nil
		})
	return fileList, err
}

func getTimeStamp() string {
	now := time.Now().Format("2006.01.02.15.04.05.000")
	timeSlot := strings.Replace(now, ".", "", -1)
	return timeSlot
}

func CopyFile(srcFile, destFile string) error {
	file, err := os.Open(srcFile)
	if err != nil {
		return err
	}
	defer file.Close()
	dest, err := os.Create(destFile)
	if err != nil {
		return err
	}
	defer dest.Close()
	_, err = io.Copy(dest, file)
	return err
}

func RunLogDirRotate(cfg LoggerConfig) {
	Go(func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				return
			case <-time.After(cfg.LogRotatePeriod):
				LogRotate(filepath.Dir(cfg.LoggerFile), cfg.LogRotateSize, cfg.LogBackupCount)
			}
		}
	})
}

func init() {
	pathReplacer = strings.NewReplacer(
		os.ExpandEnv("${APP_ROOT}"), "APP_ROOT",
		os.ExpandEnv("${_APP_SHARE_DIR}"), "_APP_SHARE_DIR",
		os.ExpandEnv("${_APP_TMP_DIR}"), "_APP_TMP_DIR",
		os.ExpandEnv("${SSL_ROOT}"), "SSL_ROOT",
		os.ExpandEnv("${CIPHER_ROOT}"), "CIPHER_ROOT",
		os.ExpandEnv("${_APP_LOG_DIR}"), "_APP_LOG_DIR",
		os.ExpandEnv("${INSTALL_ROOT}"), "INSTALL_ROOT")
}
<commit_msg>SCB-510 Expire backup log files are not removed (#330)<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package util

import (
	"archive/zip"
	"fmt"
	"golang.org/x/net/context"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"
)

var pathReplacer *strings.Replacer

func EscapPath(msg string) string {
	return pathReplacer.Replace(msg)
}

func removeFile(path string) error {
	fileInfo, err := os.Stat(path)
	if err != nil {
		return err
	}
	if fileInfo.IsDir() {
		return nil
	}
	err = os.Remove(path)
	if err != nil {
		return err
	}
	return nil
}

func removeExceededFiles(path string, baseFileName string,
	maxKeptCount int, rotateStage string) {
	if maxKeptCount < 0 {
		return
	}
	fileList := make([]string, 0, 2*maxKeptCount)
	var pat string
	if rotateStage == "rollover" {
		//rotated file, svc.log.20060102150405000
		pat = fmt.Sprintf(`%s\.[0-9]{1,17}$`, baseFileName)
	} else if rotateStage == "backup" {
		//backup compressed file, svc.log.20060102150405000.zip
		pat = fmt.Sprintf(`%s\.[0-9]{17}\.zip$`, baseFileName)
	} else {
		return
	}
	fileList, err := FilterFileList(path, pat, 0777)
	if err != nil {
		Logger().Error("filepath.Walk() "+EscapPath(path)+" failed", err)
		return
	}
	sort.Strings(fileList)
	if len(fileList) <= maxKeptCount {
		return
	}
	//remove exceeded files, keep file count below maxBackupCount
	for len(fileList) > maxKeptCount {
		filePath := fileList[0]
		Logger().Warn("remove "+EscapPath(filePath), nil)
		err := removeFile(filePath)
		if err != nil {
			Logger().Error("remove "+EscapPath(filePath)+" failed", err)
			break
		}
		//remove the first element of a list
		fileList = append(fileList[:0], fileList[1:]...)
	}
}

//filePath: file full path, like ${_APP_LOG_DIR}/svc.log.1
//fileBaseName: rollover file base name, like svc.log
//replaceTimestamp: whether or not to replace the num. of a rolled file
func compressFile(filePath, fileBaseName string, replaceTimestamp bool) error {
	ifp, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer ifp.Close()

	var zipFilePath string
	if replaceTimestamp {
		//svc.log.1 -> svc.log.20060102150405000.zip
		zipFileBase := fileBaseName + "." + getTimeStamp() + "." + "zip"
		zipFilePath = filepath.Dir(filePath) + "/" + zipFileBase
	} else {
		zipFilePath = filePath + ".zip"
	}
	zipFile, err := os.OpenFile(zipFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0440)
	if err != nil {
		return err
	}
	defer zipFile.Close()

	zipWriter := zip.NewWriter(zipFile)
	defer zipWriter.Close()

	ofp, err := zipWriter.Create(filepath.Base(filePath))
	if err != nil {
		return err
	}

	_, err = io.Copy(ofp, ifp)
	if err != nil {
		return err
	}

	return nil
}

func shouldRollover(fPath string, MaxFileSize int) bool {
	if MaxFileSize <= 0 {
		return false
	}

	fileInfo, err := os.Stat(fPath)
	if err != nil {
		Logger().Error("state "+EscapPath(fPath)+" failed", err)
		return false
	}

	if fileInfo.Size() > int64(MaxFileSize*1024*1024) {
		return true
	}
	return false
}

func doRollover(fPath string, MaxFileSize int, MaxBackupCount int) {
	if !shouldRollover(fPath, MaxFileSize) {
		return
	}

	timeStamp := getTimeStamp()
	//absolute path
	rotateFile := fPath + "." + timeStamp
	err := CopyFile(fPath, rotateFile)
	if err != nil {
		Logger().Error("copy "+EscapPath(fPath)+" failed", err)
	}

	//truncate the file
	f, err := os.OpenFile(fPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
	if err != nil {
		Logger().Error("truncate "+EscapPath(fPath)+" failed", err)
		return
	}
	f.Close()

	//remove exceeded rotate files
	removeExceededFiles(filepath.Dir(fPath), filepath.Base(fPath), MaxBackupCount, "rollover")
}

func doBackup(fPath string, MaxBackupCount int) {
	if MaxBackupCount <= 0 {
		return
	}
	pat := fmt.Sprintf(`%s\.[0-9]{1,17}$`, filepath.Base(fPath))
	rotateFileList, err := FilterFileList(filepath.Dir(fPath), pat, 0777)
	if err != nil {
		Logger().Error("walk"+EscapPath(fPath)+" failed", err)
		return
	}

	for _, file := range rotateFileList {
		var err error
		p := fmt.Sprintf(`%s\.[0-9]{17}$`, filepath.Base(fPath))
		if ret, _ := regexp.MatchString(p, file); ret {
			//svc.log.20060102150405000, not replace Timestamp
			err = compressFile(file, filepath.Base(fPath), false)
		} else {
			//svc.log.1, replace Timestamp
			err = compressFile(file, filepath.Base(fPath), true)
		}
		if err != nil {
			Logger().Error("compress"+EscapPath(file)+" failed", err)
			continue
		}
		err = removeFile(file)
		if err != nil {
			Logger().Error("remove"+EscapPath(file)+" failed", err)
		}
	}

	//remove exceeded backup files
	removeExceededFiles(filepath.Dir(fPath), filepath.Base(fPath), MaxBackupCount, "backup")
}

func LogRotateFile(file string, MaxFileSize int, MaxBackupCount int) {
	defer func() {
		if e := recover(); e != nil {
			Logger().Errorf(nil, "LogRotate file %s catch an exception, err: %v.", EscapPath(file), e)
		}
	}()

	doRollover(file, MaxFileSize, MaxBackupCount)
	doBackup(file, MaxBackupCount)
}

//path:			where log files need rollover
//MaxFileSize: 		MaxSize of a file before rotate. By M Bytes.
//MaxBackupCount: 	Max counts to keep of a log's backup files.
func LogRotate(path string, MaxFileSize int, MaxBackupCount int) {
	//filter .log .trace files
	defer func() {
		if e := recover(); e != nil {
			Logger().Errorf(nil, "LogRotate catch an exception, err: %v.", e)
		}
	}()

	pat := `.(\.log|\.trace|\.out)$`
	fileList, err := FilterFileList(path, pat, 0200)
	if err != nil {
		Logger().Error("filepath.Walk() "+EscapPath(path)+" failed", err)
		return
	}

	for _, file := range fileList {
		LogRotateFile(file, MaxFileSize, MaxBackupCount)
	}
}

func isSkip(f os.FileInfo, permits os.FileMode) bool {
	//dir or permission deny
	return f.IsDir() || (f.Mode()&permits == 0000)
}

//path    : where the file will be filtered
//pat     : regexp pattern to filter the matched file
//permit  : check the file whether match any of the permits or not
func FilterFileList(path, pat string, permits os.FileMode) ([]string, error) {
	capacity := 10
	//initialize a fileName slice, len=0, cap=10
	fileList := make([]string, 0, capacity)

	err := filepath.Walk(path,
		func(pathName string, f os.FileInfo, e error) error {
			if f == nil {
				return e
			}
			if isSkip(f, permits) {
				return nil
			}
			if pat != "" {
				ret, _ := regexp.MatchString(pat, f.Name())
				if !ret {
					return nil
				}
			}
			fileList = append(fileList, pathName)
			return nil
		})
	return fileList, err
}

func getTimeStamp() string {
	now := time.Now().Format("2006.01.02.15.04.05.000")
	timeSlot := strings.Replace(now, ".", "", -1)
	return timeSlot
}

func CopyFile(srcFile, destFile string) error {
	file, err := os.Open(srcFile)
	if err != nil {
		return err
	}
	defer file.Close()
	dest, err := os.Create(destFile)
	if err != nil {
		return err
	}
	defer dest.Close()
	_, err = io.Copy(dest, file)
	return err
}

func RunLogDirRotate(cfg LoggerConfig) {
	Go(func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				return
			case <-time.After(cfg.LogRotatePeriod):
				LogRotate(filepath.Dir(cfg.LoggerFile), cfg.LogRotateSize, cfg.LogBackupCount)
			}
		}
	})
}

func init() {
	pathReplacer = strings.NewReplacer(
		os.ExpandEnv("${APP_ROOT}"), "APP_ROOT",
		os.ExpandEnv("${_APP_SHARE_DIR}"), "_APP_SHARE_DIR",
		os.ExpandEnv("${_APP_TMP_DIR}"), "_APP_TMP_DIR",
		os.ExpandEnv("${SSL_ROOT}"), "SSL_ROOT",
		os.ExpandEnv("${CIPHER_ROOT}"), "CIPHER_ROOT",
		os.ExpandEnv("${_APP_LOG_DIR}"), "_APP_LOG_DIR",
		os.ExpandEnv("${INSTALL_ROOT}"), "INSTALL_ROOT")
}
<|endoftext|>


<commit_before>package marathon

import (
	"github.com/gondor/depcon/marathon/bluegreen"
	"github.com/gondor/depcon/pkg/cli"
	"github.com/spf13/cobra"
	"os"
	"strings"
	"time"
)

const (
	INSTANCES_FLAG  = "instances"
	STEP_DELAY_FLAG = "stepdel"
	RESUME_FLAG     = "resume"
	LB_FLAG         = "lb"
	LB_TIMEOUT_FLAG = "lb-timeout"
	BG_DRYRUN_FLAG = "dry"
)

var bgCmd = &cobra.Command{
	Use:   "bluegreen [file(.json | .yaml)]",
	Short: "Marathon blue/green deployments",
	Long: `Blue/Green deployments handled through HAProxy or Marathon-LB

    See bluegreen's subcommands for available choices`,
	Run: deployBlueGreenCmd,
}

func init() {
	bgCmd.Flags().String(LB_FLAG, "http://localhost:9090", "HAProxy URL and Stats Port")
	bgCmd.Flags().Int(LB_TIMEOUT_FLAG, 300, "HAProxy timeout - default 300 seconds")
	bgCmd.Flags().Int(INSTANCES_FLAG, 1, "Initial intances of the app to create")
	bgCmd.Flags().Int(STEP_DELAY_FLAG, 6, "Delay (in seconds) to wait between successive deployment steps. ")
	bgCmd.Flags().Bool(RESUME_FLAG, true, "Resume from a previous deployment")
	bgCmd.Flags().BoolP(IGNORE_MISSING, "i", false, `Ignore missing ${PARAMS} that are declared in app config that could not be resolved
                        CAUTION: This can be dangerous if some params define versions or other required information.`)
	bgCmd.Flags().StringP(ENV_FILE_FLAG, "c", "", `Adds a file with a param(s) that can be used for substitution.
		   These take precidence over env vars`)
	bgCmd.Flags().StringSliceP(PARAMS_FLAG, "p", nil, `Adds a param(s) that can be used for substitution.
                  eg. -p MYVAR=value would replace ${MYVAR} with "value" in the application file.
                  These take precidence over env vars`)
	bgCmd.Flags().Bool(BG_DRYRUN_FLAG, false, "Dry run (no deployment or scaling)")

}

func deployBlueGreenCmd(cmd *cobra.Command, args []string) {

	a, err := bgc(cmd).DeployBlueGreenFromFile(args[0])
	if err != nil {
		cli.Output(nil, err)
		os.Exit(1)
	}
	cli.Output(Application{a}, err)

}

func bgc(c *cobra.Command) bluegreen.BlueGreen {

	paramsFile, _ := c.Flags().GetString(ENV_FILE_FLAG)
	params, _ := c.Flags().GetStringSlice(PARAMS_FLAG)
	ignore, _ := c.Flags().GetBool(IGNORE_MISSING)
	sd, _ := c.Flags().GetInt(STEP_DELAY_FLAG)
	lbtimeout, _ := c.Flags().GetInt(LB_TIMEOUT_FLAG)

	// Create Options
	opts := bluegreen.NewBlueGreenOptions()
	opts.Resume, _ = c.Flags().GetBool(RESUME_FLAG)
	opts.LoadBalancer, _ = c.Flags().GetString(LB_FLAG)
	opts.InitialInstances, _ = c.Flags().GetInt(INSTANCES_FLAG)
	opts.ErrorOnMissingParams = !ignore
	opts.StepDelay = time.Duration(sd) * time.Second
	opts.ProxyWaitTimeout = time.Duration(lbtimeout) * time.Second
	opts.DryRun, _ = c.Flags().GetBool(BG_DRYRUN_FLAG)

	if paramsFile != "" {
		envParams, _ := parseParamsFile(paramsFile)
		opts.EnvParams = envParams
	} else {
		opts.EnvParams = make(map[string]string)
	}

	if params != nil {
		for _, p := range params {
			if strings.Contains(p, "=") {
				v := strings.Split(p, "=")
				opts.EnvParams[v[0]] = v[1]
			}
		}
	}

	return bluegreen.NewBlueGreenClient(client(c), opts)
}
<commit_msg>WIP - Issue #21 - Blue/Green deployment support<commit_after>package marathon

import (
	"github.com/gondor/depcon/marathon/bluegreen"
	"github.com/gondor/depcon/pkg/cli"
	"github.com/spf13/cobra"
	"os"
	"strings"
	"time"
)

const (
	INSTANCES_FLAG  = "instances"
	STEP_DELAY_FLAG = "stepdel"
	RESUME_FLAG     = "resume"
	LB_FLAG         = "lb"
	LB_TIMEOUT_FLAG = "lb-timeout"
	BG_DRYRUN_FLAG  = "dry"
)

var bgCmd = &cobra.Command{
	Use:   "bluegreen [file(.json | .yaml)]",
	Short: "Marathon blue/green deployments",
	Long: `Blue/Green deployments handled through HAProxy or Marathon-LB

    See bluegreen's subcommands for available choices`,
	Run: deployBlueGreenCmd,
}

func init() {
	bgCmd.Flags().String(LB_FLAG, "http://localhost:9090", "HAProxy URL and Stats Port")
	bgCmd.Flags().Int(LB_TIMEOUT_FLAG, 300, "HAProxy timeout - default 300 seconds")
	bgCmd.Flags().Int(INSTANCES_FLAG, 1, "Initial intances of the app to create")
	bgCmd.Flags().Int(STEP_DELAY_FLAG, 6, "Delay (in seconds) to wait between successive deployment steps. ")
	bgCmd.Flags().Bool(RESUME_FLAG, true, "Resume from a previous deployment")
	bgCmd.Flags().BoolP(IGNORE_MISSING, "i", false, `Ignore missing ${PARAMS} that are declared in app config that could not be resolved
                        CAUTION: This can be dangerous if some params define versions or other required information.`)
	bgCmd.Flags().StringP(ENV_FILE_FLAG, "c", "", `Adds a file with a param(s) that can be used for substitution.
		   These take precidence over env vars`)
	bgCmd.Flags().StringSliceP(PARAMS_FLAG, "p", nil, `Adds a param(s) that can be used for substitution.
                  eg. -p MYVAR=value would replace ${MYVAR} with "value" in the application file.
                  These take precidence over env vars`)
	bgCmd.Flags().Bool(BG_DRYRUN_FLAG, false, "Dry run (no deployment or scaling)")

}

func deployBlueGreenCmd(cmd *cobra.Command, args []string) {

	a, err := bgc(cmd).DeployBlueGreenFromFile(args[0])
	if err != nil {
		cli.Output(nil, err)
		os.Exit(1)
	}
	cli.Output(Application{a}, err)

}

func bgc(c *cobra.Command) bluegreen.BlueGreen {

	paramsFile, _ := c.Flags().GetString(ENV_FILE_FLAG)
	params, _ := c.Flags().GetStringSlice(PARAMS_FLAG)
	ignore, _ := c.Flags().GetBool(IGNORE_MISSING)
	sd, _ := c.Flags().GetInt(STEP_DELAY_FLAG)
	lbtimeout, _ := c.Flags().GetInt(LB_TIMEOUT_FLAG)

	// Create Options
	opts := bluegreen.NewBlueGreenOptions()
	opts.Resume, _ = c.Flags().GetBool(RESUME_FLAG)
	opts.LoadBalancer, _ = c.Flags().GetString(LB_FLAG)
	opts.InitialInstances, _ = c.Flags().GetInt(INSTANCES_FLAG)
	opts.ErrorOnMissingParams = !ignore
	opts.StepDelay = time.Duration(sd) * time.Second
	opts.ProxyWaitTimeout = time.Duration(lbtimeout) * time.Second
	opts.DryRun, _ = c.Flags().GetBool(BG_DRYRUN_FLAG)

	if paramsFile != "" {
		envParams, _ := parseParamsFile(paramsFile)
		opts.EnvParams = envParams
	} else {
		opts.EnvParams = make(map[string]string)
	}

	if params != nil {
		for _, p := range params {
			if strings.Contains(p, "=") {
				v := strings.Split(p, "=")
				opts.EnvParams[v[0]] = v[1]
			}
		}
	}

	return bluegreen.NewBlueGreenClient(client(c), opts)
}
<|endoftext|>


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

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

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

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

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"runtime/debug"
	"strings"

	"github.com/pkg/errors"

	"github.com/spf13/viper"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/minikube/download"
)

const (
	profile      = "generate-preloaded-images-tar"
	minikubePath = "out/minikube"
)

var (
	dockerStorageDriver = "overlay2"
	podmanStorageDriver = "overlay"
	containerRuntimes   = []string{"docker", "containerd", "cri-o"}
	k8sVersions         []string
	k8sVersion          = flag.String("kubernetes-version", "", "desired Kubernetes version, for example `v1.17.2`")
	noUpload            = flag.Bool("no-upload", false, "Do not upload tarballs to GCS")
	force               = flag.Bool("force", false, "Generate the preload tarball even if it's already exists")
	limit               = flag.Int("limit", 0, "Limit the number of tarballs to generate")
	armUpload           = flag.Bool("arm-upload", false, "Upload the arm64 preload tarballs to GCS")
	armPreloadsDir      = flag.String("arm-preloads-dir", "artifacts", "Directory containing the arm64 preload tarballs")
)

type preloadCfg struct {
	k8sVer  string
	runtime string
}

func (p preloadCfg) String() string {
	return fmt.Sprintf("%q/%q", p.runtime, p.k8sVer)
}

func main() {
	flag.Parse()

	if *armUpload {
		if err := uploadArmTarballs(*armPreloadsDir); err != nil {
			log.Fatal(err)
		}
		return
	}

	// used by pkg/minikube/download.PreloadExists()
	viper.Set("preload", "true")

	if *k8sVersion != "" {
		k8sVersions = []string{*k8sVersion}
	}

	if err := deleteMinikube(); err != nil {
		fmt.Printf("error cleaning up minikube at start up: %v \n", err)
	}

	k8sVersions, err := collectK8sVers()
	if err != nil {
		exit("Unable to get recent k8s versions: %v\n", err)
	}

	var toGenerate []preloadCfg
	var i int

out:
	for _, kv := range k8sVersions {
		for _, cr := range containerRuntimes {
			if *limit > 0 && i >= *limit {
				break out
			}
			// Since none/mock are the only exceptions, it does not matter what driver we choose.
			if !download.PreloadExists(kv, cr, "docker") {
				toGenerate = append(toGenerate, preloadCfg{kv, cr})
				i++
				fmt.Printf("[%d] A preloaded tarball for k8s version %s - runtime %q does not exist.\n", i, kv, cr)
			} else if *force {
				// the tarball already exists, but '--force' is passed. we need to overwrite the file
				toGenerate = append(toGenerate, preloadCfg{kv, cr})
				i++
				fmt.Printf("[%d] A preloaded tarball for k8s version %s - runtime %q already exists. Going to overwrite it.\n", i, kv, cr)
			} else {
				fmt.Printf("A preloaded tarball for k8s version %s - runtime %q already exists, skipping generation.\n", kv, cr)
			}
		}
	}

	fmt.Printf("Going to generate preloads for %v\n", toGenerate)

	for _, cfg := range toGenerate {
		if err := makePreload(cfg); err != nil {
			exit(err.Error(), err)
		}
	}
}

func collectK8sVers() ([]string, error) {
	if k8sVersions == nil {
		recent, err := recentK8sVersions()
		if err != nil {
			return nil, err
		}
		k8sVersions = recent
	}
	return append([]string{
		constants.DefaultKubernetesVersion,
		constants.NewestKubernetesVersion,
		constants.OldestKubernetesVersion,
	}, k8sVersions...), nil
}

func makePreload(cfg preloadCfg) error {
	kv, cr := cfg.k8sVer, cfg.runtime

	fmt.Printf("A preloaded tarball for k8s version %s - runtime %q doesn't exist, generating now...\n", kv, cr)
	tf := download.TarballName(kv, cr)

	defer func() {
		if err := deleteMinikube(); err != nil {
			fmt.Printf("error cleaning up minikube before finishing up: %v\n", err)
		}
	}()

	if err := generateTarball(kv, cr, tf); err != nil {
		return errors.Wrap(err, fmt.Sprintf("generating tarball for k8s version %s with %s", kv, cr))
	}

	if *noUpload {
		fmt.Printf("skip upload of %q\n", tf)
		return nil
	}
	if err := uploadTarball(tf, kv); err != nil {
		return errors.Wrap(err, fmt.Sprintf("uploading tarball for k8s version %s with %s", kv, cr))
	}
	return nil
}

var verifyDockerStorage = func() error {
	cmd := exec.Command("docker", "exec", profile, "docker", "info", "-f", "{{.Info.Driver}}")
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	output, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("%v: %v:\n%s", cmd.Args, err, stderr.String())
	}
	driver := strings.Trim(string(output), " \n")
	if driver != dockerStorageDriver {
		return fmt.Errorf("docker storage driver %s does not match requested %s", driver, dockerStorageDriver)
	}
	return nil
}

var verifyPodmanStorage = func() error {
	cmd := exec.Command("docker", "exec", profile, "sudo", "podman", "info", "-f", "json")
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	output, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("%v: %v:\n%s", cmd.Args, err, stderr.String())
	}
	var info map[string]map[string]interface{}
	err = json.Unmarshal(output, &info)
	if err != nil {
		return err
	}
	driver := info["store"]["graphDriverName"]
	if driver != podmanStorageDriver {
		return fmt.Errorf("podman storage driver %s does not match requested %s", driver, podmanStorageDriver)
	}
	return nil
}

// exit will exit and clean up minikube
func exit(msg string, err error) {
	fmt.Printf("WithError(%s)=%v called from:\n%s", msg, err, debug.Stack())
	if err := deleteMinikube(); err != nil {
		fmt.Printf("error cleaning up minikube at start up: %v\n", err)
	}
	os.Exit(60)
}
<commit_msg>remove duplicated k8s versions from preload generation<commit_after>/*
Copyright 2020 The Kubernetes Authors All rights reserved.

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

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

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

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"runtime/debug"
	"strings"

	"github.com/pkg/errors"

	"github.com/spf13/viper"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/minikube/download"
)

const (
	profile      = "generate-preloaded-images-tar"
	minikubePath = "out/minikube"
)

var (
	dockerStorageDriver = "overlay2"
	podmanStorageDriver = "overlay"
	containerRuntimes   = []string{"docker", "containerd", "cri-o"}
	k8sVersions         []string
	k8sVersion          = flag.String("kubernetes-version", "", "desired Kubernetes version, for example `v1.17.2`")
	noUpload            = flag.Bool("no-upload", false, "Do not upload tarballs to GCS")
	force               = flag.Bool("force", false, "Generate the preload tarball even if it's already exists")
	limit               = flag.Int("limit", 0, "Limit the number of tarballs to generate")
	armUpload           = flag.Bool("arm-upload", false, "Upload the arm64 preload tarballs to GCS")
	armPreloadsDir      = flag.String("arm-preloads-dir", "artifacts", "Directory containing the arm64 preload tarballs")
)

type preloadCfg struct {
	k8sVer  string
	runtime string
}

func (p preloadCfg) String() string {
	return fmt.Sprintf("%q/%q", p.runtime, p.k8sVer)
}

func main() {
	flag.Parse()

	if *armUpload {
		if err := uploadArmTarballs(*armPreloadsDir); err != nil {
			log.Fatal(err)
		}
		return
	}

	// used by pkg/minikube/download.PreloadExists()
	viper.Set("preload", "true")

	if *k8sVersion != "" {
		k8sVersions = []string{*k8sVersion}
	}

	if err := deleteMinikube(); err != nil {
		fmt.Printf("error cleaning up minikube at start up: %v \n", err)
	}

	k8sVersions, err := collectK8sVers()
	if err != nil {
		exit("Unable to get recent k8s versions: %v\n", err)
	}

	var toGenerate []preloadCfg
	var i int

out:
	for _, kv := range k8sVersions {
		for _, cr := range containerRuntimes {
			if *limit > 0 && i >= *limit {
				break out
			}
			// Since none/mock are the only exceptions, it does not matter what driver we choose.
			if !download.PreloadExists(kv, cr, "docker") {
				toGenerate = append(toGenerate, preloadCfg{kv, cr})
				i++
				fmt.Printf("[%d] A preloaded tarball for k8s version %s - runtime %q does not exist.\n", i, kv, cr)
			} else if *force {
				// the tarball already exists, but '--force' is passed. we need to overwrite the file
				toGenerate = append(toGenerate, preloadCfg{kv, cr})
				i++
				fmt.Printf("[%d] A preloaded tarball for k8s version %s - runtime %q already exists. Going to overwrite it.\n", i, kv, cr)
			} else {
				fmt.Printf("A preloaded tarball for k8s version %s - runtime %q already exists, skipping generation.\n", kv, cr)
			}
		}
	}

	fmt.Printf("Going to generate preloads for %v\n", toGenerate)

	for _, cfg := range toGenerate {
		if err := makePreload(cfg); err != nil {
			exit(err.Error(), err)
		}
	}
}

func collectK8sVers() ([]string, error) {
	if k8sVersions == nil {
		recent, err := recentK8sVersions()
		if err != nil {
			return nil, err
		}
		k8sVersions = recent
	}
	versions := append([]string{
		constants.DefaultKubernetesVersion,
		constants.NewestKubernetesVersion,
		constants.OldestKubernetesVersion,
	}, k8sVersions...)
	return removeDuplicates(versions), nil
}

func removeDuplicates(versions []string) []string {
	prevVersions := make(map[string]bool)
	for i := 0; i < len(versions); i++ {
		v := versions[i]
		if ok := prevVersions[v]; !ok {
			prevVersions[v] = true
			continue
		}
		versions = append(versions[:i], versions[i+1:]...)
		i--
	}
	return versions
}

func makePreload(cfg preloadCfg) error {
	kv, cr := cfg.k8sVer, cfg.runtime

	fmt.Printf("A preloaded tarball for k8s version %s - runtime %q doesn't exist, generating now...\n", kv, cr)
	tf := download.TarballName(kv, cr)

	defer func() {
		if err := deleteMinikube(); err != nil {
			fmt.Printf("error cleaning up minikube before finishing up: %v\n", err)
		}
	}()

	if err := generateTarball(kv, cr, tf); err != nil {
		return errors.Wrap(err, fmt.Sprintf("generating tarball for k8s version %s with %s", kv, cr))
	}

	if *noUpload {
		fmt.Printf("skip upload of %q\n", tf)
		return nil
	}
	if err := uploadTarball(tf, kv); err != nil {
		return errors.Wrap(err, fmt.Sprintf("uploading tarball for k8s version %s with %s", kv, cr))
	}
	return nil
}

var verifyDockerStorage = func() error {
	cmd := exec.Command("docker", "exec", profile, "docker", "info", "-f", "{{.Info.Driver}}")
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	output, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("%v: %v:\n%s", cmd.Args, err, stderr.String())
	}
	driver := strings.Trim(string(output), " \n")
	if driver != dockerStorageDriver {
		return fmt.Errorf("docker storage driver %s does not match requested %s", driver, dockerStorageDriver)
	}
	return nil
}

var verifyPodmanStorage = func() error {
	cmd := exec.Command("docker", "exec", profile, "sudo", "podman", "info", "-f", "json")
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	output, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("%v: %v:\n%s", cmd.Args, err, stderr.String())
	}
	var info map[string]map[string]interface{}
	err = json.Unmarshal(output, &info)
	if err != nil {
		return err
	}
	driver := info["store"]["graphDriverName"]
	if driver != podmanStorageDriver {
		return fmt.Errorf("podman storage driver %s does not match requested %s", driver, podmanStorageDriver)
	}
	return nil
}

// exit will exit and clean up minikube
func exit(msg string, err error) {
	fmt.Printf("WithError(%s)=%v called from:\n%s", msg, err, debug.Stack())
	if err := deleteMinikube(); err != nil {
		fmt.Printf("error cleaning up minikube at start up: %v\n", err)
	}
	os.Exit(60)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

var (
	root = "../../../"
)

func main() {
	var p Process
	p.ProcessDir(filepath.Join(root, "src/liteapp"))
	p.ProcessDir(filepath.Join(root, "src/plugins"))
	p.Export(filepath.Join(root, "deploy/liteapp/qrc/default"))
}

type Process struct {
	rccs []RCC
}

func (p *Process) ProcessDir(dir string) {
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if filepath.Ext(path) == ".qrc" {
			p.ProcessQrc(path)
		}
		return nil
	})
}

type QResource struct {
	Prefix string   `xml:"prefix,attr"`
	Files  []string `xml:"file"`
}

type RCC struct {
	Dir      string
	DirName  string
	FileName string
	XMLName  xml.Name  `xml:"RCC"`
	Resource QResource `xml:"qresource"`
}

func (rcc *RCC) IsEmtpy() bool {
	return len(rcc.Resource.Files) == 0
}

func (rcc *RCC) ImagesFiles() (images []string) {
	for _, file := range rcc.Resource.Files {
		if strings.HasPrefix(file, "images/") {
			image := filepath.Join(rcc.Dir, file)
			_, err := os.Lstat(image)
			if err != nil {
				log.Println("warning, not find image", image)
			}
			images = append(images, image)
		}
	}
	return
}

func (p *Process) ProcessQrc(path string) error {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	var rcc RCC
	err = xml.Unmarshal(data, &rcc)
	if err != nil {
		log.Println(err)
		return err
	}
	rcc.Dir, rcc.FileName = filepath.Split(path)
	_, rcc.DirName = filepath.Split(filepath.Clean(rcc.Dir))
	p.rccs = append(p.rccs, rcc)
	return nil
}

func (p *Process) Export(outdir string) error {
	for _, rcc := range p.rccs {
		p.ExportQrc(outdir, rcc)
	}
	return nil
}

func (p *Process) ExportQrc(outdir string, rcc RCC) error {
	images := rcc.ImagesFiles()
	if len(images) == 0 {
		log.Println("skip empty rcc", rcc.FileName)
		return nil
	}
	outpath := filepath.Join(outdir, rcc.DirName, "images")
	os.MkdirAll(outpath, 0777)
	for _, file := range images {
		err := CopyFileTo(file, outpath)
		log.Println(file, err)
	}
	return nil
}

func CopyFileTo(source string, outdir string) (err error) {
	_, name := filepath.Split(source)
	return CopyFile(source, filepath.Join(outdir, name))
}

func CopyFile(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}
	defer sourcefile.Close()
	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer destfile.Close()
	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}
	}
	return
}
<commit_msg>tools/mkimages add gray<commit_after>package main

import (
	"encoding/xml"
	"image"
	"image/draw"
	"image/png"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

var (
	root = "../../../"
)

func main() {
	var p Process
	p.ProcessDir(filepath.Join(root, "src/liteapp"))
	p.ProcessDir(filepath.Join(root, "src/plugins"))
	p.Export(filepath.Join(root, "deploy/liteapp/qrc/default"), CopyFile)
	p.Export(filepath.Join(root, "deploy/liteapp/qrc/gray"), GrayImage)
}

type Process struct {
	rccs []RCC
}

func (p *Process) ProcessDir(dir string) {
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if filepath.Ext(path) == ".qrc" {
			p.ProcessQrc(path)
		}
		return nil
	})
}

type QResource struct {
	Prefix string   `xml:"prefix,attr"`
	Files  []string `xml:"file"`
}

type RCC struct {
	Dir      string
	DirName  string
	FileName string
	XMLName  xml.Name  `xml:"RCC"`
	Resource QResource `xml:"qresource"`
}

func (rcc *RCC) IsEmtpy() bool {
	return len(rcc.Resource.Files) == 0
}

func (rcc *RCC) ImagesFiles() (images []string) {
	for _, file := range rcc.Resource.Files {
		if strings.HasPrefix(file, "images/") {
			image := filepath.Join(rcc.Dir, file)
			_, err := os.Lstat(image)
			if err != nil {
				log.Println("warning, not find image", image)
			}
			images = append(images, image)
		}
	}
	return
}

func (p *Process) ProcessQrc(path string) error {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	var rcc RCC
	err = xml.Unmarshal(data, &rcc)
	if err != nil {
		log.Println(err)
		return err
	}
	rcc.Dir, rcc.FileName = filepath.Split(path)
	_, rcc.DirName = filepath.Split(filepath.Clean(rcc.Dir))
	p.rccs = append(p.rccs, rcc)
	return nil
}

func (p *Process) Export(outdir string, copyFn CopyFunc) error {
	for _, rcc := range p.rccs {
		p.ExportQrc(outdir, rcc, copyFn)
	}
	return nil
}

func (p *Process) ExportQrc(outdir string, rcc RCC, copyFn CopyFunc) error {
	images := rcc.ImagesFiles()
	if len(images) == 0 {
		log.Println("skip empty rcc", rcc.FileName)
		return nil
	}
	outpath := filepath.Join(outdir, rcc.DirName, "images")
	os.MkdirAll(outpath, 0777)
	for _, file := range images {
		_, name := filepath.Split(file)
		dest := filepath.Join(outpath, name)
		err := copyFn(file, dest)
		log.Println(file, err)
	}
	return nil
}

type CopyFunc func(string, string) error

func GrayImage(source string, dest string) error {
	f, err := os.Open(source)
	if err != nil {
		return err
	}
	srcImage, err := png.Decode(f)
	if err != nil {
		return err
	}
	dstImage := image.NewRGBA(srcImage.Bounds())
	draw.Draw(dstImage, dstImage.Bounds(), srcImage, srcImage.Bounds().Min, draw.Src)
	//	dstImage.
	b := dstImage.Bounds()
	for y := b.Min.Y; y < b.Max.Y; y++ {
		for x := b.Min.X; x < b.Max.X; x++ {
			c := dstImage.RGBAAt(x, y)
			var avg uint8 = uint8((int(c.R) + int(c.G) + int(c.B)) / 3)
			//c.R = c.G = c.B = avg
			c.R = avg
			c.G = avg
			c.B = avg
			dstImage.SetRGBA(x, y, c)
		}
	}

	w, err := os.Create(dest)
	if err != nil {
		return err
	}
	err = png.Encode(w, dstImage)
	if err != nil {
		return err
	}
	return nil
}

func CopyFile(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}
	defer sourcefile.Close()
	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer destfile.Close()
	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}
	}
	return
}
<|endoftext|>


<commit_before>// Tideland Go Cells - Behaviors - Unit Tests - Event Rate
//
// Copyright (C) 2010-2017 Frank Mueller / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.

package behaviors_test

//--------------------
// IMPORTS
//--------------------

import (
	"math/rand"
	"testing"
	"time"

	"github.com/tideland/golib/audit"

	"github.com/tideland/gocells/behaviors"
	"github.com/tideland/gocells/cells"
)

//--------------------
// TESTS
//--------------------

// TestRateBehavior tests the event rate behavior.
func TestRateBehavior(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	env := cells.NewEnvironment("rate-behavior")
	defer env.Stop()

	matches := func(event cells.Event) bool {
		return event.Topic() == "now"
	}
	topics := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "now"}

	env.StartCell("rater", behaviors.NewRateBehavior(matches, 100))
	env.StartCell("collector", behaviors.NewCollectorBehavior(10000))
	env.Subscribe("rater", "collector")

	for i := 0; i < 10000; i++ {
		topic := topics[rand.Intn(len(topics))]
		env.EmitNew("rater", topic, nil)
		time.Sleep(time.Duration(rand.Intn(3)) * time.Millisecond)
	}

	collected, err := env.Request("collector", cells.CollectedTopic, nil, cells.DefaultTimeout)
	assert.Nil(err)
	events, ok := collected.(cells.EventDatas)
	assert.True(ok)
	assert.True(events.Len() <= 10000)
	err = events.Do(func(index int, data *cells.EventData) error {
		assert.Equal(data.Topic, "event-rate!")
		hi, ok := data.Payload.GetDuration(behaviors.EventRateHighPayload)
		assert.True(ok)
		avg, ok := data.Payload.GetDuration(behaviors.EventRateAveragePayload)
		assert.True(ok)
		lo, ok := data.Payload.GetDuration(behaviors.EventRateLowPayload)
		assert.True(ok)
		assert.True(lo <= avg)
		assert.True(avg <= hi)
		return nil
	})
	assert.Nil(err)
}

// EOF
<commit_msg>Fixed rate behavior test<commit_after>// Tideland Go Cells - Behaviors - Unit Tests - Event Rate
//
// Copyright (C) 2010-2017 Frank Mueller / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.

package behaviors_test

//--------------------
// IMPORTS
//--------------------

import (
	"context"
	"math/rand"
	"testing"
	"time"

	"github.com/tideland/golib/audit"

	"github.com/tideland/gocells/behaviors"
	"github.com/tideland/gocells/cells"
)

//--------------------
// TESTS
//--------------------

// TestRateBehavior tests the event rate behavior.
func TestRateBehavior(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	ctx := context.Background()
	env := cells.NewEnvironment("rate-behavior")
	defer env.Stop()

	matches := func(event cells.Event) bool {
		return event.Topic() == "now"
	}
	topics := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "now"}
	sink := cells.NewEventSink(10000)

	env.StartCell("rater", behaviors.NewRateBehavior(matches, 100))
	env.StartCell("collector", behaviors.NewCollectorBehavior(sink))
	env.Subscribe("rater", "collector")

	for i := 0; i < 10000; i++ {
		topic := topics[rand.Intn(len(topics))]
		env.EmitNew(ctx, "rater", topic, nil)
		time.Sleep(time.Duration(rand.Intn(3)) * time.Millisecond)
	}

	accessor, err := behaviors.RequestCollectedAccessor(ctx, env, "collector", cells.DefaultTimeout)
	assert.Nil(err)
	assert.True(accessor.Len() <= 10000)
	err = accessor.Do(func(index int, event cells.Event) error {
		assert.Equal(event.Topic(), "event-rate!")
		hi := event.Payload().GetDuration(behaviors.EventRateHighPayload, -1)
		avg := event.Payload().GetDuration(behaviors.EventRateAveragePayload, -1)
		lo := event.Payload().GetDuration(behaviors.EventRateLowPayload, -1)
		assert.True(lo <= avg)
		assert.True(avg <= hi)
		return nil
	})
	assert.Nil(err)
}

// EOF
<|endoftext|>


<commit_before>package phpcodegen

import "gutscript/ast"
import "strconv"
import "strings"
import "fmt"

type Visitor struct {
	indent int
}

func (self *Visitor) IndentSpace() string {
	if self.indent == 0 {
		return ""
	}
	return strings.Repeat("    ", self.indent)
}

func (self *Visitor) Visit(n ast.Node) string {
	// fmt.Printf("visit %#v\n", n)
	if stmts, ok := n.(*ast.StatementList); ok {
		var output string
		for _, stmt := range *stmts {
			output += self.IndentSpace() + self.Visit(stmt)
		}
		return output
	}
	if variable, ok := n.(ast.Variable); ok {
		return "$" + variable.Identifier
	}
	if number, ok := n.(ast.Number); ok {
		return strconv.FormatInt(number.Val, 10)
	}
	if floating, ok := n.(ast.FloatingNumber); ok {
		return strconv.FormatFloat(floating.Val, 'e', -1, 64)
	}

	if expr, ok := n.(ast.UnaryExpr); ok {
		if expr.Op != 0 {
			return fmt.Sprintf("%c%s", expr.Op, self.Visit(expr.Val))
		} else {
			return self.Visit(expr.Val)
		}
	}
	if expr, ok := n.(ast.Expr); ok {
		if expr.Parenthesis {
			return fmt.Sprintf("(%s %c %s)", self.Visit(expr.Left), expr.Op, self.Visit(expr.Right))
		}
		return fmt.Sprintf("%s %c %s", self.Visit(expr.Left), expr.Op, self.Visit(expr.Right))
	}
	if stmt, ok := n.(ast.AssignStatement); ok {
		return self.Visit(stmt.Variable) + " = " + self.Visit(stmt.Expr) + ";\n"
	}
	if stmt, ok := n.(*ast.IfStatement); ok {
		var out string = ""
		out += self.IndentSpace() + "if ( " + self.Visit(stmt.Expr) + " ) {\n"
		self.indent++
		out += self.Visit(stmt.Body)
		self.indent--
		out += self.IndentSpace() + "}"

		if len(stmt.ElseIfList) > 0 {
			for _, elseifStmt := range stmt.ElseIfList {
				out += self.Visit(elseifStmt)
			}
		}
		if stmt.ElseBody != nil {
			out += " else {\n"
			self.indent++
			out += self.Visit(stmt.ElseBody)
			self.indent--
			out += "}"
		}
		out += "\n"
		return out
	}
	if stmt, ok := n.(ast.ElseIfStatement); ok {
		var out string = ""
		out += self.IndentSpace() + " elseif ( " + self.Visit(stmt.Expr) + " ) {\n"
		self.indent++
		out += self.Visit(stmt.Body)
		self.indent--
		out += self.IndentSpace() + "}"
		return out
	}
	if stmt, ok := n.(ast.ReturnStatement); ok {
		return "return " + self.Visit(stmt.Expr) + ";\n"
	}
	if stmt, ok := n.(ast.ExprStatement); ok {
		return self.Visit(stmt.Expr) + ";\n"
	}
	if fnc, ok := n.(ast.FunctionCall); ok {
		var out string
		out = fnc.Name + "("
		fields := []string{}
		for _, param := range fnc.Params {
			fields = append(fields, self.Visit(param))
		}
		out += strings.Join(fields, ", ")
		out += ")"
		return out
	}
	if fn, ok := n.(ast.Function); ok {
		var out string = ""
		out += self.IndentSpace() + "function " + fn.Name + "("
		if len(fn.Params) > 0 {
			fields := []string{}
			for _, param := range fn.Params {
				field := "$" + param.Name
				if param.Type != "" {
					field = param.Type + " " + field
				}
				fields = append(fields, field)
			}
			out += strings.Join(fields, ", ")
		}
		out += ") {\n"
		self.indent++
		out += self.Visit(fn.Body)
		self.indent--
		out += self.IndentSpace() + "}\n"
		return out
	}
	return ""
}
<commit_msg>support function param default value.<commit_after>package phpcodegen

import "gutscript/ast"
import "strconv"
import "strings"
import "fmt"

type Visitor struct {
	indent int
}

func (self *Visitor) IndentSpace() string {
	if self.indent == 0 {
		return ""
	}
	return strings.Repeat("    ", self.indent)
}

func (self *Visitor) Visit(n ast.Node) string {
	// fmt.Printf("visit %#v\n", n)
	if stmts, ok := n.(*ast.StatementList); ok {
		var output string
		for _, stmt := range *stmts {
			output += self.IndentSpace() + self.Visit(stmt)
		}
		return output
	}
	if variable, ok := n.(ast.Variable); ok {
		return "$" + variable.Identifier
	}
	if number, ok := n.(ast.Number); ok {
		return strconv.FormatInt(number.Val, 10)
	}
	if floating, ok := n.(ast.FloatingNumber); ok {
		return strconv.FormatFloat(floating.Val, 'e', -1, 64)
	}

	if expr, ok := n.(ast.UnaryExpr); ok {
		if expr.Op != 0 {
			return fmt.Sprintf("%c%s", expr.Op, self.Visit(expr.Val))
		} else {
			return self.Visit(expr.Val)
		}
	}
	if expr, ok := n.(ast.Expr); ok {
		if expr.Parenthesis {
			return fmt.Sprintf("(%s %c %s)", self.Visit(expr.Left), expr.Op, self.Visit(expr.Right))
		}
		return fmt.Sprintf("%s %c %s", self.Visit(expr.Left), expr.Op, self.Visit(expr.Right))
	}
	if stmt, ok := n.(ast.AssignStatement); ok {
		return self.Visit(stmt.Variable) + " = " + self.Visit(stmt.Expr) + ";\n"
	}
	if stmt, ok := n.(*ast.IfStatement); ok {
		var out string = ""
		out += self.IndentSpace() + "if ( " + self.Visit(stmt.Expr) + " ) {\n"
		self.indent++
		out += self.Visit(stmt.Body)
		self.indent--
		out += self.IndentSpace() + "}"

		if len(stmt.ElseIfList) > 0 {
			for _, elseifStmt := range stmt.ElseIfList {
				out += self.Visit(elseifStmt)
			}
		}
		if stmt.ElseBody != nil {
			out += " else {\n"
			self.indent++
			out += self.Visit(stmt.ElseBody)
			self.indent--
			out += "}"
		}
		out += "\n"
		return out
	}
	if stmt, ok := n.(ast.ElseIfStatement); ok {
		var out string = ""
		out += self.IndentSpace() + " elseif ( " + self.Visit(stmt.Expr) + " ) {\n"
		self.indent++
		out += self.Visit(stmt.Body)
		self.indent--
		out += self.IndentSpace() + "}"
		return out
	}
	if stmt, ok := n.(ast.ReturnStatement); ok {
		return "return " + self.Visit(stmt.Expr) + ";\n"
	}
	if stmt, ok := n.(ast.ExprStatement); ok {
		return self.Visit(stmt.Expr) + ";\n"
	}
	if fnc, ok := n.(ast.FunctionCall); ok {
		var out string
		out = fnc.Name + "("
		fields := []string{}
		for _, param := range fnc.Params {
			fields = append(fields, self.Visit(param))
		}
		out += strings.Join(fields, ", ")
		out += ")"
		return out
	}
	if fn, ok := n.(ast.Function); ok {
		var out string = ""
		out += self.IndentSpace() + "function " + fn.Name + "("
		if len(fn.Params) > 0 {
			fields := []string{}
			for _, param := range fn.Params {
				field := "$" + param.Name
				if param.Type != "" {
					field = param.Type + " " + field
				}
				if param.Default != nil {
					field += " = " + self.Visit(param.Default)
				}
				fields = append(fields, field)
			}
			out += strings.Join(fields, ", ")
		}
		out += ") {\n"
		self.indent++
		out += self.Visit(fn.Body)
		self.indent--
		out += self.IndentSpace() + "}\n"
		return out
	}
	return ""
}
<|endoftext|>


<commit_before>// Package virtualmachine provides a client for Virtual Machines.
package virtualmachine

import (
	"encoding/xml"
	"fmt"

	"github.com/Azure/azure-sdk-for-go/management"
)

const (
	azureDeploymentListURL   = "services/hostedservices/%s/deployments"
	azureDeploymentURL       = "services/hostedservices/%s/deployments/%s"
	azureListDeploymentsInSlotURL = "services/hostedservices/%s/deploymentslots/Production"
	deleteAzureDeploymentURL = "services/hostedservices/%s/deployments/%s?comp=media"
	azureAddRoleURL          = "services/hostedservices/%s/deployments/%s/roles"
	azureRoleURLGetUpdate    = "services/hostedservices/%s/deployments/%s/roles/%s"
	azureRoleURLDelete       = "services/hostedservices/%s/deployments/%s/roles/%s?comp=media"
	azureOperationsURL       = "services/hostedservices/%s/deployments/%s/roleinstances/%s/Operations"
	azureRoleSizeListURL     = "rolesizes"

	errParamNotSpecified = "Parameter %s is not specified."
)

//NewClient is used to instantiate a new VirtualMachineClient from an Azure client
func NewClient(client management.Client) VirtualMachineClient {
	return VirtualMachineClient{client: client}
}

// CreateDeploymentOptions can be used to create a customized deployement request
type CreateDeploymentOptions struct {
	DNSServers         []DNSServer
	LoadBalancers      []LoadBalancer
	ReservedIPName     string
	VirtualNetworkName string
}

// CreateDeployment creates a deployment and then creates a virtual machine
// in the deployment based on the specified configuration.
//
// https://msdn.microsoft.com/en-us/library/azure/jj157194.aspx
func (vm VirtualMachineClient) CreateDeployment(
	role Role,
	cloudServiceName string,
	options CreateDeploymentOptions) (management.OperationID, error) {

	req := DeploymentRequest{
		Name:               role.RoleName,
		DeploymentSlot:     "Production",
		Label:              role.RoleName,
		RoleList:           []Role{role},
		DNSServers:         options.DNSServers,
		LoadBalancers:      options.LoadBalancers,
		ReservedIPName:     options.ReservedIPName,
		VirtualNetworkName: options.VirtualNetworkName,
	}

	data, err := xml.Marshal(req)
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureDeploymentListURL, cloudServiceName)
	return vm.client.SendAzurePostRequest(requestURL, data)
}

// GetDeploymentName queries an existing Azure cloud service for the name of the Deployment,
// if any, in its 'Production' slot (the only slot possible). If none exists, it returns empty
// string but no error
//
//https://msdn.microsoft.com/en-us/library/azure/ee460804.aspx
func (vm VirtualMachineClient) GetDeploymentName(cloudServiceName string) (string, error) {
	var deployment DeploymentResponse
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	requestURL := fmt.Sprintf(azureListDeploymentsInSlotURL, cloudServiceName)
	response, err := vm.client.SendAzureGetRequest(requestURL)
	if err != nil {
		if management.IsResourceNotFoundError(err) {
			return "", nil
		} else {
			return "", err
		}
        }
	err = xml.Unmarshal(response, &deployment)
	if err != nil {
		return "", err
	}

	return deployment.Name, nil
}

func (vm VirtualMachineClient) GetDeployment(cloudServiceName, deploymentName string) (DeploymentResponse, error) {
	var deployment DeploymentResponse
	if cloudServiceName == "" {
		return deployment, fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return deployment, fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	requestURL := fmt.Sprintf(azureDeploymentURL, cloudServiceName, deploymentName)
	response, azureErr := vm.client.SendAzureGetRequest(requestURL)
	if azureErr != nil {
		return deployment, azureErr
	}

	err := xml.Unmarshal(response, &deployment)
	return deployment, err
}

func (vm VirtualMachineClient) DeleteDeployment(cloudServiceName, deploymentName string) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}

	requestURL := fmt.Sprintf(deleteAzureDeploymentURL, cloudServiceName, deploymentName)
	return vm.client.SendAzureDeleteRequest(requestURL)
}

func (vm VirtualMachineClient) GetRole(cloudServiceName, deploymentName, roleName string) (*Role, error) {
	if cloudServiceName == "" {
		return nil, fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return nil, fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return nil, fmt.Errorf(errParamNotSpecified, "roleName")
	}

	role := new(Role)

	requestURL := fmt.Sprintf(azureRoleURLGetUpdate, cloudServiceName, deploymentName, roleName)
	response, azureErr := vm.client.SendAzureGetRequest(requestURL)
	if azureErr != nil {
		return nil, azureErr
	}

	err := xml.Unmarshal(response, role)
	if err != nil {
		return nil, err
	}

	return role, nil
}

// AddRole adds a Virtual Machine to a deployment of Virtual Machines, where role name = VM name
// See https://msdn.microsoft.com/en-us/library/azure/jj157186.aspx
func (vm VirtualMachineClient) AddRole(cloudServiceName string, deploymentName string, role Role) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}

	data, err := xml.Marshal(PersistentVMRole{Role: role})
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureAddRoleURL, cloudServiceName, deploymentName)
	return vm.client.SendAzurePostRequest(requestURL, data)
}

// UpdateRole updates the configuration of the specified virtual machine
// See https://msdn.microsoft.com/en-us/library/azure/jj157187.aspx
func (vm VirtualMachineClient) UpdateRole(cloudServiceName, deploymentName, roleName string, role Role) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	data, err := xml.Marshal(PersistentVMRole{Role: role})
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureRoleURLGetUpdate, cloudServiceName, deploymentName, roleName)
	return vm.client.SendAzurePutRequest(requestURL, "text/xml", data)
}

func (vm VirtualMachineClient) StartRole(cloudServiceName, deploymentName, roleName string) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	startRoleOperationBytes, err := xml.Marshal(StartRoleOperation{
		OperationType: "StartRoleOperation",
	})
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName)
	return vm.client.SendAzurePostRequest(requestURL, startRoleOperationBytes)
}

func (vm VirtualMachineClient) ShutdownRole(cloudServiceName, deploymentName, roleName string) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	shutdownRoleOperationBytes, err := xml.Marshal(ShutdownRoleOperation{
		OperationType: "ShutdownRoleOperation",
	})
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName)
	return vm.client.SendAzurePostRequest(requestURL, shutdownRoleOperationBytes)
}

func (vm VirtualMachineClient) RestartRole(cloudServiceName, deploymentName, roleName string) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	restartRoleOperationBytes, err := xml.Marshal(RestartRoleOperation{
		OperationType: "RestartRoleOperation",
	})
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName)
	return vm.client.SendAzurePostRequest(requestURL, restartRoleOperationBytes)
}

func (vm VirtualMachineClient) DeleteRole(cloudServiceName, deploymentName, roleName string) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	requestURL := fmt.Sprintf(azureRoleURLDelete, cloudServiceName, deploymentName, roleName)
	return vm.client.SendAzureDeleteRequest(requestURL)
}

func (vm VirtualMachineClient) GetRoleSizeList() (RoleSizeList, error) {
	roleSizeList := RoleSizeList{}

	response, err := vm.client.SendAzureGetRequest(azureRoleSizeListURL)
	if err != nil {
		return roleSizeList, err
	}

	err = xml.Unmarshal(response, &roleSizeList)
	return roleSizeList, err
}

// CaptureRole captures a VM role. If reprovisioningConfigurationSet is non-nil,
// the VM role is redeployed after capturing the image, otherwise, the original
// VM role is deleted.
//
// NOTE: an image resulting from this operation shows up in
// osimage.GetImageList() as images with Category "User".
func (vm VirtualMachineClient) CaptureRole(cloudServiceName, deploymentName, roleName, imageName, imageLabel string,
	reprovisioningConfigurationSet *ConfigurationSet) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	if reprovisioningConfigurationSet != nil &&
		!(reprovisioningConfigurationSet.ConfigurationSetType == ConfigurationSetTypeLinuxProvisioning ||
			reprovisioningConfigurationSet.ConfigurationSetType == ConfigurationSetTypeWindowsProvisioning) {
		return "", fmt.Errorf("ConfigurationSet type can only be WindowsProvisioningConfiguration or LinuxProvisioningConfiguration")
	}

	operation := CaptureRoleOperation{
		OperationType:             "CaptureRoleOperation",
		PostCaptureAction:         PostCaptureActionReprovision,
		ProvisioningConfiguration: reprovisioningConfigurationSet,
		TargetImageLabel:          imageLabel,
		TargetImageName:           imageName,
	}
	if reprovisioningConfigurationSet == nil {
		operation.PostCaptureAction = PostCaptureActionDelete
	}

	data, err := xml.Marshal(operation)
	if err != nil {
		return "", err
	}

	return vm.client.SendAzurePostRequest(fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName), data)
}
<commit_msg>Adding boolean parameter to DeleteRole based on which the disk will be deleted or retained - as per review comment on this pull request - https://github.com/Azure/azure-sdk-for-go/pull/237<commit_after>// Package virtualmachine provides a client for Virtual Machines.
package virtualmachine

import (
	"encoding/xml"
	"fmt"

	"github.com/Azure/azure-sdk-for-go/management"
)

const (
	azureDeploymentListURL   = "services/hostedservices/%s/deployments"
	azureDeploymentURL       = "services/hostedservices/%s/deployments/%s"
	azureListDeploymentsInSlotURL = "services/hostedservices/%s/deploymentslots/Production"
	deleteAzureDeploymentURL = "services/hostedservices/%s/deployments/%s?comp=media"
	azureAddRoleURL          = "services/hostedservices/%s/deployments/%s/roles"
	azureRoleURL             = "services/hostedservices/%s/deployments/%s/roles/%s"
	azureOperationsURL       = "services/hostedservices/%s/deployments/%s/roleinstances/%s/Operations"
	azureRoleSizeListURL     = "rolesizes"

	errParamNotSpecified = "Parameter %s is not specified."
)

//NewClient is used to instantiate a new VirtualMachineClient from an Azure client
func NewClient(client management.Client) VirtualMachineClient {
	return VirtualMachineClient{client: client}
}

// CreateDeploymentOptions can be used to create a customized deployement request
type CreateDeploymentOptions struct {
	DNSServers         []DNSServer
	LoadBalancers      []LoadBalancer
	ReservedIPName     string
	VirtualNetworkName string
}

// CreateDeployment creates a deployment and then creates a virtual machine
// in the deployment based on the specified configuration.
//
// https://msdn.microsoft.com/en-us/library/azure/jj157194.aspx
func (vm VirtualMachineClient) CreateDeployment(
	role Role,
	cloudServiceName string,
	options CreateDeploymentOptions) (management.OperationID, error) {

	req := DeploymentRequest{
		Name:               role.RoleName,
		DeploymentSlot:     "Production",
		Label:              role.RoleName,
		RoleList:           []Role{role},
		DNSServers:         options.DNSServers,
		LoadBalancers:      options.LoadBalancers,
		ReservedIPName:     options.ReservedIPName,
		VirtualNetworkName: options.VirtualNetworkName,
	}

	data, err := xml.Marshal(req)
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureDeploymentListURL, cloudServiceName)
	return vm.client.SendAzurePostRequest(requestURL, data)
}

// GetDeploymentName queries an existing Azure cloud service for the name of the Deployment,
// if any, in its 'Production' slot (the only slot possible). If none exists, it returns empty
// string but no error
//
//https://msdn.microsoft.com/en-us/library/azure/ee460804.aspx
func (vm VirtualMachineClient) GetDeploymentName(cloudServiceName string) (string, error) {
	var deployment DeploymentResponse
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	requestURL := fmt.Sprintf(azureListDeploymentsInSlotURL, cloudServiceName)
	response, err := vm.client.SendAzureGetRequest(requestURL)
	if err != nil {
		if management.IsResourceNotFoundError(err) {
			return "", nil
		} else {
			return "", err
		}
        }
	err = xml.Unmarshal(response, &deployment)
	if err != nil {
		return "", err
	}

	return deployment.Name, nil
}

func (vm VirtualMachineClient) GetDeployment(cloudServiceName, deploymentName string) (DeploymentResponse, error) {
	var deployment DeploymentResponse
	if cloudServiceName == "" {
		return deployment, fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return deployment, fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	requestURL := fmt.Sprintf(azureDeploymentURL, cloudServiceName, deploymentName)
	response, azureErr := vm.client.SendAzureGetRequest(requestURL)
	if azureErr != nil {
		return deployment, azureErr
	}

	err := xml.Unmarshal(response, &deployment)
	return deployment, err
}

func (vm VirtualMachineClient) DeleteDeployment(cloudServiceName, deploymentName string) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}

	requestURL := fmt.Sprintf(deleteAzureDeploymentURL, cloudServiceName, deploymentName)
	return vm.client.SendAzureDeleteRequest(requestURL)
}

func (vm VirtualMachineClient) GetRole(cloudServiceName, deploymentName, roleName string) (*Role, error) {
	if cloudServiceName == "" {
		return nil, fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return nil, fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return nil, fmt.Errorf(errParamNotSpecified, "roleName")
	}

	role := new(Role)

	requestURL := fmt.Sprintf(azureRoleURL, cloudServiceName, deploymentName, roleName)
	response, azureErr := vm.client.SendAzureGetRequest(requestURL)
	if azureErr != nil {
		return nil, azureErr
	}

	err := xml.Unmarshal(response, role)
	if err != nil {
		return nil, err
	}

	return role, nil
}

// AddRole adds a Virtual Machine to a deployment of Virtual Machines, where role name = VM name
// See https://msdn.microsoft.com/en-us/library/azure/jj157186.aspx
func (vm VirtualMachineClient) AddRole(cloudServiceName string, deploymentName string, role Role) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}

	data, err := xml.Marshal(PersistentVMRole{Role: role})
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureAddRoleURL, cloudServiceName, deploymentName)
	return vm.client.SendAzurePostRequest(requestURL, data)
}

// UpdateRole updates the configuration of the specified virtual machine
// See https://msdn.microsoft.com/en-us/library/azure/jj157187.aspx
func (vm VirtualMachineClient) UpdateRole(cloudServiceName, deploymentName, roleName string, role Role) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	data, err := xml.Marshal(PersistentVMRole{Role: role})
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureRoleURL, cloudServiceName, deploymentName, roleName)
	return vm.client.SendAzurePutRequest(requestURL, "text/xml", data)
}

func (vm VirtualMachineClient) StartRole(cloudServiceName, deploymentName, roleName string) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	startRoleOperationBytes, err := xml.Marshal(StartRoleOperation{
		OperationType: "StartRoleOperation",
	})
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName)
	return vm.client.SendAzurePostRequest(requestURL, startRoleOperationBytes)
}

func (vm VirtualMachineClient) ShutdownRole(cloudServiceName, deploymentName, roleName string) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	shutdownRoleOperationBytes, err := xml.Marshal(ShutdownRoleOperation{
		OperationType: "ShutdownRoleOperation",
	})
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName)
	return vm.client.SendAzurePostRequest(requestURL, shutdownRoleOperationBytes)
}

func (vm VirtualMachineClient) RestartRole(cloudServiceName, deploymentName, roleName string) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	restartRoleOperationBytes, err := xml.Marshal(RestartRoleOperation{
		OperationType: "RestartRoleOperation",
	})
	if err != nil {
		return "", err
	}

	requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName)
	return vm.client.SendAzurePostRequest(requestURL, restartRoleOperationBytes)
}

func (vm VirtualMachineClient) DeleteRole(cloudServiceName, deploymentName, roleName string, deleteVHD bool) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	requestURL := fmt.Sprintf(azureRoleURL, cloudServiceName, deploymentName, roleName)
	if deleteVHD {
		requestURL += "?comp=media"
	}
	return vm.client.SendAzureDeleteRequest(requestURL)
}

func (vm VirtualMachineClient) GetRoleSizeList() (RoleSizeList, error) {
	roleSizeList := RoleSizeList{}

	response, err := vm.client.SendAzureGetRequest(azureRoleSizeListURL)
	if err != nil {
		return roleSizeList, err
	}

	err = xml.Unmarshal(response, &roleSizeList)
	return roleSizeList, err
}

// CaptureRole captures a VM role. If reprovisioningConfigurationSet is non-nil,
// the VM role is redeployed after capturing the image, otherwise, the original
// VM role is deleted.
//
// NOTE: an image resulting from this operation shows up in
// osimage.GetImageList() as images with Category "User".
func (vm VirtualMachineClient) CaptureRole(cloudServiceName, deploymentName, roleName, imageName, imageLabel string,
	reprovisioningConfigurationSet *ConfigurationSet) (management.OperationID, error) {
	if cloudServiceName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
	}
	if deploymentName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
	}
	if roleName == "" {
		return "", fmt.Errorf(errParamNotSpecified, "roleName")
	}

	if reprovisioningConfigurationSet != nil &&
		!(reprovisioningConfigurationSet.ConfigurationSetType == ConfigurationSetTypeLinuxProvisioning ||
			reprovisioningConfigurationSet.ConfigurationSetType == ConfigurationSetTypeWindowsProvisioning) {
		return "", fmt.Errorf("ConfigurationSet type can only be WindowsProvisioningConfiguration or LinuxProvisioningConfiguration")
	}

	operation := CaptureRoleOperation{
		OperationType:             "CaptureRoleOperation",
		PostCaptureAction:         PostCaptureActionReprovision,
		ProvisioningConfiguration: reprovisioningConfigurationSet,
		TargetImageLabel:          imageLabel,
		TargetImageName:           imageName,
	}
	if reprovisioningConfigurationSet == nil {
		operation.PostCaptureAction = PostCaptureActionDelete
	}

	data, err := xml.Marshal(operation)
	if err != nil {
		return "", err
	}

	return vm.client.SendAzurePostRequest(fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName), data)
}
<|endoftext|>