<commit_before>package kluster

import (
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	securitygroups "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
	"github.com/gophercloud/gophercloud/pagination"
	"k8s.io/client-go/tools/cache"

	"github.com/sapcc/kubernikus/pkg/api/models"
	"github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
	"github.com/sapcc/kubernikus/pkg/client/openstack/compute"
	"github.com/sapcc/kubernikus/pkg/util/generator"
)

type KlusterClient interface {
	CreateNode(*models.NodePool, string, []byte) (string, error)
	DeleteNode(string) error
	ListNodes(*models.NodePool) ([]Node, error)
	SetSecurityGroup(nodeID string) error
	EnsureKubernikusRuleInSecurityGroup() (bool, error)
	EnsureServerGroup(name string) (string, error)
	DeleteServerGroup(name string) error
}

type klusterClient struct {
	NetworkClient  *gophercloud.ServiceClient
	ComputeClient  *gophercloud.ServiceClient
	IdentityClient *gophercloud.ServiceClient

	Kluster   *v1.Kluster
	NodeStore cache.Store
}

type cachedNodesEntry struct {
	Kluster *v1.Kluster
	Pool    *models.NodePool
	Nodes   []Node
}

func CachedNodesKeyFunc(obj interface{}) (string, error) {
	entry, ok := obj.(cachedNodesEntry)
	if !ok {
		return "", fmt.Errorf("unexpected object in cache")
	}
	return fmt.Sprintf("%v-%v", entry.Kluster.Spec.Name, entry.Pool.Name), nil
}

func NewKlusterClient(network, compute, identity *gophercloud.ServiceClient, kluster *v1.Kluster) KlusterClient {
	var client KlusterClient
	client = &klusterClient{
		NetworkClient:  network,
		ComputeClient:  compute,
		IdentityClient: identity,
		Kluster:        kluster,
		NodeStore:      cache.NewTTLStore(CachedNodesKeyFunc, 1*time.Minute),
	}

	return client
}

func (c *klusterClient) CreateNode(pool *models.NodePool, name string, userData []byte) (string, error) {
	configDrive := true

	networks := []servers.Network{{UUID: c.Kluster.Spec.Openstack.NetworkID}}

	if strings.HasPrefix(pool.Flavor, "zh") {
		networks = []servers.Network{
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
		}
	}

	if strings.HasPrefix(pool.Flavor, "zg") {
		networks = []servers.Network{
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
		}
	}

	var createOpts servers.CreateOptsBuilder = servers.CreateOpts{
		Name:           name,
		FlavorName:     pool.Flavor,
		ImageName:      pool.Image,
		Networks:       networks,
		UserData:       userData,
		ServiceClient:  c.ComputeClient,
		SecurityGroups: []string{c.Kluster.Spec.Openstack.SecurityGroupName},
		ConfigDrive:    &configDrive,
	}

	if os.Getenv("NODEPOOL_AFFINITY") != "" {
		serverGroupID, err := c.EnsureServerGroup(c.Kluster.Name + "/" + pool.Name)
		if err != nil {
			return "", err
		}

		createOpts = schedulerhints.CreateOptsExt{
			CreateOptsBuilder: createOpts,
			SchedulerHints:    schedulerhints.SchedulerHints{Group: serverGroupID},
		}
	}

	server, err := compute.Create(c.ComputeClient, createOpts).Extract()

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

	return server.ID, nil
}

func (c *klusterClient) DeleteNode(id string) (err error) {
	err = servers.Delete(c.ComputeClient, id).ExtractErr()
	if err != nil {
		return err
	}

	return nil
}

func (c *klusterClient) ListNodes(pool *models.NodePool) ([]Node, error) {
	var unfilteredNodes []Node
	var filteredNodes []Node
	var err error

	prefix := fmt.Sprintf("%v-%v-", c.Kluster.Spec.Name, pool.Name)
	err = servers.List(c.ComputeClient, servers.ListOpts{Name: prefix}).EachPage(func(page pagination.Page) (bool, error) {
		unfilteredNodes, err = ExtractServers(page)
		if err != nil {
			return false, err
		}
		return true, nil
	})
	if err != nil {
		return nil, err
	}

	//filter nodeList https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
	//we only keep nodes whose where the name length is matched the expected length of a name for this pool
	//otherwise we would be returning nodes from other nodepools here if the current pool name is a prefix of other pools
	filteredNodes = unfilteredNodes[:0]
	for _, node := range unfilteredNodes {
		if len(node.GetName()) == len(prefix)+generator.RandomLength {
			filteredNodes = append(filteredNodes, node)

		}
	}

	return filteredNodes, nil
}

func (c *klusterClient) SetSecurityGroup(nodeID string) (err error) {
	return secgroups.AddServer(c.ComputeClient, nodeID, c.Kluster.Spec.Openstack.SecurityGroupName).ExtractErr()
}

func (c *klusterClient) EnsureKubernikusRuleInSecurityGroup() (created bool, err error) {
	page, err := securitygroups.List(c.NetworkClient, securitygroups.ListOpts{Name: c.Kluster.Spec.Openstack.SecurityGroupName}).AllPages()
	if err != nil {
		return false, fmt.Errorf("SecurityGroup %v not found: %s", c.Kluster.Spec.Openstack.SecurityGroupName, err)
	}

	groups, err := securitygroups.ExtractGroups(page)
	if err != nil {
		return false, err
	}

	if len(groups) != 1 {
		return false, fmt.Errorf("More than one SecurityGroup with name %v found", c.Kluster.Spec.Openstack.SecurityGroupName)
	}

	udp := false
	tcp := false
	icmp := false
	for _, rule := range groups[0].Rules {
		if rule.Direction != string(rules.DirIngress) {
			continue
		}

		if rule.EtherType != string(rules.EtherType4) {
			continue
		}

		if rule.RemoteIPPrefix != c.Kluster.Spec.ClusterCIDR {
			continue
		}

		if rule.Protocol == string(rules.ProtocolICMP) {
			icmp = true
			continue
		}

		if rule.Protocol == string(rules.ProtocolUDP) {
			udp = true
			continue
		}

		if rule.Protocol == string(rules.ProtocolTCP) {
			tcp = true
			continue
		}

		if icmp && udp && tcp {
			break
		}
	}

	opts := rules.CreateOpts{
		Direction:      rules.DirIngress,
		EtherType:      rules.EtherType4,
		SecGroupID:     groups[0].ID,
		RemoteIPPrefix: c.Kluster.Spec.ClusterCIDR,
	}

	if !udp {
		opts.Protocol = rules.ProtocolUDP
		_, err := rules.Create(c.NetworkClient, opts).Extract()
		if err != nil {
			return false, err
		}
	}

	if !tcp {
		opts.Protocol = rules.ProtocolTCP
		_, err := rules.Create(c.NetworkClient, opts).Extract()
		if err != nil {
			return false, err
		}
	}

	if !icmp {
		opts.Protocol = rules.ProtocolICMP
		_, err := rules.Create(c.NetworkClient, opts).Extract()
		if err != nil {
			return false, err
		}
	}

	return !udp || !tcp || !icmp, nil
}

func (c *klusterClient) EnsureServerGroup(name string) (id string, err error) {
	sg, err := c.serverGroupByName(name)
	if err != nil {
		return "", err
	}
	if sg != nil {
		return sg.ID, nil
	}
	sg, err = servergroups.Create(c.ComputeClient, servergroups.CreateOpts{
		Name:     name,
		Policies: []string{"soft-affinity"},
	}).Extract()
	if err != nil {
		return "", err
	}
	return sg.ID, nil
}

func (c *klusterClient) DeleteServerGroup(name string) error {
	sg, err := c.serverGroupByName(name)
	if err != nil {
		return err
	}
	if sg != nil {
		return servergroups.Delete(c.ComputeClient, sg.ID).ExtractErr()
	}
	return nil
}

func (c *klusterClient) serverGroupByName(name string) (*servergroups.ServerGroup, error) {
	page, err := servergroups.List(c.ComputeClient).AllPages()
	if err != nil {
		return nil, err
	}
	sgs, err := servergroups.ExtractServerGroups(page)
	if err != nil {
		return nil, err
	}
	for _, sg := range sgs {
		if sg.Name == name {
			return &sg, nil
		}
	}
	return nil, nil
}

func ExtractServers(r pagination.Page) ([]Node, error) {
	var s []Node
	err := servers.ExtractServersInto(r, &s)
	return s, err
}
<commit_msg>Fix ListNodes when nodes are already deleted<commit_after>package kluster

import (
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	securitygroups "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
	"github.com/gophercloud/gophercloud/pagination"
	"k8s.io/client-go/tools/cache"

	"github.com/sapcc/kubernikus/pkg/api/models"
	"github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
	"github.com/sapcc/kubernikus/pkg/client/openstack/compute"
	"github.com/sapcc/kubernikus/pkg/util/generator"
)

type KlusterClient interface {
	CreateNode(*models.NodePool, string, []byte) (string, error)
	DeleteNode(string) error
	ListNodes(*models.NodePool) ([]Node, error)
	SetSecurityGroup(nodeID string) error
	EnsureKubernikusRuleInSecurityGroup() (bool, error)
	EnsureServerGroup(name string) (string, error)
	DeleteServerGroup(name string) error
}

type klusterClient struct {
	NetworkClient  *gophercloud.ServiceClient
	ComputeClient  *gophercloud.ServiceClient
	IdentityClient *gophercloud.ServiceClient

	Kluster   *v1.Kluster
	NodeStore cache.Store
}

type cachedNodesEntry struct {
	Kluster *v1.Kluster
	Pool    *models.NodePool
	Nodes   []Node
}

func CachedNodesKeyFunc(obj interface{}) (string, error) {
	entry, ok := obj.(cachedNodesEntry)
	if !ok {
		return "", fmt.Errorf("unexpected object in cache")
	}
	return fmt.Sprintf("%v-%v", entry.Kluster.Spec.Name, entry.Pool.Name), nil
}

func NewKlusterClient(network, compute, identity *gophercloud.ServiceClient, kluster *v1.Kluster) KlusterClient {
	var client KlusterClient
	client = &klusterClient{
		NetworkClient:  network,
		ComputeClient:  compute,
		IdentityClient: identity,
		Kluster:        kluster,
		NodeStore:      cache.NewTTLStore(CachedNodesKeyFunc, 1*time.Minute),
	}

	return client
}

func (c *klusterClient) CreateNode(pool *models.NodePool, name string, userData []byte) (string, error) {
	configDrive := true

	networks := []servers.Network{{UUID: c.Kluster.Spec.Openstack.NetworkID}}

	if strings.HasPrefix(pool.Flavor, "zh") {
		networks = []servers.Network{
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
		}
	}

	if strings.HasPrefix(pool.Flavor, "zg") {
		networks = []servers.Network{
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
			{UUID: c.Kluster.Spec.Openstack.NetworkID},
		}
	}

	var createOpts servers.CreateOptsBuilder = servers.CreateOpts{
		Name:           name,
		FlavorName:     pool.Flavor,
		ImageName:      pool.Image,
		Networks:       networks,
		UserData:       userData,
		ServiceClient:  c.ComputeClient,
		SecurityGroups: []string{c.Kluster.Spec.Openstack.SecurityGroupName},
		ConfigDrive:    &configDrive,
	}

	if os.Getenv("NODEPOOL_AFFINITY") != "" {
		serverGroupID, err := c.EnsureServerGroup(c.Kluster.Name + "/" + pool.Name)
		if err != nil {
			return "", err
		}

		createOpts = schedulerhints.CreateOptsExt{
			CreateOptsBuilder: createOpts,
			SchedulerHints:    schedulerhints.SchedulerHints{Group: serverGroupID},
		}
	}

	server, err := compute.Create(c.ComputeClient, createOpts).Extract()

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

	return server.ID, nil
}

func (c *klusterClient) DeleteNode(id string) (err error) {
	err = servers.Delete(c.ComputeClient, id).ExtractErr()
	if err != nil {
		return err
	}

	return nil
}

func (c *klusterClient) ListNodes(pool *models.NodePool) ([]Node, error) {
	var unfilteredNodes []Node
	var filteredNodes []Node
	var err error

	prefix := fmt.Sprintf("%v-%v-", c.Kluster.Spec.Name, pool.Name)
	err = servers.List(c.ComputeClient, servers.ListOpts{Name: prefix}).EachPage(func(page pagination.Page) (bool, error) {
		if page != nil {
			unfilteredNodes, err = ExtractServers(page)
			if err != nil {
				return false, err
			}
		}
		return true, nil
	})
	if err != nil {
		return nil, err
	}

	//filter nodeList https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
	//we only keep nodes whose where the name length is matched the expected length of a name for this pool
	//otherwise we would be returning nodes from other nodepools here if the current pool name is a prefix of other pools
	filteredNodes = unfilteredNodes[:0]
	for _, node := range unfilteredNodes {
		if len(node.GetName()) == len(prefix)+generator.RandomLength {
			filteredNodes = append(filteredNodes, node)

		}
	}

	return filteredNodes, nil
}

