<commit_before>package cloudstack

import (
	"context"
	"fmt"
	"math/rand"
	"strings"
	"time"

	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/xanzy/go-cloudstack/cloudstack"
)

type stepSetupNetworking struct {
	privatePort int
	publicPort  int
}

func (s *stepSetupNetworking) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
	client := state.Get("client").(*cloudstack.CloudStackClient)
	config := state.Get("config").(*Config)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Setup networking...")

	if config.UseLocalIPAddress {
		ui.Message("Using the local IP address...")
		state.Put("commPort", config.Comm.Port())
		ui.Message("Networking has been setup!")
		return multistep.ActionContinue
	}

	// Generate a random public port used to configure our port forward.
	rand.Seed(time.Now().UnixNano())
	s.publicPort = 50000 + rand.Intn(10000)
	state.Put("commPort", s.publicPort)

	// Set the currently configured port to be the private port.
	s.privatePort = config.Comm.Port()

	// Retrieve the instance ID from the previously saved state.
	instanceID, ok := state.Get("instance_id").(string)
	if !ok || instanceID == "" {
		err := fmt.Errorf("Could not retrieve instance_id from state!")
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	network, _, err := client.Network.GetNetworkByID(
		config.Network,
		cloudstack.WithProject(config.Project),
	)
	if err != nil {
		err := fmt.Errorf("Failed to retrieve the network object: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	if config.PublicIPAddress == "" {
		ui.Message("Associating public IP address...")
		p := client.Address.NewAssociateIpAddressParams()

		if config.Project != "" {
			p.SetProjectid(config.Project)
		}

		if network.Vpcid != "" {
			p.SetVpcid(network.Vpcid)
		} else {
			p.SetNetworkid(network.Id)
		}

		p.SetZoneid(config.Zone)

		// Associate a new public IP address.
		ipAddr, err := client.Address.AssociateIpAddress(p)
		if err != nil {
			err := fmt.Errorf("Failed to associate public IP address: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		// Set the IP address and it's ID.
		config.PublicIPAddress = ipAddr.Id
		state.Put("ipaddress", ipAddr.Ipaddress)

		// Store the IP address ID.
		state.Put("ip_address_id", ipAddr.Id)
	}

	ui.Message("Creating port forward...")
	p := client.Firewall.NewCreatePortForwardingRuleParams(
		config.PublicIPAddress,
		s.privatePort,
		"TCP",
		s.publicPort,
		instanceID,
	)

	// Configure the port forward.
	p.SetNetworkid(network.Id)
	p.SetOpenfirewall(false)

	// Create the port forward.
	forward, err := client.Firewall.CreatePortForwardingRule(p)
	if err != nil {
		err := fmt.Errorf("Failed to create port forward: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Store the port forward ID.
	state.Put("port_forward_id", forward.Id)

	if network.Vpcid != "" {
		ui.Message("Creating network ACL rule...")

		if network.Aclid == "" {
			err := fmt.Errorf("Failed to configure the firewall: no ACL connected to the VPC network")
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		// Create a new parameter struct.
		p := client.NetworkACL.NewCreateNetworkACLParams("TCP")

		// Configure the network ACL rule.
		p.SetAclid(network.Aclid)
		p.SetAction("allow")
		p.SetCidrlist(config.CIDRList)
		p.SetStartport(s.privatePort)
		p.SetEndport(s.privatePort)
		p.SetTraffictype("ingress")

		// Create the network ACL rule.
		aclRule, err := client.NetworkACL.CreateNetworkACL(p)
		if err != nil {
			err := fmt.Errorf("Failed to create network ACL rule: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		// Store the network ACL rule ID.
		state.Put("network_acl_rule_id", aclRule.Id)
	} else {
		ui.Message("Creating firewall rule...")

		// Create a new parameter struct.
		p := client.Firewall.NewCreateFirewallRuleParams(config.PublicIPAddress, "TCP")

		// Configure the firewall rule.
		p.SetCidrlist(config.CIDRList)
		p.SetStartport(s.publicPort)
		p.SetEndport(s.publicPort)

		fwRule, err := client.Firewall.CreateFirewallRule(p)
		if err != nil {
			err := fmt.Errorf("Failed to create firewall rule: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		// Store the firewall rule ID.
		state.Put("firewall_rule_id", fwRule.Id)
	}

	ui.Message("Networking has been setup!")
	return multistep.ActionContinue
}

// Cleanup any resources that may have been created during the Run phase.
func (s *stepSetupNetworking) Cleanup(state multistep.StateBag) {
	client := state.Get("client").(*cloudstack.CloudStackClient)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Cleanup networking...")

	if fwRuleID, ok := state.Get("firewall_rule_id").(string); ok && fwRuleID != "" {
		// Create a new parameter struct.
		p := client.Firewall.NewDeleteFirewallRuleParams(fwRuleID)

		ui.Message("Deleting firewal rule...")
		if _, err := client.Firewall.DeleteFirewallRule(p); err != nil {
			// This is a very poor way to be told the ID does no longer exist :(
			if !strings.Contains(err.Error(), fmt.Sprintf(
				"Invalid parameter id value=%s due to incorrect long value format, "+
					"or entity does not exist", fwRuleID)) {
				ui.Error(fmt.Sprintf("Error deleting firewall rule: %s", err))
			}
		}
	}

	if aclRuleID, ok := state.Get("network_acl_rule_id").(string); ok && aclRuleID != "" {
		// Create a new parameter struct.
		p := client.NetworkACL.NewDeleteNetworkACLParams(aclRuleID)

		ui.Message("Deleting network ACL rule...")
		if _, err := client.NetworkACL.DeleteNetworkACL(p); err != nil {
			// This is a very poor way to be told the ID does no longer exist :(
			if !strings.Contains(err.Error(), fmt.Sprintf(
				"Invalid parameter id value=%s due to incorrect long value format, "+
					"or entity does not exist", aclRuleID)) {
				ui.Error(fmt.Sprintf("Error deleting network ACL rule: %s", err))
			}
		}
	}

	if forwardID, ok := state.Get("port_forward_id").(string); ok && forwardID != "" {
		// Create a new parameter struct.
		p := client.Firewall.NewDeletePortForwardingRuleParams(forwardID)

		ui.Message("Deleting port forward...")
		if _, err := client.Firewall.DeletePortForwardingRule(p); err != nil {
			// This is a very poor way to be told the ID does no longer exist :(
			if !strings.Contains(err.Error(), fmt.Sprintf(
				"Invalid parameter id value=%s due to incorrect long value format, "+
					"or entity does not exist", forwardID)) {
				ui.Error(fmt.Sprintf("Error deleting port forward: %s", err))
			}
		}
	}

	if ipAddrID, ok := state.Get("ip_address_id").(string); ok && ipAddrID != "" {
		// Create a new parameter struct.
		p := client.Address.NewDisassociateIpAddressParams(ipAddrID)

		ui.Message("Releasing public IP address...")
		if _, err := client.Address.DisassociateIpAddress(p); err != nil {
			// This is a very poor way to be told the ID does no longer exist :(
			if !strings.Contains(err.Error(), fmt.Sprintf(
				"Invalid parameter id value=%s due to incorrect long value format, "+
					"or entity does not exist", ipAddrID)) {
				ui.Error(fmt.Sprintf("Error releasing public IP address: %s", err))
			}
		}
	}

	ui.Message("Networking has been cleaned!")
	return
}
<commit_msg>spelling: firewall<commit_after>package cloudstack

import (
	"context"
	"fmt"
	"math/rand"
	"strings"
	"time"

	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/xanzy/go-cloudstack/cloudstack"
)

type stepSetupNetworking struct {
	privatePort int
	publicPort  int
}

func (s *stepSetupNetworking) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
	client := state.Get("client").(*cloudstack.CloudStackClient)
	config := state.Get("config").(*Config)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Setup networking...")

	if config.UseLocalIPAddress {
		ui.Message("Using the local IP address...")
		state.Put("commPort", config.Comm.Port())
		ui.Message("Networking has been setup!")
		return multistep.ActionContinue
	}

	// Generate a random public port used to configure our port forward.
	rand.Seed(time.Now().UnixNano())
	s.publicPort = 50000 + rand.Intn(10000)
	state.Put("commPort", s.publicPort)

	// Set the currently configured port to be the private port.
	s.privatePort = config.Comm.Port()

	// Retrieve the instance ID from the previously saved state.
	instanceID, ok := state.Get("instance_id").(string)
	if !ok || instanceID == "" {
		err := fmt.Errorf("Could not retrieve instance_id from state!")
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	network, _, err := client.Network.GetNetworkByID(
		config.Network,
		cloudstack.WithProject(config.Project),
	)
	if err != nil {
		err := fmt.Errorf("Failed to retrieve the network object: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	if config.PublicIPAddress == "" {
		ui.Message("Associating public IP address...")
		p := client.Address.NewAssociateIpAddressParams()

		if config.Project != "" {
			p.SetProjectid(config.Project)
		}

		if network.Vpcid != "" {
			p.SetVpcid(network.Vpcid)
		} else {
			p.SetNetworkid(network.Id)
		}

		p.SetZoneid(config.Zone)

		// Associate a new public IP address.
		ipAddr, err := client.Address.AssociateIpAddress(p)
		if err != nil {
			err := fmt.Errorf("Failed to associate public IP address: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		// Set the IP address and it's ID.
		config.PublicIPAddress = ipAddr.Id
		state.Put("ipaddress", ipAddr.Ipaddress)

		// Store the IP address ID.
		state.Put("ip_address_id", ipAddr.Id)
	}

	ui.Message("Creating port forward...")
	p := client.Firewall.NewCreatePortForwardingRuleParams(
		config.PublicIPAddress,
		s.privatePort,
		"TCP",
		s.publicPort,
		instanceID,
	)

	// Configure the port forward.
	p.SetNetworkid(network.Id)
	p.SetOpenfirewall(false)

	// Create the port forward.
	forward, err := client.Firewall.CreatePortForwardingRule(p)
	if err != nil {
		err := fmt.Errorf("Failed to create port forward: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Store the port forward ID.
	state.Put("port_forward_id", forward.Id)

	if network.Vpcid != "" {
		ui.Message("Creating network ACL rule...")

		if network.Aclid == "" {
			err := fmt.Errorf("Failed to configure the firewall: no ACL connected to the VPC network")
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		// Create a new parameter struct.
		p := client.NetworkACL.NewCreateNetworkACLParams("TCP")

		// Configure the network ACL rule.
		p.SetAclid(network.Aclid)
		p.SetAction("allow")
		p.SetCidrlist(config.CIDRList)
		p.SetStartport(s.privatePort)
		p.SetEndport(s.privatePort)
		p.SetTraffictype("ingress")

		// Create the network ACL rule.
		aclRule, err := client.NetworkACL.CreateNetworkACL(p)
		if err != nil {
			err := fmt.Errorf("Failed to create network ACL rule: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		// Store the network ACL rule ID.
		state.Put("network_acl_rule_id", aclRule.Id)
	} else {
		ui.Message("Creating firewall rule...")

		// Create a new parameter struct.
		p := client.Firewall.NewCreateFirewallRuleParams(config.PublicIPAddress, "TCP")

		// Configure the firewall rule.
		p.SetCidrlist(config.CIDRList)
		p.SetStartport(s.publicPort)
		p.SetEndport(s.publicPort)

		fwRule, err := client.Firewall.CreateFirewallRule(p)
		if err != nil {
			err := fmt.Errorf("Failed to create firewall rule: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		// Store the firewall rule ID.
		state.Put("firewall_rule_id", fwRule.Id)
	}

	ui.Message("Networking has been setup!")
	return multistep.ActionContinue
}

// Cleanup any resources that may have been created during the Run phase.
func (s *stepSetupNetworking) Cleanup(state multistep.StateBag) {
	client := state.Get("client").(*cloudstack.CloudStackClient)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Cleanup networking...")

	if fwRuleID, ok := state.Get("firewall_rule_id").(string); ok && fwRuleID != "" {
		// Create a new parameter struct.
		p := client.Firewall.NewDeleteFirewallRuleParams(fwRuleID)

		ui.Message("Deleting firewall rule...")
		if _, err := client.Firewall.DeleteFirewallRule(p); err != nil {
			// This is a very poor way to be told the ID does no longer exist :(
			if !strings.Contains(err.Error(), fmt.Sprintf(
				"Invalid parameter id value=%s due to incorrect long value format, "+
					"or entity does not exist", fwRuleID)) {
				ui.Error(fmt.Sprintf("Error deleting firewall rule: %s", err))
			}
		}
	}

	if aclRuleID, ok := state.Get("network_acl_rule_id").(string); ok && aclRuleID != "" {
		// Create a new parameter struct.
		p := client.NetworkACL.NewDeleteNetworkACLParams(aclRuleID)

		ui.Message("Deleting network ACL rule...")
		if _, err := client.NetworkACL.DeleteNetworkACL(p); err != nil {
			// This is a very poor way to be told the ID does no longer exist :(
			if !strings.Contains(err.Error(), fmt.Sprintf(
				"Invalid parameter id value=%s due to incorrect long value format, "+
					"or entity does not exist", aclRuleID)) {
				ui.Error(fmt.Sprintf("Error deleting network ACL rule: %s", err))
			}
		}
	}

	if forwardID, ok := state.Get("port_forward_id").(string); ok && forwardID != "" {
		// Create a new parameter struct.
		p := client.Firewall.NewDeletePortForwardingRuleParams(forwardID)

		ui.Message("Deleting port forward...")
		if _, err := client.Firewall.DeletePortForwardingRule(p); err != nil {
			// This is a very poor way to be told the ID does no longer exist :(
			if !strings.Contains(err.Error(), fmt.Sprintf(
				"Invalid parameter id value=%s due to incorrect long value format, "+
					"or entity does not exist", forwardID)) {
				ui.Error(fmt.Sprintf("Error deleting port forward: %s", err))
			}
		}
	}

	if ipAddrID, ok := state.Get("ip_address_id").(string); ok && ipAddrID != "" {
		// Create a new parameter struct.
		p := client.Address.NewDisassociateIpAddressParams(ipAddrID)

		ui.Message("Releasing public IP address...")
		if _, err := client.Address.DisassociateIpAddress(p); err != nil {
			// This is a very poor way to be told the ID does no longer exist :(
			if !strings.Contains(err.Error(), fmt.Sprintf(
				"Invalid parameter id value=%s due to incorrect long value format, "+
					"or entity does not exist", ipAddrID)) {
				ui.Error(fmt.Sprintf("Error releasing public IP address: %s", err))
			}
		}
	}

	ui.Message("Networking has been cleaned!")
	return
}
<|endoftext|>


<commit_before>package build

import (
	"fmt"
	"strings"

	"github.com/flant/werf/pkg/build/stage"
	imagePkg "github.com/flant/werf/pkg/image"
	"github.com/flant/werf/pkg/logger"
	"github.com/flant/werf/pkg/util"
)

const (
	BuildCacheVersion = "1"

	LocalImageStageImageNameFormat = "image-stage-%s"
	LocalImageStageImageFormat     = "image-stage-%s:%s"
)

func NewSignaturesPhase() *SignaturesPhase {
	return &SignaturesPhase{}
}

type SignaturesPhase struct{}

func (p *SignaturesPhase) Run(c *Conveyor) error {
	return logger.LogServiceProcess("Calculating signatures", logger.LogProcessOptions{WithoutBorder: true}, func() error {
		return logger.WithoutIndent(func() error { return p.run(c) })
	})
}

func (p *SignaturesPhase) run(c *Conveyor) error {
	for _, image := range c.imagesInOrder {
		err := logger.WithTag(image.LogName(), func() error {
			return p.calculateImageSignatures(c, image)
		})

		logger.LogOptionalLn()

		if err != nil {
			return err
		}
	}

	return nil
}

func (p *SignaturesPhase) calculateImageSignatures(c *Conveyor, image *Image) error {
	var prevStage stage.Interface

	image.SetupBaseImage(c)

	var prevBuiltImage imagePkg.ImageInterface
	prevImage := image.GetBaseImage()
	err := prevImage.SyncDockerState()
	if err != nil {
		return err
	}

	maxStageNameLength := 22

	var newStagesList []stage.Interface

	for _, s := range image.GetStages() {
		if prevImage.IsExists() {
			prevBuiltImage = prevImage
		}

		isEmpty, err := s.IsEmpty(c, prevBuiltImage)
		if err != nil {
			return fmt.Errorf("error checking stage %s is empty: %s", s.Name(), err)
		}
		if isEmpty {
			logger.LogInfoF("%s:%s <empty>\n", s.Name(), strings.Repeat(" ", maxStageNameLength-len(s.Name())))
			continue
		}

		stageDependencies, err := s.GetDependencies(c, prevImage)
		if err != nil {
			return err
		}

		checksumArgs := []string{stageDependencies, BuildCacheVersion}

		if prevStage != nil {
			checksumArgs = append(checksumArgs, prevStage.GetSignature())
		}

		stageSig := util.Sha256Hash(checksumArgs...)

		s.SetSignature(stageSig)

		imageName := fmt.Sprintf(LocalImageStageImageFormat, c.projectName(), stageSig)

		logger.LogInfoF("%s:%s %s\n", s.Name(), strings.Repeat(" ", maxStageNameLength-len(s.Name())), imageName)

		i := c.GetOrCreateImage(prevImage, imageName)
		s.SetImage(i)

		if err = i.SyncDockerState(); err != nil {
			return fmt.Errorf("error synchronizing docker state of stage %s: %s", s.Name(), err)
		}

		if err = s.AfterImageSyncDockerStateHook(c); err != nil {
			return err
		}

		newStagesList = append(newStagesList, s)

		prevStage = s
		prevImage = i
	}

	stageName := c.GetBuildingGitStage(image.name)
	if stageName != "" {
		logger.LogInfoF("Git files are actual on the %s stage\n", stageName)
	}

	image.SetStages(newStagesList)

	return nil
}
<commit_msg>Update local stage image name format (werf-stages-storage/<PROJECT>)<commit_after>package build

import (
	"fmt"
	"strings"

	"github.com/flant/werf/pkg/build/stage"
	imagePkg "github.com/flant/werf/pkg/image"
	"github.com/flant/werf/pkg/logger"
	"github.com/flant/werf/pkg/util"
)

const (
	BuildCacheVersion = "1"

	LocalImageStageImageNameFormat = "werf-stages-storage/%s"
	LocalImageStageImageFormat     = "werf-stages-storage/%s:%s"
)

func NewSignaturesPhase() *SignaturesPhase {
	return &SignaturesPhase{}
}

type SignaturesPhase struct{}

func (p *SignaturesPhase) Run(c *Conveyor) error {
	return logger.LogServiceProcess("Calculating signatures", logger.LogProcessOptions{WithoutBorder: true}, func() error {
		return logger.WithoutIndent(func() error { return p.run(c) })
	})
}

func (p *SignaturesPhase) run(c *Conveyor) error {
	for _, image := range c.imagesInOrder {
		err := logger.WithTag(image.LogName(), func() error {
			return p.calculateImageSignatures(c, image)
		})

		logger.LogOptionalLn()

		if err != nil {
			return err
		}
	}

	return nil
}

func (p *SignaturesPhase) calculateImageSignatures(c *Conveyor, image *Image) error {
	var prevStage stage.Interface

	image.SetupBaseImage(c)

	var prevBuiltImage imagePkg.ImageInterface
	prevImage := image.GetBaseImage()
	err := prevImage.SyncDockerState()
	if err != nil {
		return err
	}

	maxStageNameLength := 22

	var newStagesList []stage.Interface

	for _, s := range image.GetStages() {
		if prevImage.IsExists() {
			prevBuiltImage = prevImage
		}

		isEmpty, err := s.IsEmpty(c, prevBuiltImage)
		if err != nil {
			return fmt.Errorf("error checking stage %s is empty: %s", s.Name(), err)
		}
		if isEmpty {
			logger.LogInfoF("%s:%s <empty>\n", s.Name(), strings.Repeat(" ", maxStageNameLength-len(s.Name())))
			continue
		}

		stageDependencies, err := s.GetDependencies(c, prevImage)
		if err != nil {
			return err
		}

		checksumArgs := []string{stageDependencies, BuildCacheVersion}

		if prevStage != nil {
			checksumArgs = append(checksumArgs, prevStage.GetSignature())
		}

		stageSig := util.Sha256Hash(checksumArgs...)

		s.SetSignature(stageSig)

		imageName := fmt.Sprintf(LocalImageStageImageFormat, c.projectName(), stageSig)

		logger.LogInfoF("%s:%s %s\n", s.Name(), strings.Repeat(" ", maxStageNameLength-len(s.Name())), imageName)

		i := c.GetOrCreateImage(prevImage, imageName)
		s.SetImage(i)

		if err = i.SyncDockerState(); err != nil {
			return fmt.Errorf("error synchronizing docker state of stage %s: %s", s.Name(), err)
		}

		if err = s.AfterImageSyncDockerStateHook(c); err != nil {
			return err
		}

		newStagesList = append(newStagesList, s)

		prevStage = s
		prevImage = i
	}

	stageName := c.GetBuildingGitStage(image.name)
	if stageName != "" {
		logger.LogInfoF("Git files are actual on the %s stage\n", stageName)
	}

	image.SetStages(newStagesList)

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Heptio Inc.

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 output

import (
	"fmt"
	"os"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/kubernetes/pkg/printers"

	"github.com/heptio/ark/pkg/apis/ark/v1"
	"github.com/heptio/ark/pkg/cmd/util/flag"
	"github.com/heptio/ark/pkg/generated/clientset/versioned/scheme"
	"github.com/heptio/ark/pkg/util/encode"
)

// BindFlags defines a set of output-specific flags within the provided
// FlagSet.
func BindFlags(flags *pflag.FlagSet) {
	flags.StringP("output", "o", "table", "Output display format. For create commands, display the object but do not send it to the server. Valid formats are 'table', 'json', and 'yaml'.")
	labelColumns := flag.NewStringArray()
	flags.Var(&labelColumns, "label-columns", "a comma-separated list of labels to be displayed as columns")
	flags.Bool("show-labels", false, "show labels in the last column")
}

// ClearOutputFlagDefault sets the current and default value
// of the "output" flag to the empty string.
func ClearOutputFlagDefault(cmd *cobra.Command) {
	f := cmd.Flag("output")
	if f == nil {
		return
	}
	f.DefValue = ""
	f.Value.Set("")
}

// GetOutputFlagValue returns the value of the "output" flag
// in the provided command, or the zero value if not present.
func GetOutputFlagValue(cmd *cobra.Command) string {
	return flag.GetOptionalStringFlag(cmd, "output")
}

// GetLabelColumnsValues returns the value of the "label-columns" flag
// in the provided command, or the zero value if not present.
func GetLabelColumnsValues(cmd *cobra.Command) []string {
	return flag.GetOptionalStringArrayFlag(cmd, "label-columns")
}

// GetShowLabelsValue returns the value of the "show-labels" flag
// in the provided command, or the zero value if not present.
func GetShowLabelsValue(cmd *cobra.Command) bool {
	return flag.GetOptionalBoolFlag(cmd, "show-labels")
}

// ValidateFlags returns an error if any of the output-related flags
// were specified with invalid values, or nil otherwise.
func ValidateFlags(cmd *cobra.Command) error {
	if err := validateOutputFlag(cmd); err != nil {
		return err
	}
	return nil
}

func validateOutputFlag(cmd *cobra.Command) error {
	output := GetOutputFlagValue(cmd)
	switch output {
	case "", "table", "json", "yaml":
	default:
		return errors.Errorf("invalid output format %q - valid values are 'table', 'json', and 'yaml'", output)
	}
	return nil
}

// PrintWithFormat prints the provided object in the format specified by
// the command's flags.
func PrintWithFormat(c *cobra.Command, obj runtime.Object) (bool, error) {
	format := GetOutputFlagValue(c)
	if format == "" {
		return false, nil
	}

	switch format {
	case "table":
		return printTable(c, obj)
	case "json", "yaml":
		return printEncoded(obj, format)
	}

	return false, errors.Errorf("unsupported output format %q; valid values are 'table', 'json', and 'yaml'", format)
}

func printEncoded(obj runtime.Object, format string) (bool, error) {
	// assume we're printing obj
	toPrint := obj

	if meta.IsListType(obj) {
		list, _ := meta.ExtractList(obj)
		if len(list) == 1 {
			// if obj was a list and there was only 1 item, just print that 1 instead of a list
			toPrint = list[0]
		}
	}

	encoded, err := encode.Encode(toPrint, format)
	if err != nil {
		return false, err
	}

	fmt.Println(string(encoded))

	return true, nil
}

func printTable(cmd *cobra.Command, obj runtime.Object) (bool, error) {
	printer, err := NewPrinter(cmd)
	if err != nil {
		return false, err
	}

	printer.Handler(backupColumns, nil, printBackup)
	printer.Handler(backupColumns, nil, printBackupList)
	printer.Handler(restoreColumns, nil, printRestore)
	printer.Handler(restoreColumns, nil, printRestoreList)
	printer.Handler(scheduleColumns, nil, printSchedule)
	printer.Handler(scheduleColumns, nil, printScheduleList)

	err = printer.PrintObj(obj, os.Stdout)
	if err != nil {
		return false, err
	}

	return true, nil
}

// NewPrinter returns a printer for doing human-readable table printing of
// Ark objects.
func NewPrinter(cmd *cobra.Command) (*printers.HumanReadablePrinter, error) {
	encoder, err := encode.EncoderFor("json")
	if err != nil {
		return nil, err
	}

	options := printers.PrintOptions{
		NoHeaders:    flag.GetOptionalBoolFlag(cmd, "no-headers"),
		ShowLabels:   GetShowLabelsValue(cmd),
		ColumnLabels: GetLabelColumnsValues(cmd),
	}

	printer := printers.NewHumanReadablePrinter(
		encoder,
		scheme.Codecs.UniversalDecoder(v1.SchemeGroupVersion),
		options,
	)

	return printer, nil
}
<commit_msg>Stop passing encoder/decoder to NewHumanReadablePrinter<commit_after>/*
Copyright 2017 Heptio Inc.

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 output

import (
	"fmt"
	"os"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/kubernetes/pkg/printers"

	"github.com/heptio/ark/pkg/cmd/util/flag"
	"github.com/heptio/ark/pkg/util/encode"
)

// BindFlags defines a set of output-specific flags within the provided
// FlagSet.
func BindFlags(flags *pflag.FlagSet) {
	flags.StringP("output", "o", "table", "Output display format. For create commands, display the object but do not send it to the server. Valid formats are 'table', 'json', and 'yaml'.")
	labelColumns := flag.NewStringArray()
	flags.Var(&labelColumns, "label-columns", "a comma-separated list of labels to be displayed as columns")
	flags.Bool("show-labels", false, "show labels in the last column")
}

// ClearOutputFlagDefault sets the current and default value
// of the "output" flag to the empty string.
func ClearOutputFlagDefault(cmd *cobra.Command) {
	f := cmd.Flag("output")
	if f == nil {
		return
	}
	f.DefValue = ""
	f.Value.Set("")
}

// GetOutputFlagValue returns the value of the "output" flag
// in the provided command, or the zero value if not present.
func GetOutputFlagValue(cmd *cobra.Command) string {
	return flag.GetOptionalStringFlag(cmd, "output")
}

// GetLabelColumnsValues returns the value of the "label-columns" flag
// in the provided command, or the zero value if not present.
func GetLabelColumnsValues(cmd *cobra.Command) []string {
	return flag.GetOptionalStringArrayFlag(cmd, "label-columns")
}

// GetShowLabelsValue returns the value of the "show-labels" flag
// in the provided command, or the zero value if not present.
func GetShowLabelsValue(cmd *cobra.Command) bool {
	return flag.GetOptionalBoolFlag(cmd, "show-labels")
}

// ValidateFlags returns an error if any of the output-related flags
// were specified with invalid values, or nil otherwise.
func ValidateFlags(cmd *cobra.Command) error {
	if err := validateOutputFlag(cmd); err != nil {
		return err
	}
	return nil
}

func validateOutputFlag(cmd *cobra.Command) error {
	output := GetOutputFlagValue(cmd)
	switch output {
	case "", "table", "json", "yaml":
	default:
		return errors.Errorf("invalid output format %q - valid values are 'table', 'json', and 'yaml'", output)
	}
	return nil
}

// PrintWithFormat prints the provided object in the format specified by
// the command's flags.
func PrintWithFormat(c *cobra.Command, obj runtime.Object) (bool, error) {
	format := GetOutputFlagValue(c)
	if format == "" {
		return false, nil
	}

	switch format {
	case "table":
		return printTable(c, obj)
	case "json", "yaml":
		return printEncoded(obj, format)
	}

	return false, errors.Errorf("unsupported output format %q; valid values are 'table', 'json', and 'yaml'", format)
}

func printEncoded(obj runtime.Object, format string) (bool, error) {
	// assume we're printing obj
	toPrint := obj

	if meta.IsListType(obj) {
		list, _ := meta.ExtractList(obj)
		if len(list) == 1 {
			// if obj was a list and there was only 1 item, just print that 1 instead of a list
			toPrint = list[0]
		}
	}

	encoded, err := encode.Encode(toPrint, format)
	if err != nil {
		return false, err
	}

	fmt.Println(string(encoded))

	return true, nil
}

func printTable(cmd *cobra.Command, obj runtime.Object) (bool, error) {
	printer, err := NewPrinter(cmd)
	if err != nil {
		return false, err
	}

	printer.Handler(backupColumns, nil, printBackup)
	printer.Handler(backupColumns, nil, printBackupList)
	printer.Handler(restoreColumns, nil, printRestore)
	printer.Handler(restoreColumns, nil, printRestoreList)
	printer.Handler(scheduleColumns, nil, printSchedule)
	printer.Handler(scheduleColumns, nil, printScheduleList)

	err = printer.PrintObj(obj, os.Stdout)
	if err != nil {
		return false, err
	}

	return true, nil
}

// NewPrinter returns a printer for doing human-readable table printing of
// Ark objects.
func NewPrinter(cmd *cobra.Command) (*printers.HumanReadablePrinter, error) {
	options := printers.PrintOptions{
		NoHeaders:    flag.GetOptionalBoolFlag(cmd, "no-headers"),
		ShowLabels:   GetShowLabelsValue(cmd),
		ColumnLabels: GetLabelColumnsValues(cmd),
	}

	printer := printers.NewHumanReadablePrinter(
		nil, // encoder, only needed if we want/need to convert unstructured/unknown to typed objects
		nil, // decoder, only needed if we want/need to convert unstructured/unknown to typed objects
		options,
	)

	return printer, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package domodel

import (
	"fmt"
	"strconv"
	"strings"

	"k8s.io/kops/pkg/model"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/do"
	"k8s.io/kops/upup/pkg/fi/cloudup/dotasks"
)

// DropletBuilder configures droplets for the cluster
type DropletBuilder struct {
	*DOModelContext

	BootstrapScriptBuilder *model.BootstrapScriptBuilder
	Lifecycle              fi.Lifecycle
}

var _ fi.ModelBuilder = &DropletBuilder{}

func (d *DropletBuilder) Build(c *fi.ModelBuilderContext) error {
	sshKeyName, err := d.SSHKeyName()
	if err != nil {
		return err
	}

	splitSSHKeyName := strings.Split(sshKeyName, "-")
	sshKeyFingerPrint := splitSSHKeyName[len(splitSSHKeyName)-1]

	// replace "." with "-" since DO API does not accept "."
	clusterTag := do.TagKubernetesClusterNamePrefix + ":" + strings.Replace(d.ClusterName(), ".", "-", -1)
	clusterMasterTag := do.TagKubernetesClusterMasterPrefix + ":" + strings.Replace(d.ClusterName(), ".", "-", -1)

	masterIndexCount := 0
	// In the future, DigitalOcean will use Machine API to manage groups,
	// for now create d.InstanceGroups.Spec.MinSize amount of droplets
	for _, ig := range d.InstanceGroups {
		name := d.AutoscalingGroupName(ig)

		droplet := dotasks.Droplet{
			Count:     int(fi.Int32Value(ig.Spec.MinSize)),
			Name:      fi.String(name),
			Lifecycle: d.Lifecycle,

			// kops do supports allow only 1 region
			Region: fi.String(d.Cluster.Spec.Subnets[0].Region),
			Size:   fi.String(ig.Spec.MachineType),
			Image:  fi.String(ig.Spec.Image),
			SSHKey: fi.String(sshKeyFingerPrint),
			Tags:   []string{clusterTag},
		}

		if ig.IsMaster() {
			masterIndexCount++
			// create tag based on etcd name. etcd name is now prefixed with etcd-
			// Ref: https://github.com/kubernetes/kops/commit/31f8cbd571964f19d3c31024ddba918998d29929
			clusterTagIndex := do.TagKubernetesClusterIndex + ":" + "etcd-" + strconv.Itoa(masterIndexCount)
			droplet.Tags = append(droplet.Tags, clusterTagIndex)
			droplet.Tags = append(droplet.Tags, clusterMasterTag)
			droplet.Tags = append(droplet.Tags, do.TagKubernetesInstanceGroup+":"+ig.Name)
		} else {
			droplet.Tags = append(droplet.Tags, do.TagKubernetesInstanceGroup+":"+ig.Name)
		}

		fmt.Printf("Checking network id = %s \n", d.Cluster.Spec.NetworkID)
		if d.Cluster.Spec.NetworkID != "" {
			droplet.VPC = fi.String(d.Cluster.Spec.NetworkID)
			fmt.Printf("Droplet info = %v \n", droplet)
		}

		userData, err := d.BootstrapScriptBuilder.ResourceNodeUp(c, ig)
		if err != nil {
			return err
		}
		droplet.UserData = userData

		c.AddTask(&droplet)
	}
	return nil
}
<commit_msg>Minor changes to remove additional logging<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package domodel

import (
	"strconv"
	"strings"

	"k8s.io/kops/pkg/model"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/do"
	"k8s.io/kops/upup/pkg/fi/cloudup/dotasks"
)

// DropletBuilder configures droplets for the cluster
type DropletBuilder struct {
	*DOModelContext

	BootstrapScriptBuilder *model.BootstrapScriptBuilder
	Lifecycle              fi.Lifecycle
}

var _ fi.ModelBuilder = &DropletBuilder{}

func (d *DropletBuilder) Build(c *fi.ModelBuilderContext) error {
	sshKeyName, err := d.SSHKeyName()
	if err != nil {
		return err
	}

	splitSSHKeyName := strings.Split(sshKeyName, "-")
	sshKeyFingerPrint := splitSSHKeyName[len(splitSSHKeyName)-1]

	// replace "." with "-" since DO API does not accept "."
	clusterTag := do.TagKubernetesClusterNamePrefix + ":" + strings.Replace(d.ClusterName(), ".", "-", -1)
	clusterMasterTag := do.TagKubernetesClusterMasterPrefix + ":" + strings.Replace(d.ClusterName(), ".", "-", -1)

	masterIndexCount := 0
	// In the future, DigitalOcean will use Machine API to manage groups,
	// for now create d.InstanceGroups.Spec.MinSize amount of droplets
	for _, ig := range d.InstanceGroups {
		name := d.AutoscalingGroupName(ig)

		droplet := dotasks.Droplet{
			Count:     int(fi.Int32Value(ig.Spec.MinSize)),
			Name:      fi.String(name),
			Lifecycle: d.Lifecycle,

			// kops do supports allow only 1 region
			Region: fi.String(d.Cluster.Spec.Subnets[0].Region),
			Size:   fi.String(ig.Spec.MachineType),
			Image:  fi.String(ig.Spec.Image),
			SSHKey: fi.String(sshKeyFingerPrint),
			Tags:   []string{clusterTag},
		}

		if ig.IsMaster() {
			masterIndexCount++
			// create tag based on etcd name. etcd name is now prefixed with etcd-
			// Ref: https://github.com/kubernetes/kops/commit/31f8cbd571964f19d3c31024ddba918998d29929
			clusterTagIndex := do.TagKubernetesClusterIndex + ":" + "etcd-" + strconv.Itoa(masterIndexCount)
			droplet.Tags = append(droplet.Tags, clusterTagIndex)
			droplet.Tags = append(droplet.Tags, clusterMasterTag)
			droplet.Tags = append(droplet.Tags, do.TagKubernetesInstanceGroup+":"+ig.Name)
		} else {
			droplet.Tags = append(droplet.Tags, do.TagKubernetesInstanceGroup+":"+ig.Name)
		}

		if d.Cluster.Spec.NetworkID != "" {
			droplet.VPC = fi.String(d.Cluster.Spec.NetworkID)
		}

		userData, err := d.BootstrapScriptBuilder.ResourceNodeUp(c, ig)
		if err != nil {
			return err
		}
		droplet.UserData = userData

		c.AddTask(&droplet)
	}
	return nil
}
<|endoftext|>


<commit_before>/*

For keeping a minimum running, perhaps when doing a routing table update, if destination hosts are all
 expired or about to expire we start more.

*/

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/iron-io/common"
	"github.com/iron-io/golog"
	"github.com/iron-io/iron_go/cache"
	"github.com/iron-io/iron_go/worker"
	"labix.org/v2/mgo"
	"log"
	"math/rand"
	// "net"
	"flag"
	"net/http"
	"net/url"
	"reflect"
	"runtime"
	"strings"
	"time"
	//	"io/ioutil"
)

var config struct {
	Iron struct {
	Token       string `json:"token"`
	ProjectId   string `json:"project_id"`
	SuperToken  string `json:"super_token"`
	CacheHost 	   string `json:"cache_host"`
	WorkerHost  string `json:"worker_host"`
} `json:"iron"`
	MongoAuth common.MongoConfig `json:"mongo_auth"`
	Logging   struct {
	To     string `json:"to"`
	Level  string `json:"level"`
	Prefix string `json:"prefix"`
}
}

var version = "0.0.15"

//var routingTable = map[string]*Route{}
var icache = cache.New("routing-table")

var (
	ironAuth *common.IronAuth
)

func init() {

}

type Route struct {
	// TODO: Change destinations to a simple cache so it can expire entries after 55 minutes (the one we use in common?)
	Host         string   `json:"host"`
	Destinations []string `json:"destinations"`
	ProjectId    string   `json:"project_id"`
	Token        string   `json:"token"` // store this so we can queue up new workers on demand
	CodeName     string   `json:"code_name"`
}

// for adding new hosts
type Route2 struct {
	Host string `json:"host"`
	Dest string `json:"dest"`
}

func main() {

	var configFile string
	var env string
	flag.StringVar(&configFile, "c", "", "Config file name")
	// when this was e, it was erroring out.
	flag.StringVar(&env, "e", "development", "environment")

	flag.Parse() // Scans the arg list and sets up flags

	// Deployer is now passing -c in since we're using upstart and it doesn't know what directory to run in
	if configFile == "" {
		configFile = "config_" + env + ".json"
	}

	common.LoadConfig("iron_mq", configFile, &config)
	common.SetLogLevel(config.Logging.Level)
	common.SetLogLocation(config.Logging.To, config.Logging.Prefix)

	golog.Infoln("Starting up router version", version)

	runtime.GOMAXPROCS(runtime.NumCPU())
	log.Println("Running on", runtime.NumCPU(), "CPUs")

	hosts := strings.Join(config.MongoAuth.Hosts, ",")
	session, err := mgo.Dial(hosts)
	if err != nil {
		log.Panicln(err)
	}
	// recompile????
	err = session.DB(config.MongoAuth.Database).Login(config.MongoAuth.Username, config.MongoAuth.Password)
	if err != nil {
		log.Fatalln("Could not log in to db:", err)
	}
	ironAuth = common.NewIronAuth(session, config.MongoAuth.Database)

	cacheConfigMap := map[string]interface{}{
		"token": config.Iron.Token,
		"project_id": config.Iron.ProjectId,
	}
	if config.Iron.CacheHost != "" {
		cacheConfigMap["host"] = config.Iron.CacheHost
	}
	icache.Settings.UseConfigMap(cacheConfigMap)

	r := mux.NewRouter()

	// This has to stay above the r.Host() one.
	s2 := r.Headers("Iron-Router", "").Subrouter()
	s2.Handle("/", &WorkerHandler{})

	s := r.Host("router.irondns.info").Subrouter()
	s.Handle("/1/projects/{project_id:[0-9a-fA-F]{24}}/register", &common.AuthHandler{&Register{}, ironAuth})
	s.HandleFunc("/ping", Ping)
	s.Handle("/addworker", &WorkerHandler{})
	s.HandleFunc("/", Ping)

	r.HandleFunc("/ping", Ping) // for ELB health check
	r.HandleFunc("/", ProxyFunc)

	http.Handle("/", r)
	port := 80
	golog.Infoln("Router started, listening and serving on port", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
}

func ProxyFunc(w http.ResponseWriter, req *http.Request) {
	golog.Infoln("HOST:", req.Host)
	host := strings.Split(req.Host, ":")[0]

	// We look up the destinations in the routing table and there can be 3 possible scenarios:
	// 1) This host was never registered so we return 404
	// 2) This host has active workers so we do the proxy
	// 3) This host has no active workers so we queue one (or more) up and return a 503 or something with message that says "try again in a minute"
	//	route := routingTable[host]
	golog.Infoln("getting route for host:", host, "--")
	route, err := getRoute(host)
	golog.Infoln("route:", route)
	golog.Infoln("err:", err)
	if err != nil {
		common.SendError(w, 400, fmt.Sprintln("Host not registered or error!", err))
		return
	}
	//	if route == nil { // route.Host == "" {
	//		common.SendError(w, 400, fmt.Sprintln(w, "Host not configured!"))
	//		return
	//	}

	serveEndpoint(w, req, route)
}

func serveEndpoint(w http.ResponseWriter, req *http.Request, route *Route) {
	dlen := len(route.Destinations)
	if dlen == 0 {
		golog.Infoln("No workers running, starting new task.")
		startNewWorker(route)
		common.SendError(w, 500, fmt.Sprintln("No workers running, starting them up..."))
		return
	}
	if dlen < 3 {
		golog.Infoln("Less than three workers running, starting a new task.")
		startNewWorker(route)
	}
	destIndex := rand.Intn(dlen)
	destUrlString := route.Destinations[destIndex]
	// todo: should check if http:// already exists.
	destUrlString2 := "http://" + destUrlString
	destUrl, err := url.Parse(destUrlString2)
	if err != nil {
		removeDestination(route, destIndex, w)
		golog.Infoln("error!", err)
		common.SendError(w, 500, fmt.Sprintln("Internal error occurred:", err))
		return
	}
	// todo: check destination runtime and remove it if it's expired so we don't send requests to an endpoint that is about to be killed
	golog.Infoln("proxying to", destUrl)
	proxy := NewSingleHostReverseProxy(destUrl)
	err = proxy.ServeHTTP(w, req)
	if err != nil {
		golog.Infoln("Error proxying!", err)
		etype := reflect.TypeOf(err)
		golog.Infoln("err type:", etype)
		// can't figure out how to compare types so comparing strings.... lame.
		if true || strings.Contains(etype.String(), "net.OpError") { // == reflect.TypeOf(net.OpError{}) { // couldn't figure out a better way to do this
			golog.Infoln("It's a network error so we're going to remove destination.")
			removeDestination(route, destIndex, w)
			serveEndpoint(w, req, route)
			return
		}
		common.SendError(w, 500, fmt.Sprintln("Internal error occurred:", err))
		return
	}
	golog.Infoln("Served!")
}

func removeDestination(route *Route, destIndex int, w http.ResponseWriter) {
	route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
	err := putRoute(route)
	if err != nil {
		golog.Infoln("Couldn't update routing table:", err)
		common.SendError(w, 500, fmt.Sprintln("couldn't update routing table", err))
		return
	}
	golog.Infoln("New route after remove destination:", route)
//	if len(route.Destinations) < 3 {
//		golog.Infoln("After network error, there are less than three destinations, so starting a new one. ")
//		// always want at least three running
//		startNewWorker(route)
//	}
}

func startNewWorker(route *Route) error {
	golog.Infoln("Starting a new worker")
	// start new worker
	payload := map[string]interface{}{
		"token":      config.Iron.SuperToken,
		"project_id": route.ProjectId,
		"code_name":  route.CodeName,
	}
	if config.Iron.WorkerHost != "" {
		payload["host"] = config.Iron.WorkerHost
	}
	workerapi := worker.New()
	workerapi.Settings.UseConfigMap(payload)
	jsonPayload, err := json.Marshal(payload)
	if err != nil {
		golog.Infoln("Couldn't marshal json!", err)
		return err
	}
	timeout := time.Second*time.Duration(1800 + rand.Intn(600)) // a little random factor in here to spread out worker deaths
	task := worker.Task{
		CodeName: route.CodeName,
		Payload:  string(jsonPayload),
		Timeout:  &timeout, // let's have these die quickly while testing
	}
	tasks := make([]worker.Task, 1)
	tasks[0] = task
	taskIds, err := workerapi.TaskQueue(tasks...)
	golog.Infoln("Tasks queued.", taskIds)
	if err != nil {
		golog.Infoln("Couldn't queue up worker!", err)
		return err
	}
	return err
}

type Register struct{}

// This registers a new host
func (r *Register) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	log.Println("Register called!")

	vars := mux.Vars(req)
	projectId := vars["project_id"]
	token := ironAuth.GetToken(req)
	golog.Infoln("project_id:", projectId, "token:", token.Token)

	route := Route{}
	if !common.ReadJSON(w, req, &route) {
		return
	}
	golog.Infoln("body read into route:", route)
	route.ProjectId = projectId
	route.Token = token.Token

	_, err := getRoute(route.Host)
	if err == nil {
		common.SendError(w, 400, fmt.Sprintln("This host is already registered. If you believe this is in error, please contact support@iron.io to resolve the issue.", err))
		return
		//			route = &Route{}
	}

	// todo: do we need to close body?
	err = putRoute(&route)
	if err != nil {
		golog.Infoln("couldn't register host:", err)
		common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
		return
	}
	golog.Infoln("registered route:", route)
	fmt.Fprintln(w, "Host registered successfully.")
}

type WorkerHandler struct {
}

// When a worker starts up, it calls this
func (wh *WorkerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	golog.Infoln("AddWorker called!")

	// get project id and token
	projectId := req.FormValue("project_id")
	token := req.FormValue("token")
	//	codeName := req.FormValue("code_name")
	golog.Infoln("project_id:", projectId, "token:", token)

	// check header for what operation to perform
	routerHeader := req.Header.Get("Iron-Router")
	if routerHeader == "register" {

	} else {
		r2 := Route2{}
		if !common.ReadJSON(w, req, &r2) {
			return
		}
		// todo: do we need to close body?
		golog.Infoln("DECODED:", r2)
		route, err := getRoute(r2.Host)
		if err != nil {
			common.SendError(w, 400, fmt.Sprintln("This host is not registered!", err))
			return
		}
		golog.Infoln("ROUTE:", route)
		route.Destinations = append(route.Destinations, r2.Dest)
		golog.Infoln("ROUTE new:", route)
		err = putRoute(route)
		if err != nil {
			golog.Infoln("couldn't register host:", err)
			common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
			return
		}
		fmt.Fprintln(w, "Worker added")
	}
}

func getRoute(host string) (*Route, error) {
	rx, err := icache.Get(host)
	if err != nil {
		return nil, err
	}
	rx2 := []byte(rx.(string))
	route := Route{}
	err = json.Unmarshal(rx2, &route)
	if err != nil {
		return nil, err
	}
	return &route, err
}

func putRoute(route *Route) error {
	item := cache.Item{}
	v, err := json.Marshal(route)
	if err != nil {
		return err
	}
	item.Value = string(v)
	err = icache.Put(route.Host, &item)
	return err
}

func Ping(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, "pong")
}
<commit_msg>Added more logging.<commit_after>/*

For keeping a minimum running, perhaps when doing a routing table update, if destination hosts are all
 expired or about to expire we start more.

*/

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/iron-io/common"
	"github.com/iron-io/golog"
	"github.com/iron-io/iron_go/cache"
	"github.com/iron-io/iron_go/worker"
	"labix.org/v2/mgo"
	"log"
	"math/rand"
	// "net"
	"flag"
	"net/http"
	"net/url"
	"reflect"
	"runtime"
	"strings"
	"time"
	//	"io/ioutil"
)

var config struct {
	Iron struct {
	Token       string `json:"token"`
	ProjectId   string `json:"project_id"`
	SuperToken  string `json:"super_token"`
	CacheHost 	   string `json:"cache_host"`
	WorkerHost  string `json:"worker_host"`
} `json:"iron"`
	MongoAuth common.MongoConfig `json:"mongo_auth"`
	Logging   struct {
	To     string `json:"to"`
	Level  string `json:"level"`
	Prefix string `json:"prefix"`
}
}

var version = "0.0.15"

//var routingTable = map[string]*Route{}
var icache = cache.New("routing-table")

var (
	ironAuth *common.IronAuth
)

func init() {

}

type Route struct {
	// TODO: Change destinations to a simple cache so it can expire entries after 55 minutes (the one we use in common?)
	Host         string   `json:"host"`
	Destinations []string `json:"destinations"`
	ProjectId    string   `json:"project_id"`
	Token        string   `json:"token"` // store this so we can queue up new workers on demand
	CodeName     string   `json:"code_name"`
}

// for adding new hosts
type Route2 struct {
	Host string `json:"host"`
	Dest string `json:"dest"`
}

func main() {

	var configFile string
	var env string
	flag.StringVar(&configFile, "c", "", "Config file name")
	// when this was e, it was erroring out.
	flag.StringVar(&env, "e", "development", "environment")

	flag.Parse() // Scans the arg list and sets up flags

	// Deployer is now passing -c in since we're using upstart and it doesn't know what directory to run in
	if configFile == "" {
		configFile = "config_" + env + ".json"
	}

	common.LoadConfig("iron_mq", configFile, &config)
	common.SetLogLevel(config.Logging.Level)
	common.SetLogLocation(config.Logging.To, config.Logging.Prefix)

	golog.Infoln("Starting up router version", version)

	runtime.GOMAXPROCS(runtime.NumCPU())
	log.Println("Running on", runtime.NumCPU(), "CPUs")

	hosts := strings.Join(config.MongoAuth.Hosts, ",")
	session, err := mgo.Dial(hosts)
	if err != nil {
		log.Panicln(err)
	}
	// recompile????
	err = session.DB(config.MongoAuth.Database).Login(config.MongoAuth.Username, config.MongoAuth.Password)
	if err != nil {
		log.Fatalln("Could not log in to db:", err)
	}
	ironAuth = common.NewIronAuth(session, config.MongoAuth.Database)

	cacheConfigMap := map[string]interface{}{
		"token": config.Iron.Token,
		"project_id": config.Iron.ProjectId,
	}
	if config.Iron.CacheHost != "" {
		cacheConfigMap["host"] = config.Iron.CacheHost
	}
	icache.Settings.UseConfigMap(cacheConfigMap)

	r := mux.NewRouter()

	// This has to stay above the r.Host() one.
	s2 := r.Headers("Iron-Router", "").Subrouter()
	s2.Handle("/", &WorkerHandler{})

	s := r.Host("router.irondns.info").Subrouter()
	s.Handle("/1/projects/{project_id:[0-9a-fA-F]{24}}/register", &common.AuthHandler{&Register{}, ironAuth})
	s.HandleFunc("/ping", Ping)
	s.Handle("/addworker", &WorkerHandler{})
	s.HandleFunc("/", Ping)

	r.HandleFunc("/ping", Ping) // for ELB health check
	r.HandleFunc("/", ProxyFunc)

	http.Handle("/", r)
	port := 80
	golog.Infoln("Router started, listening and serving on port", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
}

func ProxyFunc(w http.ResponseWriter, req *http.Request) {
	golog.Infoln("HOST:", req.Host)
	host := strings.Split(req.Host, ":")[0]

	// We look up the destinations in the routing table and there can be 3 possible scenarios:
	// 1) This host was never registered so we return 404
	// 2) This host has active workers so we do the proxy
	// 3) This host has no active workers so we queue one (or more) up and return a 503 or something with message that says "try again in a minute"
	//	route := routingTable[host]
	golog.Infoln("getting route for host:", host, "--")
	route, err := getRoute(host)
	golog.Infoln("route:", route)
	golog.Infoln("err:", err)
	if err != nil {
		common.SendError(w, 400, fmt.Sprintln("Host not registered or error!", err))
		return
	}
	//	if route == nil { // route.Host == "" {
	//		common.SendError(w, 400, fmt.Sprintln(w, "Host not configured!"))
	//		return
	//	}

	serveEndpoint(w, req, route)
}

func serveEndpoint(w http.ResponseWriter, req *http.Request, route *Route) {
	dlen := len(route.Destinations)
	if dlen == 0 {
		golog.Infoln("No workers running, starting new task.")
		startNewWorker(route)
		common.SendError(w, 500, fmt.Sprintln("No workers running, starting them up..."))
		return
	}
	if dlen < 3 {
		golog.Infoln("Less than three workers running, starting a new task.")
		startNewWorker(route)
	}
	destIndex := rand.Intn(dlen)
	destUrlString := route.Destinations[destIndex]
	// todo: should check if http:// already exists.
	destUrlString2 := "http://" + destUrlString
	destUrl, err := url.Parse(destUrlString2)
	if err != nil {
		removeDestination(route, destIndex, w)
		golog.Infoln("error!", err)
		common.SendError(w, 500, fmt.Sprintln("Internal error occurred:", err))
		return
	}
	// todo: check destination runtime and remove it if it's expired so we don't send requests to an endpoint that is about to be killed
	golog.Infoln("proxying to", destUrl)
	proxy := NewSingleHostReverseProxy(destUrl)
	err = proxy.ServeHTTP(w, req)
	if err != nil {
		golog.Infoln("Error proxying!", err)
		etype := reflect.TypeOf(err)
		golog.Infoln("err type:", etype)
		// can't figure out how to compare types so comparing strings.... lame.
		if true || strings.Contains(etype.String(), "net.OpError") { // == reflect.TypeOf(net.OpError{}) { // couldn't figure out a better way to do this
			golog.Infoln("It's a network error so we're going to remove destination.")
			removeDestination(route, destIndex, w)
			serveEndpoint(w, req, route)
			return
		}
		common.SendError(w, 500, fmt.Sprintln("Internal error occurred:", err))
		return
	}
	golog.Infoln("Served!")
}

func removeDestination(route *Route, destIndex int, w http.ResponseWriter) {
	golog.Infoln("Removing destination", destIndex, "from route:", route)
	route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
	err := putRoute(route)
	if err != nil {
		golog.Infoln("Couldn't update routing table:", err)
		common.SendError(w, 500, fmt.Sprintln("couldn't update routing table", err))
		return
	}
	golog.Infoln("New route after remove destination:", route)
//	if len(route.Destinations) < 3 {
//		golog.Infoln("After network error, there are less than three destinations, so starting a new one. ")
//		// always want at least three running
//		startNewWorker(route)
//	}
}

func startNewWorker(route *Route) error {
	golog.Infoln("Starting a new worker")
	// start new worker
	payload := map[string]interface{}{
		"token":      config.Iron.SuperToken,
		"project_id": route.ProjectId,
		"code_name":  route.CodeName,
	}
	if config.Iron.WorkerHost != "" {
		payload["host"] = config.Iron.WorkerHost
	}
	workerapi := worker.New()
	workerapi.Settings.UseConfigMap(payload)
	jsonPayload, err := json.Marshal(payload)
	if err != nil {
		golog.Infoln("Couldn't marshal json!", err)
		return err
	}
	timeout := time.Second*time.Duration(1800 + rand.Intn(600)) // a little random factor in here to spread out worker deaths
	task := worker.Task{
		CodeName: route.CodeName,
		Payload:  string(jsonPayload),
		Timeout:  &timeout, // let's have these die quickly while testing
	}
	tasks := make([]worker.Task, 1)
	tasks[0] = task
	taskIds, err := workerapi.TaskQueue(tasks...)
	golog.Infoln("Tasks queued.", taskIds)
	if err != nil {
		golog.Infoln("Couldn't queue up worker!", err)
		return err
	}
	return err
}

type Register struct{}

// This registers a new host
func (r *Register) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	log.Println("Register called!")

	vars := mux.Vars(req)
	projectId := vars["project_id"]
	token := ironAuth.GetToken(req)
	golog.Infoln("project_id:", projectId, "token:", token.Token)

	route := Route{}
	if !common.ReadJSON(w, req, &route) {
		return
	}
	golog.Infoln("body read into route:", route)
	route.ProjectId = projectId
	route.Token = token.Token

	_, err := getRoute(route.Host)
	if err == nil {
		common.SendError(w, 400, fmt.Sprintln("This host is already registered. If you believe this is in error, please contact support@iron.io to resolve the issue.", err))
		return
		//			route = &Route{}
	}

	// todo: do we need to close body?
	err = putRoute(&route)
	if err != nil {
		golog.Infoln("couldn't register host:", err)
		common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
		return
	}
	golog.Infoln("registered route:", route)
	fmt.Fprintln(w, "Host registered successfully.")
}

type WorkerHandler struct {
}

// When a worker starts up, it calls this
func (wh *WorkerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	golog.Infoln("AddWorker called!")

	// get project id and token
	projectId := req.FormValue("project_id")
	token := req.FormValue("token")
	//	codeName := req.FormValue("code_name")
	golog.Infoln("project_id:", projectId, "token:", token)

	// check header for what operation to perform
	routerHeader := req.Header.Get("Iron-Router")
	if routerHeader == "register" {

	} else {
		r2 := Route2{}
		if !common.ReadJSON(w, req, &r2) {
			return
		}
		// todo: do we need to close body?
		golog.Infoln("Incoming body from worker:", r2)
		route, err := getRoute(r2.Host)
		if err != nil {
			common.SendError(w, 400, fmt.Sprintln("This host is not registered!", err))
			return
		}
		golog.Infoln("ROUTE:", route)
		route.Destinations = append(route.Destinations, r2.Dest)
		golog.Infoln("ROUTE new:", route)
		err = putRoute(route)
		if err != nil {
			golog.Infoln("couldn't register host:", err)
			common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
			return
		}
		fmt.Fprintln(w, "Worker added")
	}
}

func getRoute(host string) (*Route, error) {
	golog.Infoln("getRoute for host:", host)
	rx, err := icache.Get(host)
	if err != nil {
		return nil, err
	}
	rx2 := []byte(rx.(string))
	route := Route{}
	err = json.Unmarshal(rx2, &route)
	if err != nil {
		return nil, err
	}
	return &route, err
}

func putRoute(route *Route) error {
	item := cache.Item{}
	v, err := json.Marshal(route)
	if err != nil {
		return err
	}
	item.Value = string(v)
	err = icache.Put(route.Host, &item)
	return err
}

func Ping(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, "pong")
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Rook 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 operator to manage Kubernetes storage.
package operator

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/coreos/pkg/capnslog"
	opkit "github.com/rook/operator-kit"
	"github.com/rook/rook/pkg/clusterd"
	"github.com/rook/rook/pkg/daemon/ceph/agent/flexvolume"
	"github.com/rook/rook/pkg/daemon/ceph/agent/flexvolume/attachment"
	"github.com/rook/rook/pkg/operator/ceph/agent"
	"github.com/rook/rook/pkg/operator/ceph/cluster"
	"github.com/rook/rook/pkg/operator/ceph/csi"
	"github.com/rook/rook/pkg/operator/ceph/file"
	"github.com/rook/rook/pkg/operator/ceph/object"
	objectuser "github.com/rook/rook/pkg/operator/ceph/object/user"
	"github.com/rook/rook/pkg/operator/ceph/pool"
	"github.com/rook/rook/pkg/operator/ceph/provisioner"
	"github.com/rook/rook/pkg/operator/discover"
	"github.com/rook/rook/pkg/operator/k8sutil"
	v1 "k8s.io/api/core/v1"
	"sigs.k8s.io/sig-storage-lib-external-provisioner/controller"
)

// volume provisioner constant
const (
	provisionerName       = "ceph.rook.io/block"
	provisionerNameLegacy = "rook.io/block"
)

var logger = capnslog.NewPackageLogger("github.com/rook/rook", "operator")

// The supported configurations for the volume provisioner
var provisionerConfigs = map[string]string{
	provisionerName:       flexvolume.FlexvolumeVendor,
	provisionerNameLegacy: flexvolume.FlexvolumeVendorLegacy,
}

var (
	// Whether to enable the flex driver. If true, the rook-ceph-agent daemonset will be started.
	EnableFlexDriver = true
	// Whether to enable the daemon for device discovery. If true, the rook-ceph-discover daemonset will be started.
	EnableDiscoveryDaemon = true
)

// Operator type for managing storage
type Operator struct {
	context         *clusterd.Context
	resources       []opkit.CustomResource
	rookImage       string
	securityAccount string
	// The custom resource that is global to the kubernetes cluster.
	// The cluster is global because you create multiple clusters in k8s
	clusterController     *cluster.ClusterController
	delayedDaemonsStarted bool
}

// New creates an operator instance
func New(context *clusterd.Context, volumeAttachmentWrapper attachment.Attachment, rookImage, securityAccount string) *Operator {
	schemes := []opkit.CustomResource{cluster.ClusterResource, pool.PoolResource, object.ObjectStoreResource, objectuser.ObjectStoreUserResource,
		file.FilesystemResource, attachment.VolumeResource}
	o := &Operator{
		context:         context,
		resources:       schemes,
		rookImage:       rookImage,
		securityAccount: securityAccount,
	}
	o.clusterController = cluster.NewClusterController(context, rookImage, volumeAttachmentWrapper, o.startSystemDaemons)
	return o
}

// Run the operator instance
func (o *Operator) Run() error {

	namespace := os.Getenv(k8sutil.PodNamespaceEnvVar)
	if namespace == "" {
		return fmt.Errorf("Rook operator namespace is not provided. Expose it via downward API in the rook operator manifest file using environment variable %s", k8sutil.PodNamespaceEnvVar)
	}

	if EnableDiscoveryDaemon {
		rookDiscover := discover.New(o.context.Clientset)
		if err := rookDiscover.Start(namespace, o.rookImage, o.securityAccount); err != nil {
			return fmt.Errorf("error starting device discovery daemonset. %+v", err)
		}
	}

	serverVersion, err := o.context.Clientset.Discovery().ServerVersion()
	if err != nil {
		return fmt.Errorf("error getting server version. %+v", err)
	}

	signalChan := make(chan os.Signal, 1)
	stopChan := make(chan struct{})
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	// Run volume provisioner for each of the supported configurations
	for name, vendor := range provisionerConfigs {
		volumeProvisioner := provisioner.New(o.context, vendor)
		pc := controller.NewProvisionController(
			o.context.Clientset,
			name,
			volumeProvisioner,
			serverVersion.GitVersion,
		)
		go pc.Run(stopChan)
		logger.Infof("rook-provisioner %s started using %s flex vendor dir", name, vendor)
	}

	var namespaceToWatch string
	if os.Getenv("ROOK_CURRENT_NAMESPACE_ONLY") == "true" {
		logger.Infof("Watching the current namespace for a cluster CRD")
		namespaceToWatch = namespace
	} else {
		logger.Infof("Watching all namespaces for cluster CRDs")
		namespaceToWatch = v1.NamespaceAll
	}

	// watch for changes to the rook clusters
	o.clusterController.StartWatch(namespaceToWatch, stopChan)

	for {
		select {
		case <-signalChan:
			logger.Infof("shutdown signal received, exiting...")
			close(stopChan)
			o.clusterController.StopWatch()
			return nil
		}
	}
}

func (o *Operator) startSystemDaemons() error {
	if o.delayedDaemonsStarted {
		return nil
	}

	namespace := os.Getenv(k8sutil.PodNamespaceEnvVar)
	if namespace == "" {
		return fmt.Errorf("Rook operator namespace is not provided. Expose it via downward API in the rook operator manifest file using environment variable %s", k8sutil.PodNamespaceEnvVar)
	}

	if EnableFlexDriver {
		rookAgent := agent.New(o.context.Clientset)
		if err := rookAgent.Start(namespace, o.rookImage, o.securityAccount); err != nil {
			return fmt.Errorf("error starting agent daemonset: %v", err)
		}
	}

	serverVersion, err := o.context.Clientset.Discovery().ServerVersion()
	if err != nil {
		return fmt.Errorf("error getting server version: %v", err)
	}

	if !csi.CSIEnabled() {
		logger.Infof("CSI driver is not enabled")
		return nil
	}

	if serverVersion.Major < csi.KubeMinMajor || serverVersion.Major == csi.KubeMinMajor && serverVersion.Minor < csi.KubeMinMinor {
		logger.Infof("CSI driver is only supported in K8s 1.13 or newer. version=%s", serverVersion.String())
		return nil
	}

	if err = csi.ValidateCSIParam(); err != nil {
		return fmt.Errorf("invalid csi params: %v", err)
	}
	if err = csi.StartCSIDrivers(namespace, o.context.Clientset); err != nil {
		return fmt.Errorf("failed to start Ceph csi drivers: %v", err)
	}
	logger.Infof("successfully started Ceph CSI driver(s)")

	o.delayedDaemonsStarted = true
	return nil
}
<commit_msg>ceph csi: disable csi if server version is too low<commit_after>/*
Copyright 2016 The Rook 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 operator to manage Kubernetes storage.
package operator

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/coreos/pkg/capnslog"
	opkit "github.com/rook/operator-kit"
	"github.com/rook/rook/pkg/clusterd"
	"github.com/rook/rook/pkg/daemon/ceph/agent/flexvolume"
	"github.com/rook/rook/pkg/daemon/ceph/agent/flexvolume/attachment"
	"github.com/rook/rook/pkg/operator/ceph/agent"
	"github.com/rook/rook/pkg/operator/ceph/cluster"
	"github.com/rook/rook/pkg/operator/ceph/csi"
	"github.com/rook/rook/pkg/operator/ceph/file"
	"github.com/rook/rook/pkg/operator/ceph/object"
	objectuser "github.com/rook/rook/pkg/operator/ceph/object/user"
	"github.com/rook/rook/pkg/operator/ceph/pool"
	"github.com/rook/rook/pkg/operator/ceph/provisioner"
	"github.com/rook/rook/pkg/operator/discover"
	"github.com/rook/rook/pkg/operator/k8sutil"
	v1 "k8s.io/api/core/v1"
	"sigs.k8s.io/sig-storage-lib-external-provisioner/controller"
)

// volume provisioner constant
const (
	provisionerName       = "ceph.rook.io/block"
	provisionerNameLegacy = "rook.io/block"
)

var logger = capnslog.NewPackageLogger("github.com/rook/rook", "operator")

// The supported configurations for the volume provisioner
var provisionerConfigs = map[string]string{
	provisionerName:       flexvolume.FlexvolumeVendor,
	provisionerNameLegacy: flexvolume.FlexvolumeVendorLegacy,
}

var (
	// Whether to enable the flex driver. If true, the rook-ceph-agent daemonset will be started.
	EnableFlexDriver = true
	// Whether to enable the daemon for device discovery. If true, the rook-ceph-discover daemonset will be started.
	EnableDiscoveryDaemon = true
)

// Operator type for managing storage
type Operator struct {
	context         *clusterd.Context
	resources       []opkit.CustomResource
	rookImage       string
	securityAccount string
	// The custom resource that is global to the kubernetes cluster.
	// The cluster is global because you create multiple clusters in k8s
	clusterController     *cluster.ClusterController
	delayedDaemonsStarted bool
}

// New creates an operator instance
func New(context *clusterd.Context, volumeAttachmentWrapper attachment.Attachment, rookImage, securityAccount string) *Operator {
	schemes := []opkit.CustomResource{cluster.ClusterResource, pool.PoolResource, object.ObjectStoreResource, objectuser.ObjectStoreUserResource,
		file.FilesystemResource, attachment.VolumeResource}
	o := &Operator{
		context:         context,
		resources:       schemes,
		rookImage:       rookImage,
		securityAccount: securityAccount,
	}
	o.clusterController = cluster.NewClusterController(context, rookImage, volumeAttachmentWrapper, o.startSystemDaemons)
	return o
}

// Run the operator instance
func (o *Operator) Run() error {

	namespace := os.Getenv(k8sutil.PodNamespaceEnvVar)
	if namespace == "" {
		return fmt.Errorf("Rook operator namespace is not provided. Expose it via downward API in the rook operator manifest file using environment variable %s", k8sutil.PodNamespaceEnvVar)
	}

	if EnableDiscoveryDaemon {
		rookDiscover := discover.New(o.context.Clientset)
		if err := rookDiscover.Start(namespace, o.rookImage, o.securityAccount); err != nil {
			return fmt.Errorf("error starting device discovery daemonset. %+v", err)
		}
	}

	serverVersion, err := o.context.Clientset.Discovery().ServerVersion()
	if err != nil {
		return fmt.Errorf("error getting server version. %+v", err)
	}

	signalChan := make(chan os.Signal, 1)
	stopChan := make(chan struct{})
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	// Run volume provisioner for each of the supported configurations
	for name, vendor := range provisionerConfigs {
		volumeProvisioner := provisioner.New(o.context, vendor)
		pc := controller.NewProvisionController(
			o.context.Clientset,
			name,
			volumeProvisioner,
			serverVersion.GitVersion,
		)
		go pc.Run(stopChan)
		logger.Infof("rook-provisioner %s started using %s flex vendor dir", name, vendor)
	}

	var namespaceToWatch string
	if os.Getenv("ROOK_CURRENT_NAMESPACE_ONLY") == "true" {
		logger.Infof("Watching the current namespace for a cluster CRD")
		namespaceToWatch = namespace
	} else {
		logger.Infof("Watching all namespaces for cluster CRDs")
		namespaceToWatch = v1.NamespaceAll
	}

	// watch for changes to the rook clusters
	o.clusterController.StartWatch(namespaceToWatch, stopChan)

	for {
		select {
		case <-signalChan:
			logger.Infof("shutdown signal received, exiting...")
			close(stopChan)
			o.clusterController.StopWatch()
			return nil
		}
	}
}

func (o *Operator) startSystemDaemons() error {
	if o.delayedDaemonsStarted {
		return nil
	}

	namespace := os.Getenv(k8sutil.PodNamespaceEnvVar)
	if namespace == "" {
		return fmt.Errorf("Rook operator namespace is not provided. Expose it via downward API in the rook operator manifest file using environment variable %s", k8sutil.PodNamespaceEnvVar)
	}

	if EnableFlexDriver {
		rookAgent := agent.New(o.context.Clientset)
		if err := rookAgent.Start(namespace, o.rookImage, o.securityAccount); err != nil {
			return fmt.Errorf("error starting agent daemonset: %v", err)
		}
	}

	serverVersion, err := o.context.Clientset.Discovery().ServerVersion()
	if err != nil {
		return fmt.Errorf("error getting server version: %v", err)
	}

	if !csi.CSIEnabled() {
		logger.Infof("CSI driver is not enabled")
		return nil
	}

	if serverVersion.Major < csi.KubeMinMajor || serverVersion.Major == csi.KubeMinMajor && serverVersion.Minor < csi.KubeMinMinor {
		logger.Infof("CSI driver is only supported in K8s 1.13 or newer. version=%s", serverVersion.String())
		// disable csi control variables to disable other csi functions
		csi.EnableRBD = false
		csi.EnableCephFS = false
		return nil
	}

	if err = csi.ValidateCSIParam(); err != nil {
		return fmt.Errorf("invalid csi params: %v", err)
	}
	if err = csi.StartCSIDrivers(namespace, o.context.Clientset); err != nil {
		return fmt.Errorf("failed to start Ceph csi drivers: %v", err)
	}
	logger.Infof("successfully started Ceph CSI driver(s)")

	o.delayedDaemonsStarted = true
	return nil
}
<|endoftext|>


<commit_before>/*

For keeping a minimum running, perhaps when doing a routing table update, if destination hosts are all
 expired or about to expire we start more.

*/

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/iron-io/iron_go/cache"
	"github.com/iron-io/iron_go/worker"
	"github.com/iron-io/common"
	"github.com/iron-io/golog"
	"log"
	"math/rand"
	// "net"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
	"runtime"
	"flag"
//	"io/ioutil"
)

var config struct {
	Iron struct {
	Token      string `json:"token"`
	ProjectId  string `json:"project_id"`
} `json:"iron"`
	Logging       struct {
	To     string `json:"to"`
	Level  string `json:"level"`
	Prefix string `json:"prefix"`
}
}

//var routingTable = map[string]*Route{}
var icache = cache.New("routing-table")

func init() {

}

type Route struct {
	// TODO: Change destinations to a simple cache so it can expire entries after 55 minutes (the one we use in common?)
	Host         string `json:"host"`
	Destinations []string  `json:"destinations"`
	ProjectId    string  `json:"project_id"`
	Token        string  `json:"token"` // store this so we can queue up new workers on demand
	CodeName     string  `json:"code_name"`
}

// for adding new hosts
type Route2 struct {
	Host string `json:"host"`
	Dest string `json:"dest"`
}

func main() {

	var configFile string
	var env string
	flag.StringVar(&configFile, "c", "", "Config file name")
	// when this was e, it was erroring out.
	flag.StringVar(&env, "e", "development", "environment")

	flag.Parse() // Scans the arg list and sets up flags

	// Deployer is now passing -c in since we're using upstart and it doesn't know what directory to run in
	if configFile == "" {
		configFile = "config_" + env + ".json"
	}

	common.LoadConfig("iron_mq", configFile, &config)
	common.SetLogLevel(config.Logging.Level)
	common.SetLogLocation(config.Logging.To, config.Logging.Prefix)

	runtime.GOMAXPROCS(runtime.NumCPU())
	log.Println("Running on", runtime.NumCPU(), "CPUs")

	icache.Settings.UseConfigMap(map[string]interface{}{"token": config.Iron.Token, "project_id": config.Iron.ProjectId})

	r := mux.NewRouter()
	s := r.Headers("Iron-Router", "").Subrouter()
	s.HandleFunc("/", AddWorker)
	r.HandleFunc("/addworker", AddWorker)
	r.HandleFunc("/ping", Ping) // for health

	r.HandleFunc("/", ProxyFunc)

	http.Handle("/", r)
	port := 80
	golog.Infoln("Router started, listening and serving on port", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
}

func ProxyFunc(w http.ResponseWriter, req *http.Request) {
	golog.Infoln("HOST:", req.Host)
	host := strings.Split(req.Host, ":")[0]

	// We look up the destinations in the routing table and there can be 3 possible scenarios:
	// 1) This host was never registered so we return 404
	// 2) This host has active workers so we do the proxy
	// 3) This host has no active workers so we queue one (or more) up and return a 503 or something with message that says "try again in a minute"
	//	route := routingTable[host]
 	golog.Infoln("getting route for host:", host)
	route, err := getRoute(host)
	// choose random dest
	if err != nil {
		common.SendError(w, 400, fmt.Sprintln("Host not registered or error!", err))
		return
	}
	//	if route == nil { // route.Host == "" {
	//		common.SendError(w, 400, fmt.Sprintln(w, "Host not configured!"))
	//		return
	//	}
	dlen := len(route.Destinations)
	if dlen == 0 {
		golog.Infoln("No workers running, starting new task.")
		startNewWorker(route)
		common.SendError(w, 500, fmt.Sprintln("No workers running, starting them up..."))
		return
	}
	if dlen < 3 {
		golog.Infoln("Only one worker running, starting a new task.")
		startNewWorker(route)
	}
	serveEndpoint(w, req, route)
}

func serveEndpoint(w http.ResponseWriter, req *http.Request, route *Route) {
	dlen := len(route.Destinations)
	destIndex := rand.Intn(dlen)
	destUrlString := route.Destinations[destIndex]
	// todo: should check if http:// already exists.
	destUrlString2 := "http://" + destUrlString
	destUrl, err := url.Parse(destUrlString2)
	if err != nil {
		golog.Infoln("error!", err)
		panic(err)
	}
	golog.Infoln("proxying to", destUrl)
	proxy := NewSingleHostReverseProxy(destUrl)
	err = proxy.ServeHTTP(w, req)
	if err != nil {
		golog.Infoln("Error proxying!", err)
		etype := reflect.TypeOf(err)
		golog.Infoln("err type:", etype)
		w.WriteHeader(http.StatusInternalServerError)
		// can't figure out how to compare types so comparing strings.... lame.
		if strings.Contains(etype.String(), "net.OpError") { // == reflect.TypeOf(net.OpError{}) { // couldn't figure out a better way to do this
			golog.Infoln("It's a network error and less than three other workers available so we're going to remove it and start new task.")
			route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
			err := putRoute(route)
			if err != nil {
				golog.Infoln("couldn't update routing table:", err)
				common.SendError(w, 500, fmt.Sprintln("couldn't update routing table", err))
				return
			}
			golog.Infoln("New route:", route)
			if len(route.Destinations) < 3 {
				// always want at least three running
				startNewWorker(route)
			}
			serveEndpoint(w, req, route)
			return
		}
		return
	}
	golog.Infoln("Served!")
}

func startNewWorker(route *Route) (error) {
	golog.Infoln("Starting a new worker")
	// start new worker
	payload := map[string]interface{}{
		"token":      route.Token,
		"project_id": route.ProjectId,
		"code_name":  route.CodeName,
	}
	workerapi := worker.New()
	workerapi.Settings.UseConfigMap(payload)
	jsonPayload, err := json.Marshal(payload)
	if err != nil {
		golog.Infoln("Couldn't marshal json!", err)
		return err
	}
	timeout := time.Second * time.Duration(1800 + rand.Intn(600)) // a little random factor in here to spread out worker deaths
	task := worker.Task{
		CodeName: route.CodeName,
		Payload:  string(jsonPayload),
		Timeout:  &timeout, // let's have these die quickly while testing
	}
	tasks := make([]worker.Task, 1)
	tasks[0] = task
	taskIds, err := workerapi.TaskQueue(tasks...)
	golog.Infoln("Tasks queued.", taskIds)
	if err != nil {
		golog.Infoln("Couldn't queue up worker!", err)
		return err
	}
	return err
}

// When a worker starts up, it calls this
func AddWorker(w http.ResponseWriter, req *http.Request) {
	log.Println("AddWorker called!")

//	s, err := ioutil.ReadAll(req.Body)
//	fmt.Println("req.body:", err, string(s))

	// get project id and token
	projectId := req.FormValue("project_id")
	token := req.FormValue("token")
	codeName := req.FormValue("code_name")
	golog.Infoln("project_id:", projectId, "token:", token, "code_name:", codeName)

	// check header for what operation to perform
	routerHeader := req.Header.Get("Iron-Router")
	if routerHeader == "register" {
		route := Route{}
		if !common.ReadJSON(w, req, &route) {
			return
		}
		golog.Infoln("body read into route:", route)
		route.ProjectId = projectId
		route.Token = token
		route.CodeName = codeName
		// todo: do we need to close body?
		err := putRoute(&route)
		if err != nil {
			golog.Infoln("couldn't register host:", err)
			common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
			return
		}
		golog.Infoln("registered route:", route)
		fmt.Fprintln(w, "Host registered successfully.")

	} else {
		r2 := Route2{}
		if !common.ReadJSON(w, req, &r2) {
			return
		}
		// todo: do we need to close body?
		golog.Infoln("DECODED:", r2)
		route, err := getRoute(r2.Host)
		//		route := routingTable[r2.Host]
		if err != nil {
			common.SendError(w, 400, fmt.Sprintln("This host is not registered!", err))
			return
			//			route = &Route{}
		}
		golog.Infoln("ROUTE:", route)
		route.Destinations = append(route.Destinations, r2.Dest)
		golog.Infoln("ROUTE new:", route)
		err = putRoute(route)
		if err != nil {
			golog.Infoln("couldn't register host:", err)
			common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
			return
		}
		//		routingTable[r2.Host] = route
		//		fmt.Println("New routing table:", routingTable)
		fmt.Fprintln(w, "Worker added")
	}
}

func getRoute(host string) (*Route, error) {
	rx, err := icache.Get(host)
	if err != nil {
		return nil, err
	}
	rx2 := []byte(rx.(string))
	route := Route{}
	err = json.Unmarshal(rx2, &route)
	if err != nil {
		return nil, err
	}
	return &route, err
}

func putRoute(route *Route) (error) {
	item := cache.Item{}
	v, err := json.Marshal(route)
	if err != nil {
		return err
	}
	item.Value = string(v)
	err = icache.Put(route.Host, &item)
	return err
}

func Ping(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, "pong")
}
<commit_msg>Was writing a header when I shouldn't have.<commit_after>/*

For keeping a minimum running, perhaps when doing a routing table update, if destination hosts are all
 expired or about to expire we start more.

*/

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/iron-io/iron_go/cache"
	"github.com/iron-io/iron_go/worker"
	"github.com/iron-io/common"
	"github.com/iron-io/golog"
	"log"
	"math/rand"
	// "net"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
	"runtime"
	"flag"
//	"io/ioutil"
)

var config struct {
	Iron struct {
	Token      string `json:"token"`
	ProjectId  string `json:"project_id"`
} `json:"iron"`
	Logging       struct {
	To     string `json:"to"`
	Level  string `json:"level"`
	Prefix string `json:"prefix"`
}
}

//var routingTable = map[string]*Route{}
var icache = cache.New("routing-table")

func init() {

}

type Route struct {
	// TODO: Change destinations to a simple cache so it can expire entries after 55 minutes (the one we use in common?)
	Host         string `json:"host"`
	Destinations []string  `json:"destinations"`
	ProjectId    string  `json:"project_id"`
	Token        string  `json:"token"` // store this so we can queue up new workers on demand
	CodeName     string  `json:"code_name"`
}

// for adding new hosts
type Route2 struct {
	Host string `json:"host"`
	Dest string `json:"dest"`
}

func main() {

	var configFile string
	var env string
	flag.StringVar(&configFile, "c", "", "Config file name")
	// when this was e, it was erroring out.
	flag.StringVar(&env, "e", "development", "environment")

	flag.Parse() // Scans the arg list and sets up flags

	// Deployer is now passing -c in since we're using upstart and it doesn't know what directory to run in
	if configFile == "" {
		configFile = "config_" + env + ".json"
	}

	common.LoadConfig("iron_mq", configFile, &config)
	common.SetLogLevel(config.Logging.Level)
	common.SetLogLocation(config.Logging.To, config.Logging.Prefix)

	runtime.GOMAXPROCS(runtime.NumCPU())
	log.Println("Running on", runtime.NumCPU(), "CPUs")

	icache.Settings.UseConfigMap(map[string]interface{}{"token": config.Iron.Token, "project_id": config.Iron.ProjectId})

	r := mux.NewRouter()
	s := r.Headers("Iron-Router", "").Subrouter()
	s.HandleFunc("/", AddWorker)
	r.HandleFunc("/addworker", AddWorker)
	r.HandleFunc("/ping", Ping) // for health

	r.HandleFunc("/", ProxyFunc)

	http.Handle("/", r)
	port := 80
	golog.Infoln("Router started, listening and serving on port", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
}

func ProxyFunc(w http.ResponseWriter, req *http.Request) {
	golog.Infoln("HOST:", req.Host)
	host := strings.Split(req.Host, ":")[0]

	// We look up the destinations in the routing table and there can be 3 possible scenarios:
	// 1) This host was never registered so we return 404
	// 2) This host has active workers so we do the proxy
	// 3) This host has no active workers so we queue one (or more) up and return a 503 or something with message that says "try again in a minute"
	//	route := routingTable[host]
 	golog.Infoln("getting route for host:", host)
	route, err := getRoute(host)
	// choose random dest
	if err != nil {
		common.SendError(w, 400, fmt.Sprintln("Host not registered or error!", err))
		return
	}
	//	if route == nil { // route.Host == "" {
	//		common.SendError(w, 400, fmt.Sprintln(w, "Host not configured!"))
	//		return
	//	}
	dlen := len(route.Destinations)
	if dlen == 0 {
		golog.Infoln("No workers running, starting new task.")
		startNewWorker(route)
		common.SendError(w, 500, fmt.Sprintln("No workers running, starting them up..."))
		return
	}
	if dlen < 3 {
		golog.Infoln("Only one worker running, starting a new task.")
		startNewWorker(route)
	}
	serveEndpoint(w, req, route)
}

func serveEndpoint(w http.ResponseWriter, req *http.Request, route *Route) {
	dlen := len(route.Destinations)
	destIndex := rand.Intn(dlen)
	destUrlString := route.Destinations[destIndex]
	// todo: should check if http:// already exists.
	destUrlString2 := "http://" + destUrlString
	destUrl, err := url.Parse(destUrlString2)
	if err != nil {
		golog.Infoln("error!", err)
		panic(err)
	}
	golog.Infoln("proxying to", destUrl)
	proxy := NewSingleHostReverseProxy(destUrl)
	err = proxy.ServeHTTP(w, req)
	if err != nil {
		golog.Infoln("Error proxying!", err)
		etype := reflect.TypeOf(err)
		golog.Infoln("err type:", etype)
		// can't figure out how to compare types so comparing strings.... lame.
		if strings.Contains(etype.String(), "net.OpError") { // == reflect.TypeOf(net.OpError{}) { // couldn't figure out a better way to do this
			golog.Infoln("It's a network error so we're going to remove destination.")
			route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
			err := putRoute(route)
			if err != nil {
				golog.Infoln("Couldn't update routing table:", err)
				common.SendError(w, 500, fmt.Sprintln("couldn't update routing table", err))
				return
			}
			golog.Infoln("New route:", route)
			if len(route.Destinations) < 3 {
				golog.Infoln("After network error, there are less than three destinations, so starting a new one. ")
				// always want at least three running
				startNewWorker(route)
			}
			serveEndpoint(w, req, route)
			return
		}
		common.SendError(w, 500, fmt.Sprintln("Internal error occurred:", err))
		return
	}
	golog.Infoln("Served!")
}

func startNewWorker(route *Route) (error) {
	golog.Infoln("Starting a new worker")
	// start new worker
	payload := map[string]interface{}{
		"token":      route.Token,
		"project_id": route.ProjectId,
		"code_name":  route.CodeName,
	}
	workerapi := worker.New()
	workerapi.Settings.UseConfigMap(payload)
	jsonPayload, err := json.Marshal(payload)
	if err != nil {
		golog.Infoln("Couldn't marshal json!", err)
		return err
	}
	timeout := time.Second * time.Duration(1800 + rand.Intn(600)) // a little random factor in here to spread out worker deaths
	task := worker.Task{
		CodeName: route.CodeName,
		Payload:  string(jsonPayload),
		Timeout:  &timeout, // let's have these die quickly while testing
	}
	tasks := make([]worker.Task, 1)
	tasks[0] = task
	taskIds, err := workerapi.TaskQueue(tasks...)
	golog.Infoln("Tasks queued.", taskIds)
	if err != nil {
		golog.Infoln("Couldn't queue up worker!", err)
		return err
	}
	return err
}

// When a worker starts up, it calls this
func AddWorker(w http.ResponseWriter, req *http.Request) {
	log.Println("AddWorker called!")

//	s, err := ioutil.ReadAll(req.Body)
//	fmt.Println("req.body:", err, string(s))

	// get project id and token
	projectId := req.FormValue("project_id")
	token := req.FormValue("token")
	codeName := req.FormValue("code_name")
	golog.Infoln("project_id:", projectId, "token:", token, "code_name:", codeName)

	// check header for what operation to perform
	routerHeader := req.Header.Get("Iron-Router")
	if routerHeader == "register" {
		route := Route{}
		if !common.ReadJSON(w, req, &route) {
			return
		}
		golog.Infoln("body read into route:", route)
		route.ProjectId = projectId
		route.Token = token
		route.CodeName = codeName
		// todo: do we need to close body?
		err := putRoute(&route)
		if err != nil {
			golog.Infoln("couldn't register host:", err)
			common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
			return
		}
		golog.Infoln("registered route:", route)
		fmt.Fprintln(w, "Host registered successfully.")

	} else {
		r2 := Route2{}
		if !common.ReadJSON(w, req, &r2) {
			return
		}
		// todo: do we need to close body?
		golog.Infoln("DECODED:", r2)
		route, err := getRoute(r2.Host)
		//		route := routingTable[r2.Host]
		if err != nil {
			common.SendError(w, 400, fmt.Sprintln("This host is not registered!", err))
			return
			//			route = &Route{}
		}
		golog.Infoln("ROUTE:", route)
		route.Destinations = append(route.Destinations, r2.Dest)
		golog.Infoln("ROUTE new:", route)
		err = putRoute(route)
		if err != nil {
			golog.Infoln("couldn't register host:", err)
			common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
			return
		}
		//		routingTable[r2.Host] = route
		//		fmt.Println("New routing table:", routingTable)
		fmt.Fprintln(w, "Worker added")
	}
}

func getRoute(host string) (*Route, error) {
	rx, err := icache.Get(host)
	if err != nil {
		return nil, err
	}
	rx2 := []byte(rx.(string))
	route := Route{}
	err = json.Unmarshal(rx2, &route)
	if err != nil {
		return nil, err
	}
	return &route, err
}

func putRoute(route *Route) (error) {
	item := cache.Item{}
	v, err := json.Marshal(route)
	if err != nil {
		return err
	}
	item.Value = string(v)
	err = icache.Put(route.Host, &item)
	return err
}

func Ping(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, "pong")
}
<|endoftext|>