func (c *klusterClient) SetSecurityGroup(nodeID string) (err error) {
	return secgroups.AddServer(c.ComputeClient, nodeID, c.Kluster.Spec.Openstack.SecurityGroupName).ExtractErr()
}

func (c *klusterClient) EnsureKubernikusRuleInSecurityGroup() (created bool, err error) {
	page, err := securitygroups.List(c.NetworkClient, securitygroups.ListOpts{Name: c.Kluster.Spec.Openstack.SecurityGroupName}).AllPages()
	if err != nil {
		return false, fmt.Errorf("SecurityGroup %v not found: %s", c.Kluster.Spec.Openstack.SecurityGroupName, err)
	}

	groups, err := securitygroups.ExtractGroups(page)
	if err != nil {
		return false, err
	}

	if len(groups) != 1 {
		return false, fmt.Errorf("More than one SecurityGroup with name %v found", c.Kluster.Spec.Openstack.SecurityGroupName)
	}

	udp := false
	tcp := false
	icmp := false
	for _, rule := range groups[0].Rules {
		if rule.Direction != string(rules.DirIngress) {
			continue
		}

		if rule.EtherType != string(rules.EtherType4) {
			continue
		}

		if rule.RemoteIPPrefix != c.Kluster.Spec.ClusterCIDR {
			continue
		}

		if rule.Protocol == string(rules.ProtocolICMP) {
			icmp = true
			continue
		}

		if rule.Protocol == string(rules.ProtocolUDP) {
			udp = true
			continue
		}

		if rule.Protocol == string(rules.ProtocolTCP) {
			tcp = true
			continue
		}

		if icmp && udp && tcp {
			break
		}
	}

	opts := rules.CreateOpts{
		Direction:      rules.DirIngress,
		EtherType:      rules.EtherType4,
		SecGroupID:     groups[0].ID,
		RemoteIPPrefix: c.Kluster.Spec.ClusterCIDR,
	}

	if !udp {
		opts.Protocol = rules.ProtocolUDP
		_, err := rules.Create(c.NetworkClient, opts).Extract()
		if err != nil {
			return false, err
		}
	}

	if !tcp {
		opts.Protocol = rules.ProtocolTCP
		_, err := rules.Create(c.NetworkClient, opts).Extract()
		if err != nil {
			return false, err
		}
	}

	if !icmp {
		opts.Protocol = rules.ProtocolICMP
		_, err := rules.Create(c.NetworkClient, opts).Extract()
		if err != nil {
			return false, err
		}
	}

	return !udp || !tcp || !icmp, nil
}

func (c *klusterClient) EnsureServerGroup(name string) (id string, err error) {
	sg, err := c.serverGroupByName(name)
	if err != nil {
		return "", err
	}
	if sg != nil {
		return sg.ID, nil
	}
	sg, err = servergroups.Create(c.ComputeClient, servergroups.CreateOpts{
		Name:     name,
		Policies: []string{"soft-affinity"},
	}).Extract()
	if err != nil {
		return "", err
	}
	return sg.ID, nil
}

func (c *klusterClient) DeleteServerGroup(name string) error {
	sg, err := c.serverGroupByName(name)
	if err != nil {
		return err
	}
	if sg != nil {
		return servergroups.Delete(c.ComputeClient, sg.ID).ExtractErr()
	}
	return nil
}

func (c *klusterClient) serverGroupByName(name string) (*servergroups.ServerGroup, error) {
	page, err := servergroups.List(c.ComputeClient).AllPages()
	if err != nil {
		return nil, err
	}
	sgs, err := servergroups.ExtractServerGroups(page)
	if err != nil {
		return nil, err
	}
	for _, sg := range sgs {
		if sg.Name == name {
			return &sg, nil
		}
	}
	return nil, nil
}

func ExtractServers(r pagination.Page) ([]Node, error) {
	var s []Node
	err := servers.ExtractServersInto(r, &s)
	return s, err
}
<|endoftext|>


<commit_before>package template

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/skygeario/skygear-server/pkg/core/errors"
)

type AssetGearLoader struct {
	AssetGearEndpoint  string
	AssetGearMasterKey string
}

func (l *AssetGearLoader) Load(u *url.URL) (templateContent string, err error) {
	// The url is asset-gear:///assetname
	path := u.Path
	assetName := strings.TrimPrefix(path, "/")
	reqBody := map[string]interface{}{
		"assets": []interface{}{
			map[string]interface{}{
				"asset_name": assetName,
			},
		},
	}
	body, err := json.Marshal(reqBody)
	if err != nil {
		err = errors.HandledWithMessage(err, "failed to encode body")
		return
	}

	signReq, err := http.NewRequest("POST", fmt.Sprintf("%s/_asset/get_signed_url", l.AssetGearEndpoint), bytes.NewReader(body))
	if err != nil {
		err = errors.HandledWithMessage(err, "failed to create sign request")
		return
	}
	signReq.Header.Set("x-skygear-api-key", l.AssetGearMasterKey)
	signReq.Header.Set("content-type", "application/json")

	resp, err := http.DefaultClient.Do(signReq)
	if err != nil {
		err = errors.HandledWithMessage(err, "failed to get signed template URL")
		return
	}
	defer resp.Body.Close()

	respBody := map[string]interface{}{}
	err = json.NewDecoder(resp.Body).Decode(&respBody)
	if err != nil {
		err = errors.HandledWithMessage(err, "unexpected response body")
		return
	}

	signedURL, ok := respBody["result"].(map[string]interface{})["assets"].([]interface{})[0].(map[string]interface{})["url"].(string)
	if !ok {
		err = errors.New("failed to get signed template URL")
		return
	}

	return DownloadStringFromAssuminglyTrustedURL(signedURL)
}
<commit_msg>Use local struct instead of json types<commit_after>package template

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/skygeario/skygear-server/pkg/core/errors"
)

type AssetGearLoader struct {
	AssetGearEndpoint  string
	AssetGearMasterKey string
}

type signAssetRequest struct {
	Assets []signAssetItem `json:"assets,omitempty"`
}

type signAssetItem struct {
	AssetName string `json:"asset_name,omitempty"`
	URL       string `json:"url,omitempty"`
}

type responseBody struct {
	Result signAssetRequest `json:"result,omitempty"`
}

func (l *AssetGearLoader) Load(u *url.URL) (templateContent string, err error) {
	// The url is asset-gear:///assetname
	path := u.Path
	assetName := strings.TrimPrefix(path, "/")
	reqBody := signAssetRequest{
		Assets: []signAssetItem{
			signAssetItem{
				AssetName: assetName,
			},
		},
	}
	body, err := json.Marshal(reqBody)
	if err != nil {
		err = errors.HandledWithMessage(err, "failed to encode body")
		return
	}

	signReq, err := http.NewRequest("POST", fmt.Sprintf("%s/_asset/get_signed_url", l.AssetGearEndpoint), bytes.NewReader(body))
	if err != nil {
		err = errors.HandledWithMessage(err, "failed to create sign request")
		return
	}
	signReq.Header.Set("x-skygear-api-key", l.AssetGearMasterKey)
	signReq.Header.Set("content-type", "application/json")

	resp, err := http.DefaultClient.Do(signReq)
	if err != nil {
		err = errors.HandledWithMessage(err, "failed to get signed template URL")
		return
	}
	defer resp.Body.Close()

	var respBody responseBody
	err = json.NewDecoder(resp.Body).Decode(&respBody)
	if err != nil {
		err = errors.HandledWithMessage(err, "unexpected response body")
		return
	}

	if len(respBody.Result.Assets) <= 0 {
		err = errors.New("failed to get signed template URL")
		return
	}
	signedURL := respBody.Result.Assets[0].URL
	if signedURL == "" {
		err = errors.New("failed to get signed template URL")
		return
	}

	return DownloadStringFromAssuminglyTrustedURL(signedURL)
}
<|endoftext|>


<commit_before>// +build !providerless

/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package azure_dd

import (
	"errors"
	"fmt"
	"strconv"
	"strings"

	"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	volumehelpers "k8s.io/cloud-provider/volume/helpers"
	"k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/volume/util"
	"k8s.io/legacy-cloud-providers/azure"
)

type azureDiskProvisioner struct {
	plugin  *azureDataDiskPlugin
	options volume.VolumeOptions
}

type azureDiskDeleter struct {
	*dataDisk
	spec   *volume.Spec
	plugin *azureDataDiskPlugin
}

var _ volume.Provisioner = &azureDiskProvisioner{}
var _ volume.Deleter = &azureDiskDeleter{}

func (d *azureDiskDeleter) GetPath() string {
	return getPath(d.podUID, d.dataDisk.diskName, d.plugin.host)
}

func (d *azureDiskDeleter) Delete() error {
	volumeSource, _, err := getVolumeSource(d.spec)
	if err != nil {
		return err
	}

	diskController, err := getDiskController(d.plugin.host)
	if err != nil {
		return err
	}

	managed := (*volumeSource.Kind == v1.AzureManagedDisk)

	if managed {
		return diskController.DeleteManagedDisk(volumeSource.DataDiskURI)
	}

	return diskController.DeleteBlobDisk(volumeSource.DataDiskURI)
}

// parseZoned parsed 'zoned' for storage class. If zoned is not specified (empty string),
// then it defaults to true for managed disks.
func parseZoned(zonedString string, kind v1.AzureDataDiskKind) (bool, error) {
	if zonedString == "" {
		return kind == v1.AzureManagedDisk, nil
	}

	zoned, err := strconv.ParseBool(zonedString)
	if err != nil {
		return false, fmt.Errorf("failed to parse 'zoned': %v", err)
	}

	if zoned && kind != v1.AzureManagedDisk {
		return false, fmt.Errorf("zoned is only supported by managed disks")
	}

	return zoned, nil
}

func (p *azureDiskProvisioner) Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error) {
	if !util.AccessModesContainedInAll(p.plugin.GetAccessModes(), p.options.PVC.Spec.AccessModes) {
		return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", p.options.PVC.Spec.AccessModes, p.plugin.GetAccessModes())
	}
	supportedModes := p.plugin.GetAccessModes()

	// perform static validation first
	if p.options.PVC.Spec.Selector != nil {
		return nil, fmt.Errorf("azureDisk - claim.Spec.Selector is not supported for dynamic provisioning on Azure disk")
	}

	if len(p.options.PVC.Spec.AccessModes) > 1 {
		return nil, fmt.Errorf("AzureDisk - multiple access modes are not supported on AzureDisk plugin")
	}

	if len(p.options.PVC.Spec.AccessModes) == 1 {
		if p.options.PVC.Spec.AccessModes[0] != supportedModes[0] {
			return nil, fmt.Errorf("AzureDisk - mode %s is not supported by AzureDisk plugin (supported mode is %s)", p.options.PVC.Spec.AccessModes[0], supportedModes)
		}
	}

	var (
		location, account          string
		storageAccountType, fsType string
		cachingMode                v1.AzureDataDiskCachingMode
		strKind                    string
		err                        error
		resourceGroup              string

		zoned                    bool
		zonePresent              bool
		zonesPresent             bool
		strZoned                 string
		availabilityZone         string
		availabilityZones        sets.String
		selectedAvailabilityZone string
		writeAcceleratorEnabled  string

		diskIopsReadWrite   string
		diskMbpsReadWrite   string
		diskEncryptionSetID string

		maxShares int
	)
	// maxLength = 79 - (4 for ".vhd") = 75
	name := util.GenerateVolumeName(p.options.ClusterName, p.options.PVName, 75)
	capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
	requestGiB, err := volumehelpers.RoundUpToGiBInt(capacity)
	if err != nil {
		return nil, err
	}

	for k, v := range p.options.Parameters {
		switch strings.ToLower(k) {
		case "skuname":
			storageAccountType = v
		case "location":
			location = v
		case "storageaccount":
			account = v
		case "storageaccounttype":
			storageAccountType = v
		case "kind":
			strKind = v
		case "cachingmode":
			cachingMode = v1.AzureDataDiskCachingMode(v)
		case volume.VolumeParameterFSType:
			fsType = strings.ToLower(v)
		case "resourcegroup":
			resourceGroup = v
		case "zone":
			zonePresent = true
			availabilityZone = v
		case "zones":
			zonesPresent = true
			availabilityZones, err = volumehelpers.ZonesToSet(v)
			if err != nil {
				return nil, fmt.Errorf("error parsing zones %s, must be strings separated by commas: %v", v, err)
			}
		case "zoned":
			strZoned = v
		case "diskiopsreadwrite":
			diskIopsReadWrite = v
		case "diskmbpsreadwrite":
			diskMbpsReadWrite = v
		case "diskencryptionsetid":
			diskEncryptionSetID = v
		case azure.WriteAcceleratorEnabled:
			writeAcceleratorEnabled = v
		case "maxshares":
			maxShares, err = strconv.Atoi(v)
			if err != nil {
				return nil, fmt.Errorf("parse %s failed with error: %v", v, err)
			}
			if maxShares < 1 {
				return nil, fmt.Errorf("parse %s returned with invalid value: %d", v, maxShares)
			}
		default:
			return nil, fmt.Errorf("AzureDisk - invalid option %s in storage class", k)
		}
	}

	// normalize values
	skuName, err := normalizeStorageAccountType(storageAccountType)
	if err != nil {
		return nil, err
	}

	kind, err := normalizeKind(strFirstLetterToUpper(strKind))
	if err != nil {
		return nil, err
	}

	zoned, err = parseZoned(strZoned, kind)
	if err != nil {
		return nil, err
	}

	if kind != v1.AzureManagedDisk {
		if resourceGroup != "" {
			return nil, errors.New("StorageClass option 'resourceGroup' can be used only for managed disks")
		}

		if zoned {
			return nil, errors.New("StorageClass option 'zoned' parameter is only supported for managed disks")
		}
	}

	if !zoned && (zonePresent || zonesPresent || len(allowedTopologies) > 0) {
		return nil, fmt.Errorf("zone, zones and allowedTopologies StorageClass parameters must be used together with zoned parameter")
	}

	if cachingMode, err = normalizeCachingMode(cachingMode); err != nil {
		return nil, err
	}

	diskController, err := getDiskController(p.plugin.host)
	if err != nil {
		return nil, err
	}

	// Select zone for managed disks based on zone, zones and allowedTopologies.
	if zoned {
		activeZones, err := diskController.GetActiveZones()
		if err != nil {
			return nil, fmt.Errorf("error querying active zones: %v", err)
		}

		if availabilityZone != "" || availabilityZones.Len() != 0 || activeZones.Len() != 0 || len(allowedTopologies) != 0 {
			selectedAvailabilityZone, err = volumehelpers.SelectZoneForVolume(zonePresent, zonesPresent, availabilityZone, availabilityZones, activeZones, selectedNode, allowedTopologies, p.options.PVC.Name)
			if err != nil {
				return nil, err
			}
		}
	}

	// create disk
	diskURI := ""
	labels := map[string]string{}
	if kind == v1.AzureManagedDisk {
		tags := make(map[string]string)
		if p.options.CloudTags != nil {
			tags = *(p.options.CloudTags)
		}
		if strings.EqualFold(writeAcceleratorEnabled, "true") {
			tags[azure.WriteAcceleratorEnabled] = "true"
		}

		volumeOptions := &azure.ManagedDiskOptions{
			DiskName:            name,
			StorageAccountType:  skuName,
			ResourceGroup:       resourceGroup,
			PVCName:             p.options.PVC.Name,
			SizeGB:              requestGiB,
			Tags:                tags,
			AvailabilityZone:    selectedAvailabilityZone,
			DiskIOPSReadWrite:   diskIopsReadWrite,
			DiskMBpsReadWrite:   diskMbpsReadWrite,
			DiskEncryptionSetID: diskEncryptionSetID,
			MaxShares:           int32(maxShares),
		}
		diskURI, err = diskController.CreateManagedDisk(volumeOptions)
		if err != nil {
			return nil, err
		}
		labels, err = diskController.GetAzureDiskLabels(diskURI)
		if err != nil {
			return nil, err
		}
	} else {
		if kind == v1.AzureDedicatedBlobDisk {
			_, diskURI, _, err = diskController.CreateVolume(name, account, storageAccountType, location, requestGiB)
			if err != nil {
				return nil, err
			}
		} else {
			diskURI, err = diskController.CreateBlobDisk(name, storage.SkuName(skuName), requestGiB)
			if err != nil {
				return nil, err
			}
		}
	}

	volumeMode := p.options.PVC.Spec.VolumeMode
	if volumeMode != nil && *volumeMode == v1.PersistentVolumeBlock {
		// Block volumes should not have any FSType
		fsType = ""
	}

	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name:   p.options.PVName,
			Labels: labels,
			Annotations: map[string]string{
				"volumehelper.VolumeDynamicallyCreatedByKey": "azure-disk-dynamic-provisioner",
			},
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: p.options.PersistentVolumeReclaimPolicy,
			AccessModes:                   supportedModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", requestGiB)),
			},
			VolumeMode: volumeMode,
			PersistentVolumeSource: v1.PersistentVolumeSource{
				AzureDisk: &v1.AzureDiskVolumeSource{
					CachingMode: &cachingMode,
					DiskName:    name,
					DataDiskURI: diskURI,
					Kind:        &kind,
					FSType:      &fsType,
				},
			},
			MountOptions: p.options.MountOptions,
		},
	}

	nodeSelectorTerms := make([]v1.NodeSelectorTerm, 0)

	if zoned {
		// Set node affinity labels based on availability zone labels.
		if len(labels) > 0 {
			requirements := make([]v1.NodeSelectorRequirement, 0)
			for k, v := range labels {
				requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
			}

			nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
				MatchExpressions: requirements,
			})
		}
	} else {
		// Set node affinity labels based on fault domains.
		// This is required because unzoned AzureDisk can't be attached to zoned nodes.
		// There are at most 3 fault domains available in each region.
		// Refer https://docs.microsoft.com/en-us/azure/virtual-machines/windows/manage-availability.
		for i := 0; i < 3; i++ {
			requirements := []v1.NodeSelectorRequirement{
				{
					Key:      v1.LabelZoneRegion,
					Operator: v1.NodeSelectorOpIn,
					Values:   []string{diskController.GetLocation()},
				},
				{
					Key:      v1.LabelZoneFailureDomain,
					Operator: v1.NodeSelectorOpIn,
					Values:   []string{strconv.Itoa(i)},
				},
			}
			nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
				MatchExpressions: requirements,
			})
		}
	}

	if len(nodeSelectorTerms) > 0 {
		pv.Spec.NodeAffinity = &v1.VolumeNodeAffinity{
			Required: &v1.NodeSelector{
				NodeSelectorTerms: nodeSelectorTerms,
			},
		}
	}

	return pv, nil
}
<commit_msg>feat: add azure shared disk support<commit_after>// +build !providerless

/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package azure_dd

import (
	"errors"
	"fmt"
	"strconv"
	"strings"

	"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	volumehelpers "k8s.io/cloud-provider/volume/helpers"
	"k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/volume/util"
	"k8s.io/legacy-cloud-providers/azure"
)

type azureDiskProvisioner struct {
	plugin  *azureDataDiskPlugin
	options volume.VolumeOptions
}

type azureDiskDeleter struct {
	*dataDisk
	spec   *volume.Spec
	plugin *azureDataDiskPlugin
}

var _ volume.Provisioner = &azureDiskProvisioner{}
var _ volume.Deleter = &azureDiskDeleter{}

func (d *azureDiskDeleter) GetPath() string {
	return getPath(d.podUID, d.dataDisk.diskName, d.plugin.host)
}

func (d *azureDiskDeleter) Delete() error {
	volumeSource, _, err := getVolumeSource(d.spec)
	if err != nil {
		return err
	}

	diskController, err := getDiskController(d.plugin.host)
	if err != nil {
		return err
	}

	managed := (*volumeSource.Kind == v1.AzureManagedDisk)

	if managed {
		return diskController.DeleteManagedDisk(volumeSource.DataDiskURI)
	}

	return diskController.DeleteBlobDisk(volumeSource.DataDiskURI)
}

// parseZoned parsed 'zoned' for storage class. If zoned is not specified (empty string),
// then it defaults to true for managed disks.
func parseZoned(zonedString string, kind v1.AzureDataDiskKind) (bool, error) {
	if zonedString == "" {
		return kind == v1.AzureManagedDisk, nil
	}

	zoned, err := strconv.ParseBool(zonedString)
	if err != nil {
		return false, fmt.Errorf("failed to parse 'zoned': %v", err)
	}

	if zoned && kind != v1.AzureManagedDisk {
		return false, fmt.Errorf("zoned is only supported by managed disks")
	}

	return zoned, nil
}

func (p *azureDiskProvisioner) Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error) {
	// perform static validation first
	if p.options.PVC.Spec.Selector != nil {
		return nil, fmt.Errorf("azureDisk - claim.Spec.Selector is not supported for dynamic provisioning on Azure disk")
	}

	var (
		location, account          string
		storageAccountType, fsType string
		cachingMode                v1.AzureDataDiskCachingMode
		strKind                    string
		err                        error
		resourceGroup              string

		zoned                    bool
		zonePresent              bool
		zonesPresent             bool
		strZoned                 string
		availabilityZone         string
		availabilityZones        sets.String
		selectedAvailabilityZone string
		writeAcceleratorEnabled  string

		diskIopsReadWrite   string
		diskMbpsReadWrite   string
		diskEncryptionSetID string

		maxShares int
	)
	// maxLength = 79 - (4 for ".vhd") = 75
	name := util.GenerateVolumeName(p.options.ClusterName, p.options.PVName, 75)
	capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
	requestGiB, err := volumehelpers.RoundUpToGiBInt(capacity)
	if err != nil {
		return nil, err
	}

	for k, v := range p.options.Parameters {
		switch strings.ToLower(k) {
		case "skuname":
			storageAccountType = v
		case "location":
			location = v
		case "storageaccount":
			account = v
		case "storageaccounttype":
			storageAccountType = v
		case "kind":
			strKind = v
		case "cachingmode":
			cachingMode = v1.AzureDataDiskCachingMode(v)
		case volume.VolumeParameterFSType:
			fsType = strings.ToLower(v)
		case "resourcegroup":
			resourceGroup = v
		case "zone":
			zonePresent = true
			availabilityZone = v
		case "zones":
			zonesPresent = true
			availabilityZones, err = volumehelpers.ZonesToSet(v)
			if err != nil {
				return nil, fmt.Errorf("error parsing zones %s, must be strings separated by commas: %v", v, err)
			}
		case "zoned":
			strZoned = v
		case "diskiopsreadwrite":
			diskIopsReadWrite = v
		case "diskmbpsreadwrite":
			diskMbpsReadWrite = v
		case "diskencryptionsetid":
			diskEncryptionSetID = v
		case azure.WriteAcceleratorEnabled:
			writeAcceleratorEnabled = v
		case "maxshares":
			maxShares, err = strconv.Atoi(v)
			if err != nil {
				return nil, fmt.Errorf("parse %s failed with error: %v", v, err)
			}
			if maxShares < 1 {
				return nil, fmt.Errorf("parse %s returned with invalid value: %d", v, maxShares)
			}
		default:
			return nil, fmt.Errorf("AzureDisk - invalid option %s in storage class", k)
		}
	}

	supportedModes := p.plugin.GetAccessModes()
	if maxShares < 2 {
		// only do AccessModes validation when maxShares < 2
		if !util.AccessModesContainedInAll(p.plugin.GetAccessModes(), p.options.PVC.Spec.AccessModes) {
			return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported with maxShares(%d) < 2", p.options.PVC.Spec.AccessModes, p.plugin.GetAccessModes(), maxShares)
		}

		if len(p.options.PVC.Spec.AccessModes) > 1 {
			return nil, fmt.Errorf("AzureDisk - multiple access modes are not supported on AzureDisk plugin with maxShares(%d) < 2", maxShares)
		}

		if len(p.options.PVC.Spec.AccessModes) == 1 {
			if p.options.PVC.Spec.AccessModes[0] != supportedModes[0] {
				return nil, fmt.Errorf("AzureDisk - mode %s is not supported by AzureDisk plugin (supported mode is %s) with maxShares(%d) < 2", p.options.PVC.Spec.AccessModes[0], supportedModes, maxShares)
			}
		}
	} else {
		supportedModes = []v1.PersistentVolumeAccessMode{
			v1.ReadWriteOnce,
			v1.ReadOnlyMany,
			v1.ReadWriteMany,
		}
	}

	// normalize values
	skuName, err := normalizeStorageAccountType(storageAccountType)
	if err != nil {
		return nil, err
	}

	kind, err := normalizeKind(strFirstLetterToUpper(strKind))
	if err != nil {
		return nil, err
	}

	zoned, err = parseZoned(strZoned, kind)
	if err != nil {
		return nil, err
	}

	if kind != v1.AzureManagedDisk {
		if resourceGroup != "" {
			return nil, errors.New("StorageClass option 'resourceGroup' can be used only for managed disks")
		}

		if zoned {
			return nil, errors.New("StorageClass option 'zoned' parameter is only supported for managed disks")
		}
	}

	if !zoned && (zonePresent || zonesPresent || len(allowedTopologies) > 0) {
		return nil, fmt.Errorf("zone, zones and allowedTopologies StorageClass parameters must be used together with zoned parameter")
	}

	if cachingMode, err = normalizeCachingMode(cachingMode); err != nil {
		return nil, err
	}

	diskController, err := getDiskController(p.plugin.host)
	if err != nil {
		return nil, err
	}

	// Select zone for managed disks based on zone, zones and allowedTopologies.
	if zoned {
		activeZones, err := diskController.GetActiveZones()
		if err != nil {
			return nil, fmt.Errorf("error querying active zones: %v", err)
		}

		if availabilityZone != "" || availabilityZones.Len() != 0 || activeZones.Len() != 0 || len(allowedTopologies) != 0 {
			selectedAvailabilityZone, err = volumehelpers.SelectZoneForVolume(zonePresent, zonesPresent, availabilityZone, availabilityZones, activeZones, selectedNode, allowedTopologies, p.options.PVC.Name)
			if err != nil {
				return nil, err
			}
		}
	}

	// create disk
	diskURI := ""
	labels := map[string]string{}
	if kind == v1.AzureManagedDisk {
		tags := make(map[string]string)
		if p.options.CloudTags != nil {
			tags = *(p.options.CloudTags)
		}
		if strings.EqualFold(writeAcceleratorEnabled, "true") {
			tags[azure.WriteAcceleratorEnabled] = "true"
		}

		volumeOptions := &azure.ManagedDiskOptions{
			DiskName:            name,
			StorageAccountType:  skuName,
			ResourceGroup:       resourceGroup,
			PVCName:             p.options.PVC.Name,
			SizeGB:              requestGiB,
			Tags:                tags,
			AvailabilityZone:    selectedAvailabilityZone,
			DiskIOPSReadWrite:   diskIopsReadWrite,
			DiskMBpsReadWrite:   diskMbpsReadWrite,
			DiskEncryptionSetID: diskEncryptionSetID,
			MaxShares:           int32(maxShares),
		}
		diskURI, err = diskController.CreateManagedDisk(volumeOptions)
		if err != nil {
			return nil, err
		}
		labels, err = diskController.GetAzureDiskLabels(diskURI)
		if err != nil {
			return nil, err
		}
	} else {
		if kind == v1.AzureDedicatedBlobDisk {
			_, diskURI, _, err = diskController.CreateVolume(name, account, storageAccountType, location, requestGiB)
			if err != nil {
				return nil, err
			}
		} else {
			diskURI, err = diskController.CreateBlobDisk(name, storage.SkuName(skuName), requestGiB)
			if err != nil {
				return nil, err
			}
		}
	}

	volumeMode := p.options.PVC.Spec.VolumeMode
	if volumeMode != nil && *volumeMode == v1.PersistentVolumeBlock {
		// Block volumes should not have any FSType
		fsType = ""
	}

	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name:   p.options.PVName,
			Labels: labels,
			Annotations: map[string]string{
				"volumehelper.VolumeDynamicallyCreatedByKey": "azure-disk-dynamic-provisioner",
			},
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: p.options.PersistentVolumeReclaimPolicy,
			AccessModes:                   supportedModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", requestGiB)),
			},
			VolumeMode: volumeMode,
			PersistentVolumeSource: v1.PersistentVolumeSource{
				AzureDisk: &v1.AzureDiskVolumeSource{
					CachingMode: &cachingMode,
					DiskName:    name,
					DataDiskURI: diskURI,
					Kind:        &kind,
					FSType:      &fsType,
				},
			},
			MountOptions: p.options.MountOptions,
		},
	}

	nodeSelectorTerms := make([]v1.NodeSelectorTerm, 0)

	if zoned {
		// Set node affinity labels based on availability zone labels.
		if len(labels) > 0 {
			requirements := make([]v1.NodeSelectorRequirement, 0)
			for k, v := range labels {
				requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
			}

			nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
				MatchExpressions: requirements,
			})
		}
	} else {
		// Set node affinity labels based on fault domains.
		// This is required because unzoned AzureDisk can't be attached to zoned nodes.
		// There are at most 3 fault domains available in each region.
		// Refer https://docs.microsoft.com/en-us/azure/virtual-machines/windows/manage-availability.
		for i := 0; i < 3; i++ {
			requirements := []v1.NodeSelectorRequirement{
				{
					Key:      v1.LabelZoneRegion,
					Operator: v1.NodeSelectorOpIn,
					Values:   []string{diskController.GetLocation()},
				},
				{
					Key:      v1.LabelZoneFailureDomain,
					Operator: v1.NodeSelectorOpIn,
					Values:   []string{strconv.Itoa(i)},
				},
			}
			nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
				MatchExpressions: requirements,
			})
		}
	}

	if len(nodeSelectorTerms) > 0 {
		pv.Spec.NodeAffinity = &v1.VolumeNodeAffinity{
			Required: &v1.NodeSelector{
				NodeSelectorTerms: nodeSelectorTerms,
			},
		}
	}

	return pv, nil
}
<|endoftext|>


<commit_before>package webircgateway

import (
	"fmt"
	"log"
	"net"
	"runtime/debug"
	"strings"
	"sync"

	"github.com/igm/sockjs-go/sockjs"
	"github.com/orcaman/concurrent-map"
)

type TransportKiwiirc struct {
	gateway *Gateway
}

func (t *TransportKiwiirc) Init(g *Gateway) {
	t.gateway = g
	handler := sockjs.NewHandler("/webirc/kiwiirc", sockjs.DefaultOptions, t.sessionHandler)
	t.gateway.HttpRouter.Handle("/webirc/kiwiirc/", handler)
}

func (t *TransportKiwiirc) makeChannel(chanID string, ws sockjs.Session) *TransportKiwiircChannel {
	client := t.gateway.NewClient()

	originHeader := strings.ToLower(ws.Request().Header.Get("Origin"))
	if !t.gateway.isClientOriginAllowed(originHeader) {
		client.Log(2, "Origin %s not allowed. Closing connection", originHeader)
		ws.Close(0, "Origin not allowed")
		return nil
	}

	client.RemoteAddr = t.gateway.GetRemoteAddressFromRequest(ws.Request()).String()

	clientHostnames, err := net.LookupAddr(client.RemoteAddr)
	if err != nil || len(clientHostnames) == 0 {
		client.RemoteHostname = client.RemoteAddr
	} else {
		// FQDNs include a . at the end. Strip it out
		potentialHostname := strings.Trim(clientHostnames[0], ".")

		// Must check that the resolved hostname also resolves back to the users IP
		addr, err := net.LookupIP(potentialHostname)
		if err == nil && len(addr) == 1 && addr[0].String() == client.RemoteAddr {
			client.RemoteHostname = potentialHostname
		} else {
			client.RemoteHostname = client.RemoteAddr
		}
	}

	if t.gateway.isRequestSecure(ws.Request()) {
		client.Tags["secure"] = ""
	}

	// This doesn't make sense to have since the remote port may change between requests. Only
	// here for testing purposes for now.
	_, remoteAddrPort, _ := net.SplitHostPort(ws.Request().RemoteAddr)
	client.Tags["remote-port"] = remoteAddrPort

	client.Log(2, "New kiwi channel from %s %s", client.RemoteAddr, client.RemoteHostname)

	channel := &TransportKiwiircChannel{
		Id:           chanID,
		Client:       client,
		Conn:         ws,
		waitForClose: make(chan bool),
		Closed:       false,
	}

	go channel.listenForSignals()

	return channel
}

func (t *TransportKiwiirc) sessionHandler(session sockjs.Session) {
	// Don't let a single users error kill the entire service for everyone
	defer func() {
		if r := recover(); r != nil {
			log.Printf("[ERROR] Recovered from %s\n%s", r, debug.Stack())
		}
	}()

	channels := cmap.New()

	// Read from sockjs
	go func() {
		for {
			msg, err := session.Recv()
			if err == nil && len(msg) > 0 {
				idEnd := strings.Index(msg, " ")
				if idEnd == -1 {
					// msg is in the form of ":chanId"
					chanID := msg[1:]

					c, channelExists := channels.Get(chanID)
					if channelExists {
						channel := c.(*TransportKiwiircChannel)
						channel.close()
					}

					if !channelExists {
						channel := t.makeChannel(chanID, session)
						if channel == nil {
							continue
						}
						channels.Set(chanID, channel)

						// When the channel closes, remove it from the map again
						go func() {
							<-channel.waitForClose
							channel.Client.Log(2, "Removing channel from connection")
							channels.Remove(chanID)
						}()
					}

					session.Send(":" + chanID)

				} else {
					// msg is in the form of ":chanId data"
					chanID := msg[1:idEnd]
					data := msg[idEnd+1:]

					channel, channelExists := channels.Get(chanID)
					if channelExists {
						c := channel.(*TransportKiwiircChannel)
						c.handleIncomingLine(data)
					}
				}
			} else if err != nil {
				t.gateway.Log(1, "kiwi connection closed (%s)", err.Error())
				break
			}
		}

		for channel := range channels.Iter() {
			c := channel.Val.(*TransportKiwiircChannel)
			c.Closed = true
			c.Client.StartShutdown("client_closed")
		}
	}()
}

type TransportKiwiircChannel struct {
	Conn         sockjs.Session
	Client       *Client
	Id           string
	waitForClose chan bool
	ClosedLock   sync.Mutex
	Closed       bool
}

func (c *TransportKiwiircChannel) listenForSignals() {
	for {
		signal, ok := <-c.Client.Signals
		if !ok {
			break
		}
		c.Client.Log(1, "signal:%s %s", signal[0], signal[1])
		if signal[0] == "state" {
			if signal[1] == "connected" {
				c.Conn.Send(fmt.Sprintf(":%s control connected", c.Id))
			} else if signal[1] == "closed" {
				c.Conn.Send(fmt.Sprintf(":%s control closed %s", c.Id, signal[2]))
			}
		}

		if signal[0] == "data" {
			toSend := strings.Trim(signal[1], "\r\n")
			c.Conn.Send(fmt.Sprintf(":%s %s", c.Id, toSend))
		}
	}

	c.ClosedLock.Lock()

	c.Closed = true
	close(c.Client.Recv)
	close(c.waitForClose)

	c.ClosedLock.Unlock()
}

func (c *TransportKiwiircChannel) handleIncomingLine(line string) {
	c.ClosedLock.Lock()

	if !c.Closed {
		c.Client.Recv <- line
	}

	c.ClosedLock.Unlock()
}

func (c *TransportKiwiircChannel) close() {
	c.Conn.Close(0, "Requested")
}
<commit_msg>Show host in new client connections debug messages<commit_after>package webircgateway

import (
	"fmt"
	"log"
	"net"
	"runtime/debug"
	"strings"
	"sync"

	"github.com/igm/sockjs-go/sockjs"
	"github.com/orcaman/concurrent-map"
)

type TransportKiwiirc struct {
	gateway *Gateway
}

func (t *TransportKiwiirc) Init(g *Gateway) {
	t.gateway = g
	handler := sockjs.NewHandler("/webirc/kiwiirc", sockjs.DefaultOptions, t.sessionHandler)
	t.gateway.HttpRouter.Handle("/webirc/kiwiirc/", handler)
}

func (t *TransportKiwiirc) makeChannel(chanID string, ws sockjs.Session) *TransportKiwiircChannel {
	client := t.gateway.NewClient()

	originHeader := strings.ToLower(ws.Request().Header.Get("Origin"))
	if !t.gateway.isClientOriginAllowed(originHeader) {
		client.Log(2, "Origin %s not allowed. Closing connection", originHeader)
		ws.Close(0, "Origin not allowed")
		return nil
	}

	client.RemoteAddr = t.gateway.GetRemoteAddressFromRequest(ws.Request()).String()

	clientHostnames, err := net.LookupAddr(client.RemoteAddr)
	if err != nil || len(clientHostnames) == 0 {
		client.RemoteHostname = client.RemoteAddr
	} else {
		// FQDNs include a . at the end. Strip it out
		potentialHostname := strings.Trim(clientHostnames[0], ".")

		// Must check that the resolved hostname also resolves back to the users IP
		addr, err := net.LookupIP(potentialHostname)
		if err == nil && len(addr) == 1 && addr[0].String() == client.RemoteAddr {
			client.RemoteHostname = potentialHostname
		} else {
			client.RemoteHostname = client.RemoteAddr
		}
	}

	if t.gateway.isRequestSecure(ws.Request()) {
		client.Tags["secure"] = ""
	}

	// This doesn't make sense to have since the remote port may change between requests. Only
	// here for testing purposes for now.
	_, remoteAddrPort, _ := net.SplitHostPort(ws.Request().RemoteAddr)
	client.Tags["remote-port"] = remoteAddrPort

	client.Log(2, "New kiwi channel on %s from %s %s", ws.Request().Host, client.RemoteAddr, client.RemoteHostname)

	channel := &TransportKiwiircChannel{
		Id:           chanID,
		Client:       client,
		Conn:         ws,
		waitForClose: make(chan bool),
		Closed:       false,
	}

	go channel.listenForSignals()

	return channel
}

func (t *TransportKiwiirc) sessionHandler(session sockjs.Session) {
	// Don't let a single users error kill the entire service for everyone
	defer func() {
		if r := recover(); r != nil {
			log.Printf("[ERROR] Recovered from %s\n%s", r, debug.Stack())
		}
	}()

	channels := cmap.New()

	// Read from sockjs
	go func() {
		for {
			msg, err := session.Recv()
			if err == nil && len(msg) > 0 {
				idEnd := strings.Index(msg, " ")
				if idEnd == -1 {
					// msg is in the form of ":chanId"
					chanID := msg[1:]

					c, channelExists := channels.Get(chanID)
					if channelExists {
						channel := c.(*TransportKiwiircChannel)
						channel.close()
					}

					if !channelExists {
						channel := t.makeChannel(chanID, session)
						if channel == nil {
							continue
						}
						channels.Set(chanID, channel)

						// When the channel closes, remove it from the map again
						go func() {
							<-channel.waitForClose
							channel.Client.Log(2, "Removing channel from connection")
							channels.Remove(chanID)
						}()
					}

					session.Send(":" + chanID)

				} else {
					// msg is in the form of ":chanId data"
					chanID := msg[1:idEnd]
					data := msg[idEnd+1:]

					channel, channelExists := channels.Get(chanID)
					if channelExists {
						c := channel.(*TransportKiwiircChannel)
						c.handleIncomingLine(data)
					}
				}
			} else if err != nil {
				t.gateway.Log(1, "kiwi connection closed (%s)", err.Error())
				break
			}
		}

		for channel := range channels.Iter() {
			c := channel.Val.(*TransportKiwiircChannel)
			c.Closed = true
			c.Client.StartShutdown("client_closed")
		}
	}()
}

type TransportKiwiircChannel struct {
	Conn         sockjs.Session
	Client       *Client
	Id           string
	waitForClose chan bool
	ClosedLock   sync.Mutex
	Closed       bool
}

func (c *TransportKiwiircChannel) listenForSignals() {
	for {
		signal, ok := <-c.Client.Signals
		if !ok {
			break
		}
		c.Client.Log(1, "signal:%s %s", signal[0], signal[1])
		if signal[0] == "state" {
			if signal[1] == "connected" {
				c.Conn.Send(fmt.Sprintf(":%s control connected", c.Id))
			} else if signal[1] == "closed" {
				c.Conn.Send(fmt.Sprintf(":%s control closed %s", c.Id, signal[2]))
			}
		}

		if signal[0] == "data" {
			toSend := strings.Trim(signal[1], "\r\n")
			c.Conn.Send(fmt.Sprintf(":%s %s", c.Id, toSend))
		}
	}

	c.ClosedLock.Lock()

	c.Closed = true
	close(c.Client.Recv)
	close(c.waitForClose)

	c.ClosedLock.Unlock()
}

func (c *TransportKiwiircChannel) handleIncomingLine(line string) {
	c.ClosedLock.Lock()

	if !c.Closed {
		c.Client.Recv <- line
	}

	c.ClosedLock.Unlock()
}

func (c *TransportKiwiircChannel) close() {
	c.Conn.Close(0, "Requested")
}
<|endoftext|>


<commit_before>// +build darwin

package darwin

import (
	"bufio"
	"bytes"
	"os/exec"
	"strconv"
	"strings"

	"github.com/mackerelio/mackerel-agent/logging"
)

// CPUGenerator Collects CPU specs
type CPUGenerator struct {
}

// Key XXX
func (g *CPUGenerator) Key() string {
	return "cpu"
}

var cpuLogger = logging.GetLogger("spec.cpu")

type cpuSpec map[string]interface{}

var sysCtlKeyMap = map[string]string{
	"core_count":   "cores",
	"brand_string": "model_name",
	"model":        "model",
	"vendor":       "vendor_id",
	"family":       "family",
	"stepping":     "stepping",
}

func (g *CPUGenerator) parseSysCtlBytes(res []byte) (interface{}, error) {
	scanner := bufio.NewScanner(bytes.NewBuffer(res))

	results := cpuSpec{}
	var coreCount int

	for scanner.Scan() {
		line := scanner.Text()
		kv := strings.SplitN(line, ":", 2)
		if len(kv) < 2 {
			continue
		}
		key := strings.TrimPrefix(strings.TrimSpace(kv[0]), "machdep.cpu.")
		val := strings.TrimSpace(kv[1])
		if label, ok := sysCtlKeyMap[key]; ok {
			results[label] = val
		}
		if key == "core_count" {
			var err error
			coreCount, err = strconv.Atoi(val)
			if err != nil {
				cpuLogger.Errorf("while parsing %q: %s", val, err)
				return nil, err
			}
		}
	}

	if err := scanner.Err(); err != nil {
		cpuLogger.Errorf("Failed (skip this spec): %s", err)
		return nil, err
	}

	wholeResults := make([]cpuSpec, coreCount)
	for i := 0; i < coreCount; i++ {
		wholeResults[i] = results
	}
	return wholeResults, nil
}

// MEMO: sysctl -a machdep.cpu.brand_string

// Generate collects CPU specs.
// Returns an array of cpuSpec.
// Each spec is expected to have keys below:
// - model_name (used in Web)
// - vendor_id
// - family
// - model
// - stepping
// - physical_id
// - core_id
// - cores
// - mhz
// - cache_size
// - flags
func (g *CPUGenerator) Generate() (interface{}, error) {
	cpuInfoBytes, err := exec.Command("sysctl", "-a", "machdep.cpu").Output()
	if err != nil {
		cpuLogger.Errorf("Failed: %s", err)
		return nil, err
	}
	return g.parseSysCtlBytes(cpuInfoBytes)
}
<commit_msg>Take cpu counts from hw.logicalcpu<commit_after>// +build darwin

package darwin

import (
	"bufio"
	"bytes"
	"os/exec"
	"strconv"
	"strings"

	"github.com/mackerelio/mackerel-agent/logging"
)

// CPUGenerator Collects CPU specs
type CPUGenerator struct {
}

// Key XXX
func (g *CPUGenerator) Key() string {
	return "cpu"
}

var cpuLogger = logging.GetLogger("spec.cpu")

type cpuSpec map[string]interface{}

var sysCtlKeyMap = map[string]string{
	"core_count":   "cores",
	"brand_string": "model_name",
	"model":        "model",
	"vendor":       "vendor_id",
	"family":       "family",
	"stepping":     "stepping",
}

func (g *CPUGenerator) parseSysCtlBytes(res []byte) (cpuSpec, error) {
	scanner := bufio.NewScanner(bytes.NewBuffer(res))

	results := cpuSpec{}

	for scanner.Scan() {
		line := scanner.Text()
		kv := strings.SplitN(line, ":", 2)
		if len(kv) < 2 {
			continue
		}
		key := strings.TrimPrefix(strings.TrimSpace(kv[0]), "machdep.cpu.")
		val := strings.TrimSpace(kv[1])
		if label, ok := sysCtlKeyMap[key]; ok {
			results[label] = val
		}
	}

	if err := scanner.Err(); err != nil {
		cpuLogger.Errorf("Failed (skip this spec): %s", err)
		return nil, err
	}
	return results, nil
}

func (g *CPUGenerator) getCoreCount() (*int, error) {
	coreCountBytes, err := exec.Command("sysctl", "-n", "hw.logicalcpu").Output()
	if err != nil {
		cpuLogger.Errorf("Failed: %s", err)
		return nil, err
	}
	coreCount, err := strconv.Atoi(strings.TrimSpace(string(coreCountBytes)))
	if err != nil {
		cpuLogger.Errorf("Failed to parse: %s", err)
		return nil, err
	}
	return &coreCount, nil
}

// MEMO: sysctl -a machdep.cpu.brand_string

// Generate collects CPU specs.
// Returns an array of cpuSpec.
// Each spec is expected to have keys below:
// - model_name (used in Web)
// - vendor_id
// - family
// - model
// - stepping
// - physical_id
// - core_id
// - cores
// - mhz
// - cache_size
// - flags
func (g *CPUGenerator) Generate() (interface{}, error) {
	cpuInfoBytes, err := exec.Command("sysctl", "-a", "machdep.cpu").Output()
	coreInfo, err := g.parseSysCtlBytes(cpuInfoBytes)
	if err != nil {
		cpuLogger.Errorf("Failed: %s", err)
		return nil, err
	}
	coreCount, err := g.getCoreCount()
	if err != nil {
		cpuLogger.Errorf("Failed: %s", err)
		return nil, err
	}
	results := make([]cpuSpec, *coreCount)
	for i := 0; i < *coreCount; i++ {
		results[i] = coreInfo
	}
	return results, nil
}
<|endoftext|>


<commit_before>package primitive

import (
	"fmt"
	"image"
	"math/rand"
	"time"

	"github.com/fogleman/gg"
)

const SaveFrames = false

type Model struct {
	W, H    int
	Target  *image.RGBA
	Current *image.RGBA
	Buffer  *image.RGBA
	Context *gg.Context
	Score   float64
	Alpha   int
	Scale   int
	Mode    Mode
	Shapes  []Shape
}

func NewModel(target image.Image, alpha, scale int, mode Mode) *Model {
	c := averageImageColor(target)
	// c = color.White
	// c = color.Black
	size := target.Bounds().Size()
	model := &Model{}
	model.W = size.X
	model.H = size.Y
	model.Target = imageToRGBA(target)
	model.Current = uniformRGBA(target.Bounds(), c)
	model.Buffer = uniformRGBA(target.Bounds(), c)
	model.Score = differenceFull(model.Target, model.Current)
	model.Context = gg.NewContext(model.W*scale, model.H*scale)
	model.Context.Scale(float64(scale), float64(scale))
	model.Context.Translate(0.5, 0.5)
	model.Context.SetColor(c)
	model.Context.Clear()
	model.Alpha = alpha
	model.Scale = scale
	model.Mode = mode
	return model
}

func (model *Model) Run(n int) image.Image {
	start := time.Now()
	for i := 1; i <= n; i++ {
		model.Step()
		elapsed := time.Since(start).Seconds()
		fmt.Printf("%d, %.3f, %.6f\n", i, elapsed, model.Score)
		if SaveFrames {
			SavePNG("out.png", model.Current)
			model.Context.SavePNG(fmt.Sprintf("out%03d.png", i))
		}
	}
	return model.Context.Image()
}

func (model *Model) Step() {
	state := model.BestRandomState(model.Buffer, model.Mode, 3000)
	// state = Anneal(state, 0.1, 0.00001, 25000).(*State)
	state = HillClimb(state, 1000).(*State)
	model.Add(state.Shape)
}

func (model *Model) worker(i int, ch chan *State) {
	buffer := image.NewRGBA(model.Target.Bounds())
	state := model.BestRandomState(buffer, Mode(i+1), 3000)
	// state = Anneal(state, 0.1, 0.00001, 25000).(*State)
	state = HillClimb(state, 1000).(*State)
	ch <- state
}

func (model *Model) GoStep() {
	n := 3
	ch := make(chan *State, n)
	for i := 0; i < n; i++ {
		go model.worker(i, ch)
	}
	var bestShape Shape
	var bestEnergy float64
	for i := 0; i < n; i++ {
		state := <-ch
		shape := state.Shape
		energy := model.Energy(shape, model.Buffer)
		fmt.Printf("%.6f\n", energy)
		if i == 0 || energy < bestEnergy {
			bestEnergy = energy
			bestShape = shape
		}
	}
	model.Add(bestShape)
}

func (model *Model) BestRandomState(buffer *image.RGBA, t Mode, n int) *State {
	var bestEnergy float64
	var bestState *State
	for i := 0; i < n; i++ {
		state := model.RandomState(buffer, t)
		energy := state.Energy()
		if i == 0 || energy < bestEnergy {
			bestEnergy = energy
			bestState = state
		}
	}
	return bestState
}

func (model *Model) RandomState(buffer *image.RGBA, t Mode) *State {
	switch t {
	default:
		return model.RandomState(buffer, Mode(rand.Intn(4)+1))
	case ModeTriangle:
		return NewState(model, buffer, NewRandomTriangle(model.W, model.H))
	case ModeRectangle:
		return NewState(model, buffer, NewRandomRectangle(model.W, model.H))
	case ModeEllipse:
		return NewState(model, buffer, NewRandomEllipse(model.W, model.H))
	case ModeCircle:
		return NewState(model, buffer, NewRandomCircle(model.W, model.H))
	case ModeRotatedRectangle:
		return NewState(model, buffer, NewRandomRotatedRectangle(model.W, model.H))
	}
}

func (model *Model) Add(shape Shape) {
	lines := shape.Rasterize()
	c := model.computeColor(lines, model.Alpha)
	s := model.computeScore(lines, c, model.Buffer)
	Draw(model.Current, c, lines)

	model.Score = s
	model.Shapes = append(model.Shapes, shape)

	model.Context.SetRGBA255(c.R, c.G, c.B, c.A)
	shape.Draw(model.Context)
}

func (model *Model) computeColor(lines []Scanline, alpha int) Color {
	var count int
	var rsum, gsum, bsum float64
	a := float64(alpha) / 255
	for _, line := range lines {
		i := model.Target.PixOffset(line.X1, line.Y)
		for x := line.X1; x <= line.X2; x++ {
			count++
			tr := float64(model.Target.Pix[i])
			tg := float64(model.Target.Pix[i+1])
			tb := float64(model.Target.Pix[i+2])
			cr := float64(model.Current.Pix[i])
			cg := float64(model.Current.Pix[i+1])
			cb := float64(model.Current.Pix[i+2])
			i += 4
			rsum += (a*cr - cr + tr) / a
			gsum += (a*cg - cg + tg) / a
			bsum += (a*cb - cb + tb) / a
		}
	}
	if count == 0 {
		return Color{}
	}
	r := clampInt(int(rsum/float64(count)), 0, 255)
	g := clampInt(int(gsum/float64(count)), 0, 255)
	b := clampInt(int(bsum/float64(count)), 0, 255)
	return Color{r, g, b, alpha}
}

func (model *Model) computeScore(lines []Scanline, c Color, buffer *image.RGBA) float64 {
	copy(buffer.Pix, model.Current.Pix)
	Draw(buffer, c, lines)
	return differencePartial(model.Target, model.Current, buffer, model.Score, lines)
}

func (model *Model) Energy(shape Shape, buffer *image.RGBA) float64 {
	lines := shape.Rasterize()
	c := model.computeColor(lines, model.Alpha)
	s := model.computeScore(lines, c, buffer)
	return s
}
<commit_msg>BestHillClimbState<commit_after>package primitive

import (
	"fmt"
	"image"
	"math/rand"
	"time"

	"github.com/fogleman/gg"
)

const SaveFrames = false

type Model struct {
	W, H    int
	Target  *image.RGBA
	Current *image.RGBA
	Buffer  *image.RGBA
	Context *gg.Context
	Score   float64
	Alpha   int
	Scale   int
	Mode    Mode
	Shapes  []Shape
}

func NewModel(target image.Image, alpha, scale int, mode Mode) *Model {
	c := averageImageColor(target)
	// c = color.White
	// c = color.Black
	size := target.Bounds().Size()
	model := &Model{}
	model.W = size.X
	model.H = size.Y
	model.Target = imageToRGBA(target)
	model.Current = uniformRGBA(target.Bounds(), c)
	model.Buffer = uniformRGBA(target.Bounds(), c)
	model.Score = differenceFull(model.Target, model.Current)
	model.Context = gg.NewContext(model.W*scale, model.H*scale)
	model.Context.Scale(float64(scale), float64(scale))
	model.Context.Translate(0.5, 0.5)
	model.Context.SetColor(c)
	model.Context.Clear()
	model.Alpha = alpha
	model.Scale = scale
	model.Mode = mode
	return model
}

func (model *Model) Run(n int) image.Image {
	start := time.Now()
	for i := 1; i <= n; i++ {
		model.Step()
		elapsed := time.Since(start).Seconds()
		fmt.Printf("%d, %.3f, %.6f\n", i, elapsed, model.Score)
		if SaveFrames {
			SavePNG("out.png", model.Current)
			model.Context.SavePNG(fmt.Sprintf("out%03d.png", i))
		}
	}
	return model.Context.Image()
}

func (model *Model) Step() {
	state := model.BestHillClimbState(model.Buffer, model.Mode, 100, 100, 10)
	// state := model.BestRandomState(model.Buffer, model.Mode, 3000)
	// state = Anneal(state, 0.1, 0.00001, 25000).(*State)
	state = HillClimb(state, 1000).(*State)
	model.Add(state.Shape)
}

func (model *Model) worker(i int, ch chan *State) {
	mode := Mode(i + 1)
	buffer := image.NewRGBA(model.Target.Bounds())
	state := model.BestHillClimbState(buffer, mode, 100, 100, 10)
	// state := model.BestRandomState(buffer, mode, 3000)
	// state = Anneal(state, 0.1, 0.00001, 25000).(*State)
	state = HillClimb(state, 1000).(*State)
	ch <- state
}

func (model *Model) GoStep() {
	n := 3
	ch := make(chan *State, n)
	for i := 0; i < n; i++ {
		go model.worker(i, ch)
	}
	var bestShape Shape
	var bestEnergy float64
	for i := 0; i < n; i++ {
		state := <-ch
		shape := state.Shape
		energy := model.Energy(shape, model.Buffer)
		fmt.Printf("%.6f\n", energy)
		if i == 0 || energy < bestEnergy {
			bestEnergy = energy
			bestShape = shape
		}
	}
	model.Add(bestShape)
}

func (model *Model) BestHillClimbState(buffer *image.RGBA, t Mode, n, age, m int) *State {
	var bestEnergy float64
	var bestState *State
	for i := 0; i < m; i++ {
		state := model.BestRandomState(buffer, t, n)
		state = HillClimb(state, age).(*State)
		energy := state.Energy()
		if i == 0 || energy < bestEnergy {
			bestEnergy = energy
			bestState = state
		}
	}
	return bestState
}

func (model *Model) BestRandomState(buffer *image.RGBA, t Mode, n int) *State {
	var bestEnergy float64
	var bestState *State
	for i := 0; i < n; i++ {
		state := model.RandomState(buffer, t)
		energy := state.Energy()
		if i == 0 || energy < bestEnergy {
			bestEnergy = energy
			bestState = state
		}
	}
	return bestState
}

func (model *Model) RandomState(buffer *image.RGBA, t Mode) *State {
	switch t {
	default:
		return model.RandomState(buffer, Mode(rand.Intn(4)+1))
	case ModeTriangle:
		return NewState(model, buffer, NewRandomTriangle(model.W, model.H))
	case ModeRectangle:
		return NewState(model, buffer, NewRandomRectangle(model.W, model.H))
	case ModeEllipse:
		return NewState(model, buffer, NewRandomEllipse(model.W, model.H))
	case ModeCircle:
		return NewState(model, buffer, NewRandomCircle(model.W, model.H))
	case ModeRotatedRectangle:
		return NewState(model, buffer, NewRandomRotatedRectangle(model.W, model.H))
	}
}

func (model *Model) Add(shape Shape) {
	lines := shape.Rasterize()
	c := model.computeColor(lines, model.Alpha)
	s := model.computeScore(lines, c, model.Buffer)
	Draw(model.Current, c, lines)

	model.Score = s
	model.Shapes = append(model.Shapes, shape)

	model.Context.SetRGBA255(c.R, c.G, c.B, c.A)
	shape.Draw(model.Context)
}

func (model *Model) computeColor(lines []Scanline, alpha int) Color {
	var count int
	var rsum, gsum, bsum float64
	a := float64(alpha) / 255
	for _, line := range lines {
		i := model.Target.PixOffset(line.X1, line.Y)
		for x := line.X1; x <= line.X2; x++ {
			count++
			tr := float64(model.Target.Pix[i])
			tg := float64(model.Target.Pix[i+1])
			tb := float64(model.Target.Pix[i+2])
			cr := float64(model.Current.Pix[i])
			cg := float64(model.Current.Pix[i+1])
			cb := float64(model.Current.Pix[i+2])
			i += 4
			rsum += (a*cr - cr + tr) / a
			gsum += (a*cg - cg + tg) / a
			bsum += (a*cb - cb + tb) / a
		}
	}
	if count == 0 {
		return Color{}
	}
	r := clampInt(int(rsum/float64(count)), 0, 255)
	g := clampInt(int(gsum/float64(count)), 0, 255)
	b := clampInt(int(bsum/float64(count)), 0, 255)
	return Color{r, g, b, alpha}
}

func (model *Model) computeScore(lines []Scanline, c Color, buffer *image.RGBA) float64 {
	copy(buffer.Pix, model.Current.Pix)
	Draw(buffer, c, lines)
	return differencePartial(model.Target, model.Current, buffer, model.Score, lines)
}

func (model *Model) Energy(shape Shape, buffer *image.RGBA) float64 {
	lines := shape.Rasterize()
	c := model.computeColor(lines, model.Alpha)
	s := model.computeScore(lines, c, buffer)
	return s
}
<|endoftext|>


<commit_before>// Copyright 2017 PingCAP, 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 core_test

import (
	. "github.com/pingcap/check"
	"github.com/pingcap/tidb/expression"
	"github.com/pingcap/tidb/infoschema"
	"github.com/pingcap/tidb/parser/ast"
	"github.com/pingcap/tidb/parser/model"
	"github.com/pingcap/tidb/planner/core"
	driver "github.com/pingcap/tidb/types/parser_driver"
	"github.com/pingcap/tidb/util/testkit"
)

var _ = Suite(&testCacheableSuite{})

type testCacheableSuite struct {
}

func (s *testCacheableSuite) TestCacheable(c *C) {
	store, dom, err := newStoreWithBootstrap()
	c.Assert(err, IsNil)
	tk := testkit.NewTestKit(c, store)
	defer func() {
		dom.Close()
		store.Close()
	}()
	tk.MustExec("use test")
	tk.MustExec("create table t1(a int, b int) partition by range(a) ( partition p0 values less than (6), partition p1 values less than (11) )")
	tk.MustExec("create table t2(a int, b int) partition by hash(a) partitions 11")
	tk.MustExec("create table t3(a int, b int)")
	tbl := &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t3")}
	is := tk.Se.GetInfoSchema().(infoschema.InfoSchema)
	// test non-SelectStmt/-InsertStmt/-DeleteStmt/-UpdateStmt/-SetOprStmt
	var stmt ast.Node = &ast.ShowStmt{}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	stmt = &ast.LoadDataStmt{}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	// test SetOprStmt
	stmt = &ast.SetOprStmt{}
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	tableRefsClause := &ast.TableRefsClause{TableRefs: &ast.Join{Left: &ast.TableSource{Source: tbl}}}
	// test InsertStmt
	stmt = &ast.InsertStmt{Table: tableRefsClause}
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	// test DeleteStmt
	whereExpr := &ast.FuncCallExpr{}
	stmt = &ast.DeleteStmt{
		TableRefs: tableRefsClause,
		Where:     whereExpr,
	}
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	for funcName := range expression.UnCacheableFunctions {
		whereExpr.FnName = model.NewCIStr(funcName)
		c.Assert(core.Cacheable(stmt, is), IsFalse)
	}

	whereExpr.FnName = model.NewCIStr(ast.Rand)
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	stmt = &ast.DeleteStmt{
		TableRefs: tableRefsClause,
		Where:     &ast.ExistsSubqueryExpr{},
	}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	limitStmt := &ast.Limit{
		Count: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.DeleteStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	limitStmt = &ast.Limit{
		Offset: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.DeleteStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	limitStmt = &ast.Limit{}
	stmt = &ast.DeleteStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	stmt.(*ast.DeleteStmt).TableHints = append(stmt.(*ast.DeleteStmt).TableHints, &ast.TableOptimizerHint{
		HintName: model.NewCIStr(core.HintIgnorePlanCache),
	})
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	// test UpdateStmt
	whereExpr = &ast.FuncCallExpr{}
	stmt = &ast.UpdateStmt{
		TableRefs: tableRefsClause,
		Where:     whereExpr,
	}
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	for funcName := range expression.UnCacheableFunctions {
		whereExpr.FnName = model.NewCIStr(funcName)
		c.Assert(core.Cacheable(stmt, is), IsFalse)
	}

	whereExpr.FnName = model.NewCIStr(ast.Rand)
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	stmt = &ast.UpdateStmt{
		TableRefs: tableRefsClause,
		Where:     &ast.ExistsSubqueryExpr{},
	}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	limitStmt = &ast.Limit{
		Count: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.UpdateStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	limitStmt = &ast.Limit{
		Offset: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.UpdateStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	limitStmt = &ast.Limit{}
	stmt = &ast.UpdateStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	stmt.(*ast.UpdateStmt).TableHints = append(stmt.(*ast.UpdateStmt).TableHints, &ast.TableOptimizerHint{
		HintName: model.NewCIStr(core.HintIgnorePlanCache),
	})
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	// test SelectStmt
	whereExpr = &ast.FuncCallExpr{}
	stmt = &ast.SelectStmt{
		Where: whereExpr,
	}
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	for funcName := range expression.UnCacheableFunctions {
		whereExpr.FnName = model.NewCIStr(funcName)
		c.Assert(core.Cacheable(stmt, is), IsFalse)
	}

	whereExpr.FnName = model.NewCIStr(ast.Rand)
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	stmt = &ast.SelectStmt{
		Where: &ast.ExistsSubqueryExpr{},
	}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	limitStmt = &ast.Limit{
		Count: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.SelectStmt{
		Limit: limitStmt,
	}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	limitStmt = &ast.Limit{
		Offset: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.SelectStmt{
		Limit: limitStmt,
	}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	limitStmt = &ast.Limit{}
	stmt = &ast.SelectStmt{
		Limit: limitStmt,
	}
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	paramExpr := &driver.ParamMarkerExpr{}
	orderByClause := &ast.OrderByClause{Items: []*ast.ByItem{{Expr: paramExpr}}}
	stmt = &ast.SelectStmt{
		OrderBy: orderByClause,
	}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	valExpr := &driver.ValueExpr{}
	orderByClause = &ast.OrderByClause{Items: []*ast.ByItem{{Expr: valExpr}}}
	stmt = &ast.SelectStmt{
		OrderBy: orderByClause,
	}
	c.Assert(core.Cacheable(stmt, is), IsTrue)

	stmt.(*ast.SelectStmt).TableHints = append(stmt.(*ast.SelectStmt).TableHints, &ast.TableOptimizerHint{
		HintName: model.NewCIStr(core.HintIgnorePlanCache),
	})
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	boundExpr := &ast.FrameBound{Expr: &driver.ParamMarkerExpr{}}
	c.Assert(core.Cacheable(boundExpr, is), IsFalse)

	// Partition table can not be cached.
	join := &ast.Join{
		Left:  &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t1")},
		Right: &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t2")},
	}
	stmt = &ast.SelectStmt{
		From: &ast.TableRefsClause{
			TableRefs: join,
		},
	}
	c.Assert(core.Cacheable(stmt, is), IsFalse)

	join = &ast.Join{
		Left: &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t3")},
	}
	stmt = &ast.SelectStmt{
		From: &ast.TableRefsClause{
			TableRefs: join,
		},
	}
	c.Assert(core.Cacheable(stmt, is), IsTrue)
}
<commit_msg>planner/core: migrate cacheable_checker_test.go test infra to testify (#28694)<commit_after>// Copyright 2017 PingCAP, 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 core_test

import (
	"testing"

	"github.com/pingcap/tidb/expression"
	"github.com/pingcap/tidb/infoschema"
	"github.com/pingcap/tidb/parser/ast"
	"github.com/pingcap/tidb/parser/model"
	"github.com/pingcap/tidb/planner/core"
	"github.com/pingcap/tidb/testkit"
	driver "github.com/pingcap/tidb/types/parser_driver"
	"github.com/stretchr/testify/require"
)

func TestCacheable(t *testing.T) {
	t.Parallel()

	store, clean := testkit.CreateMockStore(t)
	defer clean()

	tk := testkit.NewTestKit(t, store)

	tk.MustExec("use test")
	tk.MustExec("create table t1(a int, b int) partition by range(a) ( partition p0 values less than (6), partition p1 values less than (11) )")
	tk.MustExec("create table t2(a int, b int) partition by hash(a) partitions 11")
	tk.MustExec("create table t3(a int, b int)")
	tbl := &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t3")}
	is := tk.Session().GetInfoSchema().(infoschema.InfoSchema)
	// test non-SelectStmt/-InsertStmt/-DeleteStmt/-UpdateStmt/-SetOprStmt
	var stmt ast.Node = &ast.ShowStmt{}
	require.False(t, core.Cacheable(stmt, is))

	stmt = &ast.LoadDataStmt{}
	require.False(t, core.Cacheable(stmt, is))

	// test SetOprStmt
	stmt = &ast.SetOprStmt{}
	require.True(t, core.Cacheable(stmt, is))

	tableRefsClause := &ast.TableRefsClause{TableRefs: &ast.Join{Left: &ast.TableSource{Source: tbl}}}
	// test InsertStmt
	stmt = &ast.InsertStmt{Table: tableRefsClause}
	require.True(t, core.Cacheable(stmt, is))

	// test DeleteStmt
	whereExpr := &ast.FuncCallExpr{}
	stmt = &ast.DeleteStmt{
		TableRefs: tableRefsClause,
		Where:     whereExpr,
	}
	require.True(t, core.Cacheable(stmt, is))

	for funcName := range expression.UnCacheableFunctions {
		whereExpr.FnName = model.NewCIStr(funcName)
		require.False(t, core.Cacheable(stmt, is))
	}

	whereExpr.FnName = model.NewCIStr(ast.Rand)
	require.True(t, core.Cacheable(stmt, is))

	stmt = &ast.DeleteStmt{
		TableRefs: tableRefsClause,
		Where:     &ast.ExistsSubqueryExpr{},
	}
	require.False(t, core.Cacheable(stmt, is))

	limitStmt := &ast.Limit{
		Count: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.DeleteStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	require.False(t, core.Cacheable(stmt, is))

	limitStmt = &ast.Limit{
		Offset: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.DeleteStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	require.False(t, core.Cacheable(stmt, is))

	limitStmt = &ast.Limit{}
	stmt = &ast.DeleteStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	require.True(t, core.Cacheable(stmt, is))

	stmt.(*ast.DeleteStmt).TableHints = append(stmt.(*ast.DeleteStmt).TableHints, &ast.TableOptimizerHint{
		HintName: model.NewCIStr(core.HintIgnorePlanCache),
	})
	require.False(t, core.Cacheable(stmt, is))

	// test UpdateStmt
	whereExpr = &ast.FuncCallExpr{}
	stmt = &ast.UpdateStmt{
		TableRefs: tableRefsClause,
		Where:     whereExpr,
	}
	require.True(t, core.Cacheable(stmt, is))

	for funcName := range expression.UnCacheableFunctions {
		whereExpr.FnName = model.NewCIStr(funcName)
		require.False(t, core.Cacheable(stmt, is))
	}

	whereExpr.FnName = model.NewCIStr(ast.Rand)
	require.True(t, core.Cacheable(stmt, is))

	stmt = &ast.UpdateStmt{
		TableRefs: tableRefsClause,
		Where:     &ast.ExistsSubqueryExpr{},
	}
	require.False(t, core.Cacheable(stmt, is))

	limitStmt = &ast.Limit{
		Count: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.UpdateStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	require.False(t, core.Cacheable(stmt, is))

	limitStmt = &ast.Limit{
		Offset: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.UpdateStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	require.False(t, core.Cacheable(stmt, is))

	limitStmt = &ast.Limit{}
	stmt = &ast.UpdateStmt{
		TableRefs: tableRefsClause,
		Limit:     limitStmt,
	}
	require.True(t, core.Cacheable(stmt, is))

	stmt.(*ast.UpdateStmt).TableHints = append(stmt.(*ast.UpdateStmt).TableHints, &ast.TableOptimizerHint{
		HintName: model.NewCIStr(core.HintIgnorePlanCache),
	})
	require.False(t, core.Cacheable(stmt, is))

	// test SelectStmt
	whereExpr = &ast.FuncCallExpr{}
	stmt = &ast.SelectStmt{
		Where: whereExpr,
	}
	require.True(t, core.Cacheable(stmt, is))

	for funcName := range expression.UnCacheableFunctions {
		whereExpr.FnName = model.NewCIStr(funcName)
		require.False(t, core.Cacheable(stmt, is))
	}

	whereExpr.FnName = model.NewCIStr(ast.Rand)
	require.True(t, core.Cacheable(stmt, is))

	stmt = &ast.SelectStmt{
		Where: &ast.ExistsSubqueryExpr{},
	}
	require.False(t, core.Cacheable(stmt, is))

	limitStmt = &ast.Limit{
		Count: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.SelectStmt{
		Limit: limitStmt,
	}
	require.False(t, core.Cacheable(stmt, is))

	limitStmt = &ast.Limit{
		Offset: &driver.ParamMarkerExpr{},
	}
	stmt = &ast.SelectStmt{
		Limit: limitStmt,
	}
	require.False(t, core.Cacheable(stmt, is))

	limitStmt = &ast.Limit{}
	stmt = &ast.SelectStmt{
		Limit: limitStmt,
	}
	require.True(t, core.Cacheable(stmt, is))

	paramExpr := &driver.ParamMarkerExpr{}
	orderByClause := &ast.OrderByClause{Items: []*ast.ByItem{{Expr: paramExpr}}}
	stmt = &ast.SelectStmt{
		OrderBy: orderByClause,
	}
	require.False(t, core.Cacheable(stmt, is))

	valExpr := &driver.ValueExpr{}
	orderByClause = &ast.OrderByClause{Items: []*ast.ByItem{{Expr: valExpr}}}
	stmt = &ast.SelectStmt{
		OrderBy: orderByClause,
	}
	require.True(t, core.Cacheable(stmt, is))

	stmt.(*ast.SelectStmt).TableHints = append(stmt.(*ast.SelectStmt).TableHints, &ast.TableOptimizerHint{
		HintName: model.NewCIStr(core.HintIgnorePlanCache),
	})
	require.False(t, core.Cacheable(stmt, is))

	boundExpr := &ast.FrameBound{Expr: &driver.ParamMarkerExpr{}}
	require.False(t, core.Cacheable(boundExpr, is))

	// Partition table can not be cached.
	join := &ast.Join{
		Left:  &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t1")},
		Right: &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t2")},
	}
	stmt = &ast.SelectStmt{
		From: &ast.TableRefsClause{
			TableRefs: join,
		},
	}
	require.False(t, core.Cacheable(stmt, is))

	join = &ast.Join{
		Left: &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t3")},
	}
	stmt = &ast.SelectStmt{
		From: &ast.TableRefsClause{
			TableRefs: join,
		},
	}
	require.True(t, core.Cacheable(stmt, is))

}
<|endoftext|>


<commit_before>package specs

// Spec is the base configuration for the container.  It specifies platform
// independent configuration. This information must be included when the
// bundle is packaged for distribution.
type Spec struct {
	// Version is the version of the specification that is supported.
	Version string `json:"ociVersion"`
	// Platform is the host information for OS and Arch.
	Platform Platform `json:"platform"`
	// Process is the container's main process.
	Process Process `json:"process"`
	// Root is the root information for the container's filesystem.
	Root Root `json:"root"`
	// Hostname is the container's host name.
	Hostname string `json:"hostname,omitempty"`
	// Mounts profile configuration for adding mounts to the container's filesystem.
	Mounts []Mount `json:"mounts"`
	// Hooks are the commands run at various lifecycle events of the container.
	Hooks Hooks `json:"hooks"`
	// Annotations is an unstructured key value map that may be set by external tools to store and retrieve arbitrary metadata.
	Annotations map[string]string `json:"annotations,omitempty"`
}

// Process contains information to start a specific application inside the container.
type Process struct {
	// Terminal creates an interactive terminal for the container.
	Terminal bool `json:"terminal"`
	// User specifies user information for the process.
	User User `json:"user"`
	// Args specifies the binary and arguments for the application to execute.
	Args []string `json:"args"`
	// Env populates the process environment for the process.
	Env []string `json:"env,omitempty"`
	// Cwd is the current working directory for the process and must be
	// relative to the container's root.
	Cwd string `json:"cwd"`
	// Capabilities are linux capabilities that are kept for the container.
	Capabilities []string `json:"capabilities,omitempty"`
	// ApparmorProfile specified the apparmor profile for the container.
	ApparmorProfile string `json:"apparmorProfile,omitempty"`
	// SelinuxProcessLabel specifies the selinux context that the container process is run as.
	SelinuxLabel string `json:"selinuxLabel,omitempty"`
	// NoNewPrivileges controls whether additional privileges could be gained by processes in the container.
	NoNewPrivileges bool `json:"noNewPrivileges,omitempty"`
}

// Root contains information about the container's root filesystem on the host.
type Root struct {
	// Path is the absolute path to the container's root filesystem.
	Path string `json:"path"`
	// Readonly makes the root filesystem for the container readonly before the process is executed.
	Readonly bool `json:"readonly"`
}

// Platform specifies OS and arch information for the host system that the container
// is created for.
type Platform struct {
	// OS is the operating system.
	OS string `json:"os"`
	// Arch is the architecture
	Arch string `json:"arch"`
}

// Mount specifies a mount for a container.
type Mount struct {
	// Destination is the path where the mount will be placed relative to the container's root.  The path and child directories MUST exist, a runtime MUST NOT create directories automatically to a mount point.
	Destination string `json:"destination"`
	// Type specifies the mount kind.
	Type string `json:"type"`
	// Source specifies the source path of the mount.  In the case of bind mounts on
	// linux based systems this would be the file on the host.
	Source string `json:"source"`
	// Options are fstab style mount options.
	Options []string `json:"options,omitempty"`
}

// Hook specifies a command that is run at a particular event in the lifecycle of a container
type Hook struct {
	Path string   `json:"path"`
	Args []string `json:"args,omitempty"`
	Env  []string `json:"env,omitempty"`
}

// Hooks for container setup and teardown
type Hooks struct {
	// Prestart is a list of hooks to be run before the container process is executed.
	// On Linux, they are run after the container namespaces are created.
	Prestart []Hook `json:"prestart,omitempty"`
	// Poststart is a list of hooks to be run after the container process is started.
	Poststart []Hook `json:"poststart,omitempty"`
	// Poststop is a list of hooks to be run after the container process exits.
	Poststop []Hook `json:"poststop,omitempty"`
}
<commit_msg>specs-go/config: Fix 'SelinuxProcessLabel' -> 'SelinuxLabel'<commit_after>package specs

// Spec is the base configuration for the container.  It specifies platform
// independent configuration. This information must be included when the
// bundle is packaged for distribution.
type Spec struct {
	// Version is the version of the specification that is supported.
	Version string `json:"ociVersion"`
	// Platform is the host information for OS and Arch.
	Platform Platform `json:"platform"`
	// Process is the container's main process.
	Process Process `json:"process"`
	// Root is the root information for the container's filesystem.
	Root Root `json:"root"`
	// Hostname is the container's host name.
	Hostname string `json:"hostname,omitempty"`
	// Mounts profile configuration for adding mounts to the container's filesystem.
	Mounts []Mount `json:"mounts"`
	// Hooks are the commands run at various lifecycle events of the container.
	Hooks Hooks `json:"hooks"`
	// Annotations is an unstructured key value map that may be set by external tools to store and retrieve arbitrary metadata.
	Annotations map[string]string `json:"annotations,omitempty"`
}

// Process contains information to start a specific application inside the container.
type Process struct {
	// Terminal creates an interactive terminal for the container.
	Terminal bool `json:"terminal"`
	// User specifies user information for the process.
	User User `json:"user"`
	// Args specifies the binary and arguments for the application to execute.
	Args []string `json:"args"`
	// Env populates the process environment for the process.
	Env []string `json:"env,omitempty"`
	// Cwd is the current working directory for the process and must be
	// relative to the container's root.
	Cwd string `json:"cwd"`
	// Capabilities are linux capabilities that are kept for the container.
	Capabilities []string `json:"capabilities,omitempty"`
	// ApparmorProfile specified the apparmor profile for the container.
	ApparmorProfile string `json:"apparmorProfile,omitempty"`
	// SelinuxLabel specifies the selinux context that the container process is run as.
	SelinuxLabel string `json:"selinuxLabel,omitempty"`
	// NoNewPrivileges controls whether additional privileges could be gained by processes in the container.
	NoNewPrivileges bool `json:"noNewPrivileges,omitempty"`
}

// Root contains information about the container's root filesystem on the host.
type Root struct {
	// Path is the absolute path to the container's root filesystem.
	Path string `json:"path"`
	// Readonly makes the root filesystem for the container readonly before the process is executed.
	Readonly bool `json:"readonly"`
}

// Platform specifies OS and arch information for the host system that the container
// is created for.
type Platform struct {
	// OS is the operating system.
	OS string `json:"os"`
	// Arch is the architecture
	Arch string `json:"arch"`
}

// Mount specifies a mount for a container.
type Mount struct {
	// Destination is the path where the mount will be placed relative to the container's root.  The path and child directories MUST exist, a runtime MUST NOT create directories automatically to a mount point.
	Destination string `json:"destination"`
	// Type specifies the mount kind.
	Type string `json:"type"`
	// Source specifies the source path of the mount.  In the case of bind mounts on
	// linux based systems this would be the file on the host.
	Source string `json:"source"`
	// Options are fstab style mount options.
	Options []string `json:"options,omitempty"`
}

// Hook specifies a command that is run at a particular event in the lifecycle of a container
type Hook struct {
	Path string   `json:"path"`
	Args []string `json:"args,omitempty"`
	Env  []string `json:"env,omitempty"`
}

// Hooks for container setup and teardown
type Hooks struct {
	// Prestart is a list of hooks to be run before the container process is executed.
	// On Linux, they are run after the container namespaces are created.
	Prestart []Hook `json:"prestart,omitempty"`
	// Poststart is a list of hooks to be run after the container process is started.
	Poststart []Hook `json:"poststart,omitempty"`
	// Poststop is a list of hooks to be run after the container process exits.
	Poststop []Hook `json:"poststop,omitempty"`
}
<|endoftext|>


<commit_before>package spql

import (
	"log"
	"testing"
)

func TestParse(t *testing.T) {
	log.Printf("TestParser--")

	queryStr := `{
            "type": "create_database",
            "params": {
                "name": "testedb",
                "max_cache_size": "214141"
            }
    }`

	query, err := NewParser(queryStr).ParseQuery()
	if err != nil {
		log.Printf("ERROR: %s", err.Error())
		return
	}

	qp := query.Params.(*CreateDatabaseStmt)
	log.Printf("SUCCESS: %s", qp.Name)
}
<commit_msg>unused<commit_after><|endoftext|>


<commit_before>package jiracli

import (
	"fmt"

	"github.com/coryb/figtree"
	"github.com/coryb/oreo"

	jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
	"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
	kingpin "gopkg.in/alecthomas/kingpin.v2"
)

type CreateOptions struct {
	GlobalOptions        `yaml:",inline" json:",inline" figtree:",inline"`
	jiradata.IssueUpdate `yaml:",inline" json:",inline" figtree:",inline"`
	Project              string            `yaml:"project,omitempty" json:"project,omitempty"`
	IssueType            string            `yaml:"issuetype,omitempty" json:"issuetype,omitempty"`
	Overrides            map[string]string `yaml:"overrides,omitempty" json:"overrides,omitempty"`
}

func CmdCreateRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
	opts := CreateOptions{
		GlobalOptions: GlobalOptions{
			Template: figtree.NewStringOption("create"),
		},
		Overrides: map[string]string{},
	}

	return &CommandRegistryEntry{
		"Create issue",
		func() error {
			return CmdCreate(o, &opts)
		},
		func(cmd *kingpin.CmdClause) error {
			LoadConfigs(cmd, fig, &opts)
			return CmdCreateUsage(cmd, &opts)
		},
	}
}

func CmdCreateUsage(cmd *kingpin.CmdClause, opts *CreateOptions) error {
	if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
		return err
	}
	BrowseUsage(cmd, &opts.GlobalOptions)
	EditorUsage(cmd, &opts.GlobalOptions)
	TemplateUsage(cmd, &opts.GlobalOptions)
	cmd.Flag("noedit", "Disable opening the editor").SetValue(&opts.SkipEditing)
	cmd.Flag("project", "project to create issue in").Short('p').StringVar(&opts.Project)
	cmd.Flag("issuetype", "issuetype in to create").Short('i').StringVar(&opts.IssueType)
	cmd.Flag("comment", "Comment message for issue").Short('m').PreAction(func(ctx *kingpin.ParseContext) error {
		opts.Overrides["comment"] = flagValue(ctx, "comment")
		return nil
	}).String()
	cmd.Flag("override", "Set issue property").Short('o').StringMapVar(&opts.Overrides)
	return nil
}

// CmdCreate sends the create-metadata to the "create" template for editing, then
// will parse the edited document as YAML and submit the document to jira.
func CmdCreate(o *oreo.Client, opts *CreateOptions) error {
	type templateInput struct {
		Meta      *jiradata.IssueType `yaml:"meta" json:"meta"`
		Overrides map[string]string   `yaml:"overrides" json:"overrides"`
	}

	if err := defaultIssueType(o, opts.Endpoint.Value, &opts.Project, &opts.IssueType); err != nil {
		return err
	}
	createMeta, err := jira.GetIssueCreateMetaIssueType(o, opts.Endpoint.Value, opts.Project, opts.IssueType)
	if err != nil {
		return err
	}

	issueUpdate := jiradata.IssueUpdate{}
	input := templateInput{
		Meta:      createMeta,
		Overrides: opts.Overrides,
	}
	input.Overrides["project"] = opts.Project
	input.Overrides["issuetype"] = opts.IssueType
	input.Overrides["user"] = opts.User.Value

	var issueResp *jiradata.IssueCreateResponse
	err = editLoop(&opts.GlobalOptions, &input, &issueUpdate, func() error {
		issueResp, err = jira.CreateIssue(o, opts.Endpoint.Value, &issueUpdate)
		return err
	})
	if err != nil {
		return err
	}

	fmt.Printf("OK %s %s/browse/%s\n", issueResp.Key, opts.Endpoint.Value, issueResp.Key)

	if opts.Browse.Value {
		return CmdBrowse(&BrowseOptions{opts.GlobalOptions, issueResp.Key})
	}
	return nil
}

func defaultIssueType(o *oreo.Client, endpoint string, project, issuetype *string) error {
	if project == nil || *project == "" {
		return fmt.Errorf("Project undefined, please use --project argument or set the `project` config property")
	}
	if issuetype != nil && *issuetype != "" {
		return nil
	}
	projectMeta, err := jira.GetIssueCreateMetaProject(o, endpoint, *project)
	if err != nil {
		return err
	}

	issueTypes := map[string]bool{}

	for _, issuetype := range projectMeta.IssueTypes {
		issueTypes[issuetype.Name] = true
	}

	//  prefer "Bug" type
	if _, ok := issueTypes["Bug"]; ok {
		*issuetype = "Bug"
		return nil
	}
	// next best default it "Task"
	if _, ok := issueTypes["Task"]; ok {
		*issuetype = "Task"
		return nil
	}

	return fmt.Errorf("Unable to find default issueType of Bug or Task, please set --issuetype argument or set the `issuetype` config property")
}
<commit_msg>add --saveFile for `create`<commit_after>package jiracli

import (
	"fmt"
	"os"

	"github.com/coryb/figtree"
	"github.com/coryb/oreo"

	jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
	"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
	kingpin "gopkg.in/alecthomas/kingpin.v2"
	yaml "gopkg.in/coryb/yaml.v2"
)

type CreateOptions struct {
	GlobalOptions        `yaml:",inline" json:",inline" figtree:",inline"`
	jiradata.IssueUpdate `yaml:",inline" json:",inline" figtree:",inline"`
	Project              string            `yaml:"project,omitempty" json:"project,omitempty"`
	IssueType            string            `yaml:"issuetype,omitempty" json:"issuetype,omitempty"`
	Overrides            map[string]string `yaml:"overrides,omitempty" json:"overrides,omitempty"`
	SaveFile             string            `yaml:"savefile,omitempty" json:"savefile,omitempty"`
}

func CmdCreateRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
	opts := CreateOptions{
		GlobalOptions: GlobalOptions{
			Template: figtree.NewStringOption("create"),
		},
		Overrides: map[string]string{},
	}

	return &CommandRegistryEntry{
		"Create issue",
		func() error {
			return CmdCreate(o, &opts)
		},
		func(cmd *kingpin.CmdClause) error {
			LoadConfigs(cmd, fig, &opts)
			return CmdCreateUsage(cmd, &opts)
		},
	}
}

func CmdCreateUsage(cmd *kingpin.CmdClause, opts *CreateOptions) error {
	if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
		return err
	}
	BrowseUsage(cmd, &opts.GlobalOptions)
	EditorUsage(cmd, &opts.GlobalOptions)
	TemplateUsage(cmd, &opts.GlobalOptions)
	cmd.Flag("noedit", "Disable opening the editor").SetValue(&opts.SkipEditing)
	cmd.Flag("project", "project to create issue in").Short('p').StringVar(&opts.Project)
	cmd.Flag("issuetype", "issuetype in to create").Short('i').StringVar(&opts.IssueType)
	cmd.Flag("comment", "Comment message for issue").Short('m').PreAction(func(ctx *kingpin.ParseContext) error {
		opts.Overrides["comment"] = flagValue(ctx, "comment")
		return nil
	}).String()
	cmd.Flag("override", "Set issue property").Short('o').StringMapVar(&opts.Overrides)
	cmd.Flag("saveFile", "Write issue as yaml to file").StringVar(&opts.SaveFile)
	return nil
}

// CmdCreate sends the create-metadata to the "create" template for editing, then
// will parse the edited document as YAML and submit the document to jira.
func CmdCreate(o *oreo.Client, opts *CreateOptions) error {
	type templateInput struct {
		Meta      *jiradata.IssueType `yaml:"meta" json:"meta"`
		Overrides map[string]string   `yaml:"overrides" json:"overrides"`
	}

	if err := defaultIssueType(o, opts.Endpoint.Value, &opts.Project, &opts.IssueType); err != nil {
		return err
	}
	createMeta, err := jira.GetIssueCreateMetaIssueType(o, opts.Endpoint.Value, opts.Project, opts.IssueType)
	if err != nil {
		return err
	}

	issueUpdate := jiradata.IssueUpdate{}
	input := templateInput{
		Meta:      createMeta,
		Overrides: opts.Overrides,
	}
	input.Overrides["project"] = opts.Project
	input.Overrides["issuetype"] = opts.IssueType
	input.Overrides["user"] = opts.User.Value

	var issueResp *jiradata.IssueCreateResponse
	err = editLoop(&opts.GlobalOptions, &input, &issueUpdate, func() error {
		issueResp, err = jira.CreateIssue(o, opts.Endpoint.Value, &issueUpdate)
		return err
	})
	if err != nil {
		return err
	}

	fmt.Printf("OK %s %s/browse/%s\n", issueResp.Key, opts.Endpoint.Value, issueResp.Key)

	if opts.SaveFile != "" {
		fh, err := os.Create(opts.SaveFile)
		if err != nil {
			return err
		}
		defer fh.Close()
		out, err := yaml.Marshal(map[string]string{
			"issue": issueResp.Key,
			"link":  fmt.Sprintf("%s/browse/%s", opts.Endpoint.Value, issueResp.Key),
		})
		if err != nil {
			return err
		}
		fh.Write(out)
	}

	if opts.Browse.Value {
		return CmdBrowse(&BrowseOptions{opts.GlobalOptions, issueResp.Key})
	}
	return nil
}

func defaultIssueType(o *oreo.Client, endpoint string, project, issuetype *string) error {
	if project == nil || *project == "" {
		return fmt.Errorf("Project undefined, please use --project argument or set the `project` config property")
	}
	if issuetype != nil && *issuetype != "" {
		return nil
	}
	projectMeta, err := jira.GetIssueCreateMetaProject(o, endpoint, *project)
	if err != nil {
		return err
	}

	issueTypes := map[string]bool{}

	for _, issuetype := range projectMeta.IssueTypes {
		issueTypes[issuetype.Name] = true
	}

	//  prefer "Bug" type
	if _, ok := issueTypes["Bug"]; ok {
		*issuetype = "Bug"
		return nil
	}
	// next best default it "Task"
	if _, ok := issueTypes["Task"]; ok {
		*issuetype = "Task"
		return nil
	}

	return fmt.Errorf("Unable to find default issueType of Bug or Task, please set --issuetype argument or set the `issuetype` config property")
}
<|endoftext|>