<commit_before>package main

import ("github.com/nsf/termbox-go";"time";"strings")

// unicode constants
const (
	v = '\u2502'
	h = '\u2500'
	box_ul = '\u256d'
	box_ur = '\u256e'
	box_ll = '\u2570'
	box_lr = '\u256f'
	name = " SAMAYAM "
	FG = termbox.ColorRed
	BG = termbox.ColorBlack
)

type BOX struct {

	width,height int
	x_start,y_start int

}

type TASK struct {

	task string
	start time.Time
	end time.Time
}

func draw_string_box(fill string,x,y,padding int){

	actual_width := len(fill) + 2*padding
	actual_height :=  1 + 2*padding
	actual_start_x := x + padding
	actual_start_y := y + padding

	for i,j := range fill {
		termbox.SetCell(actual_start_x+i,actual_start_y,j,FG,BG)
	}

	rect := BOX{ x_start:x,y_start:y,width:actual_width,height:actual_height}
	rect.draw_mainbox()
}

func draw_vertical(x,y,h int){

	for i := y ; i <= y+h-1 ; i++ {
		termbox.SetCell(x,i,v,FG,BG)
	}
}

func draw_horizontal(x,y,w int){

	for i := x ; i <= x+w-1 ; i++ {
		termbox.SetCell(i,y,h,FG,BG)
	}
}

func box_filler(x,y,w int){
	// debug function
	a := [5] termbox.Attribute{ termbox.ColorRed,termbox.ColorGreen,termbox.ColorYellow }
	for i := x ; i <= x+w-1 ; i++ {
		termbox.SetCell(i,y,h,termbox.ColorWhite,a[i%3])
	}
}

func (r *BOX) draw_mainbox(){

	termbox.SetCell(r.x_start,r.y_start,box_ul,FG,BG)
	draw_horizontal(r.x_start+1,r.y_start,r.width-2)
	termbox.SetCell(r.x_start+r.width-1,r.y_start,box_ur,FG,BG)

	termbox.SetCell(r.x_start,r.y_start+r.height-1,box_ll,FG,BG)
	draw_horizontal(r.x_start+1,r.y_start+r.height-1,r.width-2)
	termbox.SetCell(r.x_start+r.width-1,r.y_start+r.height-1,box_lr,FG,BG)

	draw_vertical(r.x_start,r.y_start+1,r.height-2)
	draw_vertical(r.x_start+r.width-1,r.y_start+1,r.height-2)

}

func ( t *TASK) draw_task (x,y,padding int){
	s := []string{t.task,t.start.String()}
	string_to_draw :=  strings.Join(s, " --- " )
	draw_string_box(string_to_draw,x,y,padding )

}

func main(){

	errit := termbox.Init()
	if errit != nil {
		panic(" Trouble somewhere")
	} else {
		//x := BOX{width:20,x_start:3,y_start:3,height:20}
		//x.draw_mainbox()
		task_1 := TASK{task:" finish samayam",start:time.Now()}
		task_1.draw_task(10,10,2)
		termbox.Flush()
		time.Sleep(10*time.Second)
	}
	defer termbox.Close()

}

<commit_msg>static tree illustration done<commit_after>package main

import ("github.com/nsf/termbox-go";"time";"strings")

// unicode constants
const (
	v = '\u2502'
	h = '\u2500'
	box_ul = '\u256d'
	box_ur = '\u256e'
	box_ll = '\u2570'
	box_lr = '\u256f'
	name = " SAMAYAM "
	FG = termbox.ColorRed
	BG = termbox.ColorBlack
	H_SPACE = 1
	H_LEN =5
	V_SPACE = 2
	V_LEN =5
)

type BOX struct {

	width,height int
	x_start,y_start int

}

type TASK struct {

	task string
	start time.Time
	end time.Time
}

type TASK_TREE struct {

	tree []TASK

}

func draw_string_box(fill string,x,y,padding int){

	actual_width := len(fill) + 2*padding
	actual_height :=  1 + 2*padding
	actual_start_x := x + padding
	actual_start_y := y + padding

	for i,j := range fill {
		termbox.SetCell(actual_start_x+i,actual_start_y,j,FG,BG)
	}

	rect := BOX{ x_start:x,y_start:y,width:actual_width,height:actual_height}
	rect.draw_mainbox()
}

func draw_vertical(x,y,h int){

	for i := y ; i <= y+h-1 ; i++ {
		termbox.SetCell(x,i,v,FG,BG)
	}
}

func draw_horizontal(x,y,w int){

	for i := x ; i <= x+w-1 ; i++ {
		termbox.SetCell(i,y,h,FG,BG)
	}
}

func box_filler(x,y,w int){
	// debug function
	a := [5] termbox.Attribute{ termbox.ColorRed,termbox.ColorGreen,termbox.ColorYellow }
	for i := x ; i <= x+w-1 ; i++ {
		termbox.SetCell(i,y,h,termbox.ColorWhite,a[i%3])
	}
}

func (r *BOX) draw_mainbox(){

	termbox.SetCell(r.x_start,r.y_start,box_ul,FG,BG)
	draw_horizontal(r.x_start+1,r.y_start,r.width-2)
	termbox.SetCell(r.x_start+r.width-1,r.y_start,box_ur,FG,BG)

	termbox.SetCell(r.x_start,r.y_start+r.height-1,box_ll,FG,BG)
	draw_horizontal(r.x_start+1,r.y_start+r.height-1,r.width-2)
	termbox.SetCell(r.x_start+r.width-1,r.y_start+r.height-1,box_lr,FG,BG)

	draw_vertical(r.x_start,r.y_start+1,r.height-2)
	draw_vertical(r.x_start+r.width-1,r.y_start+1,r.height-2)

}

func ( t *TASK) draw_task (x,y,padding int){
	s := []string{t.task,t.start.String()}
	string_to_draw :=  strings.Join(s, "    " )
	draw_string_box(string_to_draw,x,y,padding )

}

func ( t* TASK_TREE) draw_tree(){

	//length := len(t.tree)
	for i,j :=range(t.tree){
		box_x := H_SPACE + H_LEN
		box_y := V_SPACE + i*V_LEN
		j.draw_task(box_x,box_y,1)
		draw_horizontal(H_SPACE,box_y+1,H_LEN)
		if i>=1 {
			draw_vertical(H_SPACE,box_y+1-V_LEN,V_LEN)
		}
	}

}

func main(){

	errit := termbox.Init()
	if errit != nil {
		panic(" Trouble somewhere")
	} else {
		//x := BOX{width:20,x_start:3,y_start:3,height:20}
		//x.draw_mainbox()
		for  {
			task_1 := TASK_TREE{tree:[]TASK{  TASK{task:" take meeko for a walk",start:time.Now()}, TASK{task:" finish samayam",start:time.Now()}, TASK{task:" finish python",start:time.Now()}}}
			task_1.draw_tree()
			termbox.Flush()
			g := termbox.PollEvent()
			if g.Ch == 'z'{
				break
			}
			termbox.Clear(FG,BG)
		}
	}
	defer termbox.Close()

}

<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"
	"strings"

	"github.com/evandroflores/claimr/model"
	"github.com/shomali11/slacker"
)

var commands []model.Command

const (
	directMessagePrefix = "D"
	channelPrefix       = "<#C"
	userPrefix          = "<@U"
)

// Register add a command to commands list an prepare to register to slacker
func Register(usage string, description string, handler func(request *slacker.Request, response slacker.ResponseWriter)) {
	commands = append(commands, model.Command{Usage: usage, Description: description, Handler: handler})
}

// CommandList returns the list of registered commands
func CommandList() []model.Command {
	return commands
}

func notImplemented(request *slacker.Request, response slacker.ResponseWriter) {
	response.Reply(Messages["not-implemented"])
}

func isDirect(channelID string) (bool, error) {
	if strings.HasPrefix(strings.ToUpper(channelID), directMessagePrefix) {
		return true, fmt.Errorf(Messages["direct-not-allowed"])
	}
	return false, nil
}

func hasUserOnText(message string) (bool, error) {
	if strings.Contains(strings.ToUpper(message), userPrefix) {
		return true, fmt.Errorf(Messages["shouldnt-mention-user"])
	}
	return false, nil
}

func hasChannelOnText(message string) (bool, error) {
	if strings.Contains(strings.ToUpper(message), channelPrefix) {
		return true, fmt.Errorf(Messages["shouldnt-mention-channel"])
	}
	return false, nil
}

func getEvent(request *slacker.Request) ClaimrEvent {
	if request == nil {
		return ClaimrEvent{}
	}
	return ClaimrEvent{
		Team:    request.Event.Team,
		Channel: request.Event.Channel,
		User:    request.Event.User,
	}
}

// ClaimrEvent is a struct to simplify the usage of request.Event (and help testing)
type ClaimrEvent struct {
	Team    string
	Channel string
	User    string
}

// GetEventText exists to help testing event message
func GetEventText(request *slacker.Request) string {
	return request.Event.Msg.Text
}

func isAdmin(userName string) bool {
	if strings.ToUpper(userName) == strings.ToUpper(os.Getenv("CLAIMR_SUPERUSER")) {
		return true
	}

	for _, admin := range model.Admins {
		if strings.ToUpper(userName) == strings.ToUpper(admin.ID) {
			return true
		}
	}

	return false
}

type Check struct {
	isPositive bool
	message    string
}
<commit_msg>Adding export string documentation<commit_after>package cmd

import (
	"fmt"
	"os"
	"strings"

	"github.com/evandroflores/claimr/model"
	"github.com/shomali11/slacker"
)

var commands []model.Command

const (
	directMessagePrefix = "D"
	channelPrefix       = "<#C"
	userPrefix          = "<@U"
)

// Register add a command to commands list an prepare to register to slacker
func Register(usage string, description string, handler func(request *slacker.Request, response slacker.ResponseWriter)) {
	commands = append(commands, model.Command{Usage: usage, Description: description, Handler: handler})
}

// CommandList returns the list of registered commands
func CommandList() []model.Command {
	return commands
}

func notImplemented(request *slacker.Request, response slacker.ResponseWriter) {
	response.Reply(Messages["not-implemented"])
}

func isDirect(channelID string) (bool, error) {
	if strings.HasPrefix(strings.ToUpper(channelID), directMessagePrefix) {
		return true, fmt.Errorf(Messages["direct-not-allowed"])
	}
	return false, nil
}

func hasUserOnText(message string) (bool, error) {
	if strings.Contains(strings.ToUpper(message), userPrefix) {
		return true, fmt.Errorf(Messages["shouldnt-mention-user"])
	}
	return false, nil
}

func hasChannelOnText(message string) (bool, error) {
	if strings.Contains(strings.ToUpper(message), channelPrefix) {
		return true, fmt.Errorf(Messages["shouldnt-mention-channel"])
	}
	return false, nil
}

func getEvent(request *slacker.Request) ClaimrEvent {
	if request == nil {
		return ClaimrEvent{}
	}
	return ClaimrEvent{
		Team:    request.Event.Team,
		Channel: request.Event.Channel,
		User:    request.Event.User,
	}
}

// ClaimrEvent is a struct to simplify the usage of request.Event (and help testing)
type ClaimrEvent struct {
	Team    string
	Channel string
	User    string
}

// GetEventText exists to help testing event message
func GetEventText(request *slacker.Request) string {
	return request.Event.Msg.Text
}

func isAdmin(userName string) bool {
	if strings.ToUpper(userName) == strings.ToUpper(os.Getenv("CLAIMR_SUPERUSER")) {
		return true
	}

	for _, admin := range model.Admins {
		if strings.ToUpper(userName) == strings.ToUpper(admin.ID) {
			return true
		}
	}

	return false
}

// Check is a struct to help grouping checks before command execution
type Check struct {
	isPositive bool
	message    string
}
<|endoftext|>


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

package cmd

import (
	"fmt"

	"github.com/spf13/cobra"
)

// getCmd represents the get command
var getCmd = &cobra.Command{
	Use:   "get",
	Short: "Get full details of a single droplet",
	Long:  `The get subcommand retrieves all related information for a given droplet`,
	Run: func(cmd *cobra.Command, args []string) {
		// TODO: Work your own magic here
		//if len(args) != 1 {
		//	fmt.Println("get called without droplet name")
		//} else {
		//	fmt.Println("get called with droplet name", args[0])
		//}
	},
}

func init() {
	dropletCmd.AddCommand(getCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// getCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// getCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
	getCmd.Flags().StringP("name", "n", "", "name of droplet to describe")

	getCmd.Flags().IntP("id", "i", 0, "id of droplet to describe")

}
<commit_msg>Get takes a string, converts it to the droplet ID and return all information around given droplet ID.<commit_after>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"errors"
	"fmt"
	"os"

	"github.com/digitalocean/godo"
	"github.com/jconard3/docore/client"
	"github.com/spf13/cobra"
)

// getCmd represents the get command
var getCmd = &cobra.Command{
	Use:   "get <droplet_name>",
	Short: "Get full details of a single droplet",
	Long:  `The get subcommand retrieves all related information for a given droplet described by the provided name.`,
	Run: func(cmd *cobra.Command, args []string) {
		// TODO: Work your own magic here
		if len(args) < 1 {
			fmt.Println("No droplet name provided in command arguments. Aborting")
			os.Exit(-1)
		}
		droplet_name := args[0]

		my_client, err := client.CreateClient()
		if err != nil {
			fmt.Println(err)
			fmt.Println("Error creating digitalocean client. Aborting.\n")
			os.Exit(-1)
		}

		my_droplet, err := GetDroplet(my_client, droplet_name)
		if err != nil {
			fmt.Println(err)
			fmt.Println("Error retriving droplet. Aborting")
			os.Exit(-1)
		}
		fmt.Println(my_droplet)
	},
}

func init() {
	dropletCmd.AddCommand(getCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// getCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// getCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
	//getCmd.Flags().StringP("name", "n", "", "name of droplet to describe")

	//getCmd.Flags().IntP("id", "i", 0, "id of droplet to describe")

}

func GetDroplet(client godo.Client, droplet_name string) (*godo.Droplet, error) {
	//Check which flag was given, if neither name nor id, error out w/ usage
	droplet_id, err := NameToID(client, droplet_name)
	if err != nil {
		return nil, err
	}

	droplet, _, err := client.Droplets.Get(droplet_id)
	return droplet, err
}

func NameToID(client godo.Client, droplet_name string) (int, error) {
	opt := &godo.ListOptions{
		Page:    1,
		PerPage: 25,
	}
	droplets, _, err := client.Droplets.List(opt)
	if err != nil {
		return 0, err
	}

	for _, element := range droplets {
		if element.Name == droplet_name {
			return element.ID, nil
		}
	}
	return 0, errors.New("No droplet matches for given name")
}
<|endoftext|>


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

package main

import (
	"fmt"
	"net"

	"github.com/containernetworking/cni/pkg/types"
	"github.com/romana/core/common"
	log "github.com/romana/rlog"
	"github.com/vishvananda/netlink"
)

// RomanaAddressManager describes functions that allow allocating and deallocating
// IP addresses from Romana.
type RomanaAddressManager interface {
	Allocate(NetConf, RomanaAllocatorPodDescription) (*net.IPNet, error)
	Deallocate(NetConf, string) error
}

// NewRomanaAddressManager returns structure that satisfies RomanaAddresManager,
// it allows multiple implementations.
func NewRomanaAddressManager(provider RomanaAddressManagerProvider) (RomanaAddressManager, error) {
	if provider == DefaultProvider {
		return DefaultAddressManager{}, nil
	}

	return nil, fmt.Errorf("Unknown provider type %s", provider)
}

type RomanaAddressManagerProvider string

// DefaultProvider allocates and deallocates IP addresses using rest requests
// to Romana IPAM.
const DefaultProvider RomanaAddressManagerProvider = "default"

// RomanaAllocatorPodDescription represents collection of parameters used to allocate IP address.
type RomanaAllocatorPodDescription struct {
	Name        string
	Hostname    string
	Namespace   string
	Labels      map[string]string
	Annotations map[string]string
}

// NetConf represents parameters CNI plugin receives via stdin.
type NetConf struct {
	types.NetConf
	KubernetesConfig string `json:"kubernetes_config"`
	RomanaRoot       string `json:"romana_root"`

	// Name of a current host in romana.
	// If omitted, current hostname will be used.
	RomanaHostName   string `json:"romana_host_name"`
	SegmentLabelName string `json:"segment_label_name"`
	TenantLabelName  string `json:"tenant_label_name"` // TODO for stas, we don't use it. May be it should go away.
	UseAnnotations   bool   `json:"use_annotattions"`
}

type DefaultAddressManager struct{}

func (DefaultAddressManager) Allocate(config NetConf, pod RomanaAllocatorPodDescription) (*net.IPNet, error) {
	// Discover pod segment.
	var segmentLabel string
	var ok bool
	if config.UseAnnotations {
		segmentLabel, ok = pod.Annotations[config.SegmentLabelName]
	} else {
		segmentLabel, ok = pod.Labels[config.SegmentLabelName]
	}
	if !ok {
		return nil, fmt.Errorf("Failed to discover segment label for a pod")
	}
	log.Infof("Discovered segment %s for a pod", segmentLabel)

	// Rest client config
	clientConfig := common.GetDefaultRestClientConfig(config.RomanaRoot, nil)
	client, err := common.NewRestClient(clientConfig)
	if err != nil {
		return nil, fmt.Errorf("Failed to reach romana root at %s, err=(%s)", config.RomanaRoot, err)
	}
	log.Infof("Created romana client %v", client)

	// Topology, find host id.
	hosts, err := client.ListHosts()
	if err != nil {
		return nil, fmt.Errorf("Failed to list romana hosts err=(%s)", err)
	}
	var currentHost common.Host
	for hostNum, host := range hosts {
		if host.Name == config.RomanaHostName {
			currentHost = hosts[hostNum]
			break
		}
	}
	if currentHost.Name == "" {
		return nil, fmt.Errorf("Failed to find romana host with name %s in romana database", config.RomanaHostName)
	}

	// Tenant and segemnt
	tenantUrl, err := client.GetServiceUrl("tenant")
	if err != nil {
		return nil, fmt.Errorf("Failed to discover tenant url from romana root err=(%s)", err)
	}
	tenantUrl += "/tenants"
	var tenants []common.Tenant
	err = client.Get(tenantUrl, &tenants)
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch romana tenants from %s, err=(%s)", tenantUrl, err)
	}
	var currentTenant common.Tenant
	for tenantNum, tenant := range tenants {
		if tenant.Name == pod.Namespace {
			currentTenant = tenants[tenantNum]
			break
		}
	}
	if currentTenant.Name == "" {
		return nil, fmt.Errorf("Failed to find romana tenant with name %s, err=(%s)", pod.Namespace, err)
	}
	var segments []common.Segment
	segmentsUrl := fmt.Sprintf("%s/%d/segments", tenantUrl, currentTenant.ID)
	err = client.Get(segmentsUrl, &segments)
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch segments from %s, err=(%s)", segmentsUrl, err)
	}
	var currentSegment common.Segment
	for segmentNum, segment := range segments {
		if segment.Name == segmentLabel {
			currentSegment = segments[segmentNum]
			break
		}
	}
	if currentSegment.Name == "" {
		return nil, fmt.Errorf("Failed to discover segment %s within tenant %s", pod.Namespace, segmentLabel)
	}
	log.Infof("Discovered tenant=%d, segment=%d, host=%d", currentTenant.ID, currentSegment.ID, currentHost.ID)

	// IPAM allocate
	ipamUrl, err := client.GetServiceUrl("ipam")
	if err != nil {
		return nil, fmt.Errorf("Failed to discover ipam url from romana root err=(%s)", err)
	}
	ipamUrl += "/endpoints"
	var ipamReq, ipamResp common.IPAMEndpoint
	ipamReq = common.IPAMEndpoint{
		TenantID:  fmt.Sprintf("%d", currentTenant.ID),
		SegmentID: fmt.Sprintf("%d", currentSegment.ID),
		HostId:    fmt.Sprintf("%d", currentHost.ID),
		Name:      pod.Name,
	}
	err = client.Post(ipamUrl, &ipamReq, &ipamResp)
	if err != nil {
		return nil, fmt.Errorf("Failed to allocate IP address for a pod %s.%s, err=(%s)", pod.Namespace, currentTenant.Name, err)
	}
	log.Infof("Allocated IP address %s", ipamResp.Ip)
	ipamIP, err := netlink.ParseIPNet(ipamResp.Ip + "/32")
	if err != nil {
		return nil, fmt.Errorf("Failed to parse IP address %s, err=(%s)", ipamResp.Ip, err)
	}

	return ipamIP, nil
}

func (DefaultAddressManager) Deallocate(config NetConf, targetName string) error {
	// Rest client config
	clientConfig := common.GetDefaultRestClientConfig(config.RomanaRoot, nil)
	client, err := common.NewRestClient(clientConfig)
	if err != nil {
		return fmt.Errorf("Failed to reach romana root at %s, err=(%s)", config.RomanaRoot, err)
	}
	log.Infof("Created romana client %v", client)

	ipamUrl, err := client.GetServiceUrl("ipam")
	if err != nil {
		return fmt.Errorf("Failed to discover ipam url from romana root err=(%s)", err)
	}
	ipamUrl += "/endpoints"

	var endpoints, podEndpoints []common.IPAMEndpoint
	err = client.Get(ipamUrl, &endpoints)
	if err != nil {
		return fmt.Errorf("Failed to fetch ipam endpoints, err=(%s)", err)
	}

	for eNum, endpoint := range endpoints {
		if endpoint.Name == targetName && endpoint.InUse {
			podEndpoints = append(podEndpoints, endpoints[eNum])
		}
	}

	if len(podEndpoints) == 0 {
		return fmt.Errorf("No IPAM endpoints found for pod %s", targetName)
	}

	if len(podEndpoints) > 1 {
		return fmt.Errorf("Multiple IPAM endpoints found for pod %s, not supported", targetName)
	}

	endpointDeleteUrl := fmt.Sprintf("%s/%s", ipamUrl, podEndpoints[0].Ip)
	err = client.Delete(endpointDeleteUrl, nil, nil)
	if err != nil {
		return fmt.Errorf("Failed to delete IPAM endpoint %v", podEndpoints[0])
	}

	return nil
}
<commit_msg>Fix issue in CNI plugin where default segment not created<commit_after>// Copyright (c) 2017 Pani Networks
// All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package main

import (
	"fmt"
	"net"

	"github.com/containernetworking/cni/pkg/types"
	"github.com/romana/core/common"
	log "github.com/romana/rlog"
	"github.com/vishvananda/netlink"
)

const DefaultSegmentName = "default"

// RomanaAddressManager describes functions that allow allocating and deallocating
// IP addresses from Romana.
type RomanaAddressManager interface {
	Allocate(NetConf, RomanaAllocatorPodDescription) (*net.IPNet, error)
	Deallocate(NetConf, string) error
}

// NewRomanaAddressManager returns structure that satisfies RomanaAddresManager,
// it allows multiple implementations.
func NewRomanaAddressManager(provider RomanaAddressManagerProvider) (RomanaAddressManager, error) {
	if provider == DefaultProvider {
		return DefaultAddressManager{}, nil
	}

	return nil, fmt.Errorf("Unknown provider type %s", provider)
}

type RomanaAddressManagerProvider string

// DefaultProvider allocates and deallocates IP addresses using rest requests
// to Romana IPAM.
const DefaultProvider RomanaAddressManagerProvider = "default"

// RomanaAllocatorPodDescription represents collection of parameters used to allocate IP address.
type RomanaAllocatorPodDescription struct {
	Name        string
	Hostname    string
	Namespace   string
	Labels      map[string]string
	Annotations map[string]string
}

// NetConf represents parameters CNI plugin receives via stdin.
type NetConf struct {
	types.NetConf
	KubernetesConfig string `json:"kubernetes_config"`
	RomanaRoot       string `json:"romana_root"`

	// Name of a current host in romana.
	// If omitted, current hostname will be used.
	RomanaHostName   string `json:"romana_host_name"`
	SegmentLabelName string `json:"segment_label_name"`
	TenantLabelName  string `json:"tenant_label_name"` // TODO for stas, we don't use it. May be it should go away.
	UseAnnotations   bool   `json:"use_annotattions"`
}

type DefaultAddressManager struct{}

func (DefaultAddressManager) Allocate(config NetConf, pod RomanaAllocatorPodDescription) (*net.IPNet, error) {
	// Discover pod segment.
	var segmentLabel string
	var ok bool
	if config.UseAnnotations {
		segmentLabel, ok = pod.Annotations[config.SegmentLabelName]
	} else {
		segmentLabel, ok = pod.Labels[config.SegmentLabelName]
	}
	if !ok {
		log.Warnf("Failed to discover segment label for a pod, using %s", DefaultSegmentName)
		segmentLabel = DefaultSegmentName
	}
	log.Infof("Discovered segment %s for a pod", segmentLabel)

	// Rest client config
	clientConfig := common.GetDefaultRestClientConfig(config.RomanaRoot, nil)
	client, err := common.NewRestClient(clientConfig)
	if err != nil {
		return nil, fmt.Errorf("Failed to reach romana root at %s, err=(%s)", config.RomanaRoot, err)
	}
	log.Infof("Created romana client %v", client)

	// Topology, find host id.
	hosts, err := client.ListHosts()
	if err != nil {
		return nil, fmt.Errorf("Failed to list romana hosts err=(%s)", err)
	}
	var currentHost common.Host
	for hostNum, host := range hosts {
		if host.Name == config.RomanaHostName {
			currentHost = hosts[hostNum]
			break
		}
	}
	if currentHost.Name == "" {
		return nil, fmt.Errorf("Failed to find romana host with name %s in romana database", config.RomanaHostName)
	}

	// Tenant and segemnt
	tenantUrl, err := client.GetServiceUrl("tenant")
	if err != nil {
		return nil, fmt.Errorf("Failed to discover tenant url from romana root err=(%s)", err)
	}
	tenantUrl += "/tenants"
	var tenants []common.Tenant
	err = client.Get(tenantUrl, &tenants)
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch romana tenants from %s, err=(%s)", tenantUrl, err)
	}
	var currentTenant common.Tenant
	for tenantNum, tenant := range tenants {
		if tenant.Name == pod.Namespace {
			currentTenant = tenants[tenantNum]
			break
		}
	}
	if currentTenant.Name == "" {
		return nil, fmt.Errorf("Failed to find romana tenant with name %s, err=(%s)", pod.Namespace, err)
	}

	currentSegment, err := getOrCreateSegment(client, segmentLabel, tenantUrl, currentTenant.ID)
	if err != nil {
		return nil, fmt.Errorf("Failed to discover segment %s within tenant %s", pod.Namespace, segmentLabel)
	}
	log.Infof("Discovered tenant=%d, segment=%d, host=%d", currentTenant.ID, currentSegment.ID, currentHost.ID)

	// IPAM allocate
	ipamUrl, err := client.GetServiceUrl("ipam")
	if err != nil {
		return nil, fmt.Errorf("Failed to discover ipam url from romana root err=(%s)", err)
	}
	ipamUrl += "/endpoints"
	var ipamReq, ipamResp common.IPAMEndpoint
	ipamReq = common.IPAMEndpoint{
		TenantID:  fmt.Sprintf("%d", currentTenant.ID),
		SegmentID: fmt.Sprintf("%d", currentSegment.ID),
		HostId:    fmt.Sprintf("%d", currentHost.ID),
		Name:      pod.Name,
	}
	err = client.Post(ipamUrl, &ipamReq, &ipamResp)
	if err != nil {
		return nil, fmt.Errorf("Failed to allocate IP address for a pod %s.%s, err=(%s)", pod.Namespace, currentTenant.Name, err)
	}
	log.Infof("Allocated IP address %s", ipamResp.Ip)
	ipamIP, err := netlink.ParseIPNet(ipamResp.Ip + "/32")
	if err != nil {
		return nil, fmt.Errorf("Failed to parse IP address %s, err=(%s)", ipamResp.Ip, err)
	}

	return ipamIP, nil
}

func (DefaultAddressManager) Deallocate(config NetConf, targetName string) error {
	// Rest client config
	clientConfig := common.GetDefaultRestClientConfig(config.RomanaRoot, nil)
	client, err := common.NewRestClient(clientConfig)
	if err != nil {
		return fmt.Errorf("Failed to reach romana root at %s, err=(%s)", config.RomanaRoot, err)
	}
	log.Infof("Created romana client %v", client)

	ipamUrl, err := client.GetServiceUrl("ipam")
	if err != nil {
		return fmt.Errorf("Failed to discover ipam url from romana root err=(%s)", err)
	}
	ipamUrl += "/endpoints"

	var endpoints, podEndpoints []common.IPAMEndpoint
	err = client.Get(ipamUrl, &endpoints)
	if err != nil {
		return fmt.Errorf("Failed to fetch ipam endpoints, err=(%s)", err)
	}

	for eNum, endpoint := range endpoints {
		if endpoint.Name == targetName && endpoint.InUse {
			podEndpoints = append(podEndpoints, endpoints[eNum])
		}
	}

	if len(podEndpoints) == 0 {
		return fmt.Errorf("No IPAM endpoints found for pod %s", targetName)
	}

	if len(podEndpoints) > 1 {
		return fmt.Errorf("Multiple IPAM endpoints found for pod %s, not supported", targetName)
	}

	endpointDeleteUrl := fmt.Sprintf("%s/%s", ipamUrl, podEndpoints[0].Ip)
	err = client.Delete(endpointDeleteUrl, nil, nil)
	if err != nil {
		return fmt.Errorf("Failed to delete IPAM endpoint %v", podEndpoints[0])
	}

	return nil
}

func getOrCreateSegment(client *common.RestClient, segmentLabel, tenantUrl string, tenantId uint64) (*common.Segment, error) {
	var segments []common.Segment
	segmentsUrl := fmt.Sprintf("%s/%d/segments", tenantUrl, tenantId)
	err := client.Get(segmentsUrl, &segments)
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch segments from %s, err=(%s)", segmentsUrl, err)
	}

	for segmentNum, segment := range segments {
		if segment.Name == segmentLabel {
			return &segments[segmentNum], nil
		}
	}

	log.Errorf("no segment %s found, trying to create", segmentLabel)

	var newSegment common.Segment
	err = client.Post(segmentsUrl, common.Segment{Name: segmentLabel}, &newSegment)
	if err != nil {
		return nil, fmt.Errorf("failed to create segment %s, err=(%s)", segmentsUrl, err)
	}

	log.Infof("new segment created %v for tenant with id %d", newSegment, tenantId)

	return &newSegment, nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Microsoft. All rights reserved.
// MIT License

package cns

// Container Network Service remote API Contract
const (
	SetEnvironmentPath             = "/network/environment"
	CreateNetworkPath              = "/network/create"
	DeleteNetworkPath              = "/network/delete"
	ReserveIPAddressPath           = "/network/ip/reserve"
	ReleaseIPAddressPath           = "/network/ip/release"
	GetHostLocalIPPath             = "/network/ip/hostlocal"
	GetIPAddressUtilizationPath    = "/network/ip/utilization"
	GetUnhealthyIPAddressesPath    = "/network/ipaddresses/unhealthy"
	GetHealthReportPath            = "/network/health"
	V1Prefix                       = "/v0.1"
	V2Prefix                       = "/v0.2"
	CreateOrUpdateNetworkContainer = "/network/createorupdatenetworkcontainer"
	DeleteNetworkContainer         = "/network/deletenetworkcontainer"
	GetNetworkContainerStatus      = "/network/getnetworkcontainerstatus"
	GetInterfaceForContainer       = "/network/getinterfaceforcontainer"
)

// SetEnvironmentRequest describes the Request to set the environment in CNS.
type SetEnvironmentRequest struct {
	Location    string
	NetworkType string
}

// OverlayConfiguration describes configuration for all the nodes that are part of overlay.
type OverlayConfiguration struct {
	NodeCount     int
	LocalNodeIP   string
	OverlaySubent Subnet
	NodeConfig    []NodeConfiguration
}

// CreateNetworkRequest describes request to create the network.
type CreateNetworkRequest struct {
	NetworkName          string
	OverlayConfiguration OverlayConfiguration
	Options              map[string]interface{}
}

// DeleteNetworkRequest describes request to delete the network.
type DeleteNetworkRequest struct {
	NetworkName string
}

// ReserveIPAddressRequest describes request to reserve an IP Address
type ReserveIPAddressRequest struct {
	ReservationID string
}

// ReserveIPAddressResponse describes response to reserve an IP address.
type ReserveIPAddressResponse struct {
	Response  Response
	IPAddress string
}

// ReleaseIPAddressRequest describes request to release an IP Address.
type ReleaseIPAddressRequest struct {
	ReservationID string
}

// IPAddressesUtilizationResponse describes response for ip address utilization.
type IPAddressesUtilizationResponse struct {
	Response  Response
	Available int
	Reserved  int
	Unhealthy int
}

// GetIPAddressesResponse describes response containing requested ip addresses.
type GetIPAddressesResponse struct {
	Response    Response
	IPAddresses []string
}

// HostLocalIPAddressResponse describes reponse that returns the host local IP Address.
type HostLocalIPAddressResponse struct {
	Response  Response
	IPAddress string
}

// Subnet contains the ip address and the number of bits in prefix.
type Subnet struct {
	IPAddress    string
	PrefixLength int
}

// NodeConfiguration describes confguration for a node in overlay network.
type NodeConfiguration struct {
	NodeIP     string
	NodeID     string
	NodeSubnet Subnet
}

// Response describes generic response from CNS.
type Response struct {
	ReturnCode int
	Message    string
}

// OptionMap describes generic options that can be passed to CNS.
type OptionMap map[string]interface{}

// Response to a failed request.
type errorResponse struct {
	Err string
}

// CreateNetworkContainerRequest specifies request to create a network container or network isolation boundary.
type CreateNetworkContainerRequest struct {
	Version                    string
	NetworkContainerType       string
	NetworkContainerid         string // Mandatory input.
	PrimaryInterfaceIdentifier string // Primary CA.
	AuthorizationToken         string
	IPConfiguration            IPConfiguration
	MultiTenancyInfo           MultiTenancyInfo
	VnetAddressSpace           []IPSubnet // To setup SNAT (should include service endpoint vips).
	Routes                     []Route
}

// MultiTenancyInfo contains encap type and id.
type MultiTenancyInfo struct {
	EncapType string
	ID        int // This can be vlanid, vxlanid, gre-key etc. (depends on EnacapType).
}

// IPConfiguration contains details about ip config to provision in the VM.
type IPConfiguration struct {
	IPSubnet         IPSubnet
	DNSServers       []string
	GatewayIPAddress string
}

// IPSubnet contains ip subnet.
type IPSubnet struct {
	IPAddress    string
	PrefixLength uint8
}

// Route describes an entry in routing table.
type Route struct {
	IPAddress        string
	GatewayIPAddress string
	InterfaceToUse   string
}

// CreateNetworkContainerResponse specifies response of creating a network container.
type CreateNetworkContainerResponse struct {
	Response Response
}

// GetNetworkContainerStatusRequest specifies the details about the request to retrieve status of a specifc network container.
type GetNetworkContainerStatusRequest struct {
	NetworkContainerid string
}

// GetNetworkContainerStatusResponse specifies response of retriving a network container status.
type GetNetworkContainerStatusResponse struct {
	NetworkContainerid string
	Version            string
	AzureHostVersion   string
	Response           Response
}

// GetNetworkContainerRequest specifies the details about the request to retrieve a specifc network container.
type GetNetworkContainerRequest struct {
}

// GetNetworkContainerResponse describes the response to retrieve a specifc network container.
type GetNetworkContainerResponse struct {
	Response Response
}

// DeleteNetworkContainerRequest specifies the details about the request to delete a specifc network container.
type DeleteNetworkContainerRequest struct {
	NetworkContainerid string
}

// DeleteNetworkContainerResponse describes the response to delete a specifc network container.
type DeleteNetworkContainerResponse struct {
	Response Response
}

// GetInterfaceForContainerRequest specifies the container ID for which interface needs to be identified.
type GetInterfaceForContainerRequest struct {
	NetworkContainerID string
}

// GetInterfaceForContainerResponse specifies the interface for a given container ID.
type GetInterfaceForContainerResponse struct {
	NetworkInterface NetworkInterface
	VnetAddressSpace []IPSubnet
	Response         Response
}

// NetworkInterface specifies the information that can be used to unquely identify an interface.
type NetworkInterface struct {
	Name      string
	IPAddress string
}
<commit_msg>Adding NetworkContainerOrchestratorInfo field to CreateNetworkContainerRequest.<commit_after>// Copyright 2017 Microsoft. All rights reserved.
// MIT License

package cns

// Container Network Service remote API Contract
const (
	SetEnvironmentPath             = "/network/environment"
	CreateNetworkPath              = "/network/create"
	DeleteNetworkPath              = "/network/delete"
	ReserveIPAddressPath           = "/network/ip/reserve"
	ReleaseIPAddressPath           = "/network/ip/release"
	GetHostLocalIPPath             = "/network/ip/hostlocal"
	GetIPAddressUtilizationPath    = "/network/ip/utilization"
	GetUnhealthyIPAddressesPath    = "/network/ipaddresses/unhealthy"
	GetHealthReportPath            = "/network/health"
	V1Prefix                       = "/v0.1"
	V2Prefix                       = "/v0.2"
	CreateOrUpdateNetworkContainer = "/network/createorupdatenetworkcontainer"
	DeleteNetworkContainer         = "/network/deletenetworkcontainer"
	GetNetworkContainerStatus      = "/network/getnetworkcontainerstatus"
	GetInterfaceForContainer       = "/network/getinterfaceforcontainer"
)

// SetEnvironmentRequest describes the Request to set the environment in CNS.
type SetEnvironmentRequest struct {
	Location    string
	NetworkType string
}

// OverlayConfiguration describes configuration for all the nodes that are part of overlay.
type OverlayConfiguration struct {
	NodeCount     int
	LocalNodeIP   string
	OverlaySubent Subnet
	NodeConfig    []NodeConfiguration
}

// CreateNetworkRequest describes request to create the network.
type CreateNetworkRequest struct {
	NetworkName          string
	OverlayConfiguration OverlayConfiguration
	Options              map[string]interface{}
}

// DeleteNetworkRequest describes request to delete the network.
type DeleteNetworkRequest struct {
	NetworkName string
}

// ReserveIPAddressRequest describes request to reserve an IP Address
type ReserveIPAddressRequest struct {
	ReservationID string
}

// ReserveIPAddressResponse describes response to reserve an IP address.
type ReserveIPAddressResponse struct {
	Response  Response
	IPAddress string
}

// ReleaseIPAddressRequest describes request to release an IP Address.
type ReleaseIPAddressRequest struct {
	ReservationID string
}

// IPAddressesUtilizationResponse describes response for ip address utilization.
type IPAddressesUtilizationResponse struct {
	Response  Response
	Available int
	Reserved  int
	Unhealthy int
}

// GetIPAddressesResponse describes response containing requested ip addresses.
type GetIPAddressesResponse struct {
	Response    Response
	IPAddresses []string
}

// HostLocalIPAddressResponse describes reponse that returns the host local IP Address.
type HostLocalIPAddressResponse struct {
	Response  Response
	IPAddress string
}

// Subnet contains the ip address and the number of bits in prefix.
type Subnet struct {
	IPAddress    string
	PrefixLength int
}

// NodeConfiguration describes confguration for a node in overlay network.
type NodeConfiguration struct {
	NodeIP     string
	NodeID     string
	NodeSubnet Subnet
}

// Response describes generic response from CNS.
type Response struct {
	ReturnCode int
	Message    string
}

// OptionMap describes generic options that can be passed to CNS.
type OptionMap map[string]interface{}

// Response to a failed request.
type errorResponse struct {
	Err string
}

// CreateNetworkContainerRequest specifies request to create a network container or network isolation boundary.
type CreateNetworkContainerRequest struct {
	Version                          string
	NetworkContainerType             string
	NetworkContainerid               string // Mandatory input.
	PrimaryInterfaceIdentifier       string // Primary CA.
	AuthorizationToken               string
	NetworkContainerOrchestratorInfo NetworkContainerOrchestratorInfo
	IPConfiguration                  IPConfiguration
	MultiTenancyInfo                 MultiTenancyInfo
	VnetAddressSpace                 []IPSubnet // To setup SNAT (should include service endpoint vips).
	Routes                           []Route
}

// NetworkContainerOrchestratorInfo contains orchestrator type, pod name and pod namespace.
type NetworkContainerOrchestratorInfo struct {
	OrchestratorType string
	Podname          string
	PodNamespace     string
}

// MultiTenancyInfo contains encap type and id.
type MultiTenancyInfo struct {
	EncapType string
	ID        int // This can be vlanid, vxlanid, gre-key etc. (depends on EnacapType).
}

// IPConfiguration contains details about ip config to provision in the VM.
type IPConfiguration struct {
	IPSubnet         IPSubnet
	DNSServers       []string
	GatewayIPAddress string
}

// IPSubnet contains ip subnet.
type IPSubnet struct {
	IPAddress    string
	PrefixLength uint8
}

// Route describes an entry in routing table.
type Route struct {
	IPAddress        string
	GatewayIPAddress string
	InterfaceToUse   string
}

// CreateNetworkContainerResponse specifies response of creating a network container.
type CreateNetworkContainerResponse struct {
	Response Response
}

// GetNetworkContainerStatusRequest specifies the details about the request to retrieve status of a specifc network container.
type GetNetworkContainerStatusRequest struct {
	NetworkContainerid string
}

// GetNetworkContainerStatusResponse specifies response of retriving a network container status.
type GetNetworkContainerStatusResponse struct {
	NetworkContainerid string
	Version            string
	AzureHostVersion   string
	Response           Response
}

// GetNetworkContainerRequest specifies the details about the request to retrieve a specifc network container.
type GetNetworkContainerRequest struct {
}

// GetNetworkContainerResponse describes the response to retrieve a specifc network container.
type GetNetworkContainerResponse struct {
	Response Response
}

// DeleteNetworkContainerRequest specifies the details about the request to delete a specifc network container.
type DeleteNetworkContainerRequest struct {
	NetworkContainerid string
}

// DeleteNetworkContainerResponse describes the response to delete a specifc network container.
type DeleteNetworkContainerResponse struct {
	Response Response
}

// GetInterfaceForContainerRequest specifies the container ID for which interface needs to be identified.
type GetInterfaceForContainerRequest struct {
	NetworkContainerID string
}

// GetInterfaceForContainerResponse specifies the interface for a given container ID.
type GetInterfaceForContainerResponse struct {
	NetworkInterface NetworkInterface
	VnetAddressSpace []IPSubnet
	Response         Response
}

// NetworkInterface specifies the information that can be used to unquely identify an interface.
type NetworkInterface struct {
	Name      string
	IPAddress string
}
<|endoftext|>


<commit_before>package azurerm

import (
	"fmt"
	"log"
	"regexp"
	"strings"

	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmPublicIp() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmPublicIpCreate,
		Read:   resourceArmPublicIpRead,
		Update: resourceArmPublicIpCreate,
		Delete: resourceArmPublicIpDelete,
		Importer: &schema.ResourceImporter{
			State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
				id, err := parseAzureResourceID(d.Id())
				if err != nil {
					return nil, err
				}
				name := id.Path["publicIPAddresses"]
				if name == "" {
					return nil, fmt.Errorf("Error parsing supplied resource id. Please check it and rerun:\n %s", d.Id())
				}
				return []*schema.ResourceData{d}, nil
			},
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": locationSchema(),

			"resource_group_name": resourceGroupNameSchema(),

			"public_ip_address_allocation": {
				Type:             schema.TypeString,
				Required:         true,
				ValidateFunc:     validatePublicIpAllocation,
				StateFunc:        ignoreCaseStateFunc,
				DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
			},

			"idle_timeout_in_minutes": {
				Type:     schema.TypeInt,
				Optional: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(int)
					if value < 4 || value > 30 {
						errors = append(errors, fmt.Errorf(
							"The idle timeout must be between 4 and 30 minutes"))
					}
					return
				},
			},

			"domain_name_label": {
				Type:         schema.TypeString,
				Optional:     true,
				ValidateFunc: validatePublicIpDomainNameLabel,
			},

			"reverse_fqdn": {
				Type:     schema.TypeString,
				Optional: true,
			},

			"fqdn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"ip_address": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmPublicIpCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient)
	publicIPClient := client.publicIPClient

	log.Printf("[INFO] preparing arguments for Azure ARM Public IP creation.")

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	tags := d.Get("tags").(map[string]interface{})

	properties := network.PublicIPAddressPropertiesFormat{
		PublicIPAllocationMethod: network.IPAllocationMethod(d.Get("public_ip_address_allocation").(string)),
	}

	dnl, hasDnl := d.GetOk("domain_name_label")
	rfqdn, hasRfqdn := d.GetOk("reverse_fqdn")

	if hasDnl || hasRfqdn {
		dnsSettings := network.PublicIPAddressDNSSettings{}

		if hasRfqdn {
			reverse_fqdn := rfqdn.(string)
			dnsSettings.ReverseFqdn = &reverse_fqdn
		}

		if hasDnl {
			domain_name_label := dnl.(string)
			dnsSettings.DomainNameLabel = &domain_name_label

		}

		properties.DNSSettings = &dnsSettings
	}

	if v, ok := d.GetOk("idle_timeout_in_minutes"); ok {
		idle_timeout := int32(v.(int))
		properties.IdleTimeoutInMinutes = &idle_timeout
	}

	publicIp := network.PublicIPAddress{
		Name:                            &name,
		Location:                        &location,
		PublicIPAddressPropertiesFormat: &properties,
		Tags: expandTags(tags),
	}

	_, error := publicIPClient.CreateOrUpdate(resGroup, name, publicIp, make(chan struct{}))
	err := <-error
	if err != nil {
		return err
	}

	read, err := publicIPClient.Get(resGroup, name, "")
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read Public IP %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	return resourceArmPublicIpRead(d, meta)
}

func resourceArmPublicIpRead(d *schema.ResourceData, meta interface{}) error {
	publicIPClient := meta.(*ArmClient).publicIPClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["publicIPAddresses"]

	resp, err := publicIPClient.Get(resGroup, name, "")
	if err != nil {
		if utils.ResponseWasNotFound(resp.Response) {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error making Read request on Azure public ip %s: %s", name, err)
	}

	d.Set("resource_group_name", resGroup)
	d.Set("location", azureRMNormalizeLocation(*resp.Location))
	d.Set("name", resp.Name)
	d.Set("public_ip_address_allocation", strings.ToLower(string(resp.PublicIPAddressPropertiesFormat.PublicIPAllocationMethod)))

	if resp.PublicIPAddressPropertiesFormat.DNSSettings != nil && resp.PublicIPAddressPropertiesFormat.DNSSettings.Fqdn != nil && *resp.PublicIPAddressPropertiesFormat.DNSSettings.Fqdn != "" {
		d.Set("fqdn", resp.PublicIPAddressPropertiesFormat.DNSSettings.Fqdn)
	}

	if resp.PublicIPAddressPropertiesFormat.IPAddress != nil && *resp.PublicIPAddressPropertiesFormat.IPAddress != "" {
		d.Set("ip_address", resp.PublicIPAddressPropertiesFormat.IPAddress)
	}

	flattenAndSetTags(d, resp.Tags)

	return nil
}

func resourceArmPublicIpDelete(d *schema.ResourceData, meta interface{}) error {
	publicIPClient := meta.(*ArmClient).publicIPClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["publicIPAddresses"]

	_, error := publicIPClient.Delete(resGroup, name, make(chan struct{}))
	err = <-error

	return err
}

func validatePublicIpAllocation(v interface{}, k string) (ws []string, errors []error) {
	value := strings.ToLower(v.(string))
	allocations := map[string]bool{
		"static":  true,
		"dynamic": true,
	}

	if !allocations[value] {
		errors = append(errors, fmt.Errorf("Public IP Allocation can only be Static of Dynamic"))
	}
	return
}

func validatePublicIpDomainNameLabel(v interface{}, k string) (ws []string, errors []error) {
	value := v.(string)
	if !regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(value) {
		errors = append(errors, fmt.Errorf(
			"only alphanumeric characters and hyphens allowed in %q: %q",
			k, value))
	}

	if len(value) > 61 {
		errors = append(errors, fmt.Errorf(
			"%q cannot be longer than 61 characters: %q", k, value))
	}

	if len(value) == 0 {
		errors = append(errors, fmt.Errorf(
			"%q cannot be an empty string: %q", k, value))
	}
	if regexp.MustCompile(`-$`).MatchString(value) {
		errors = append(errors, fmt.Errorf(
			"%q cannot end with a hyphen: %q", k, value))
	}

	return
}
<commit_msg>Clarify the error message the public IP domain name label<commit_after>package azurerm

import (
	"fmt"
	"log"
	"regexp"
	"strings"

	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmPublicIp() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmPublicIpCreate,
		Read:   resourceArmPublicIpRead,
		Update: resourceArmPublicIpCreate,
		Delete: resourceArmPublicIpDelete,
		Importer: &schema.ResourceImporter{
			State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
				id, err := parseAzureResourceID(d.Id())
				if err != nil {
					return nil, err
				}
				name := id.Path["publicIPAddresses"]
				if name == "" {
					return nil, fmt.Errorf("Error parsing supplied resource id. Please check it and rerun:\n %s", d.Id())
				}
				return []*schema.ResourceData{d}, nil
			},
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": locationSchema(),

			"resource_group_name": resourceGroupNameSchema(),

			"public_ip_address_allocation": {
				Type:             schema.TypeString,
				Required:         true,
				ValidateFunc:     validatePublicIpAllocation,
				StateFunc:        ignoreCaseStateFunc,
				DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
			},

			"idle_timeout_in_minutes": {
				Type:     schema.TypeInt,
				Optional: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(int)
					if value < 4 || value > 30 {
						errors = append(errors, fmt.Errorf(
							"The idle timeout must be between 4 and 30 minutes"))
					}
					return
				},
			},

			"domain_name_label": {
				Type:         schema.TypeString,
				Optional:     true,
				ValidateFunc: validatePublicIpDomainNameLabel,
			},

			"reverse_fqdn": {
				Type:     schema.TypeString,
				Optional: true,
			},

			"fqdn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"ip_address": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmPublicIpCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient)
	publicIPClient := client.publicIPClient

	log.Printf("[INFO] preparing arguments for Azure ARM Public IP creation.")

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	tags := d.Get("tags").(map[string]interface{})

	properties := network.PublicIPAddressPropertiesFormat{
		PublicIPAllocationMethod: network.IPAllocationMethod(d.Get("public_ip_address_allocation").(string)),
	}

	dnl, hasDnl := d.GetOk("domain_name_label")
	rfqdn, hasRfqdn := d.GetOk("reverse_fqdn")

	if hasDnl || hasRfqdn {
		dnsSettings := network.PublicIPAddressDNSSettings{}

		if hasRfqdn {
			reverse_fqdn := rfqdn.(string)
			dnsSettings.ReverseFqdn = &reverse_fqdn
		}

		if hasDnl {
			domain_name_label := dnl.(string)
			dnsSettings.DomainNameLabel = &domain_name_label

		}

		properties.DNSSettings = &dnsSettings
	}

	if v, ok := d.GetOk("idle_timeout_in_minutes"); ok {
		idle_timeout := int32(v.(int))
		properties.IdleTimeoutInMinutes = &idle_timeout
	}

	publicIp := network.PublicIPAddress{
		Name:                            &name,
		Location:                        &location,
		PublicIPAddressPropertiesFormat: &properties,
		Tags: expandTags(tags),
	}

	_, error := publicIPClient.CreateOrUpdate(resGroup, name, publicIp, make(chan struct{}))
	err := <-error
	if err != nil {
		return err
	}

	read, err := publicIPClient.Get(resGroup, name, "")
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read Public IP %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	return resourceArmPublicIpRead(d, meta)
}

func resourceArmPublicIpRead(d *schema.ResourceData, meta interface{}) error {
	publicIPClient := meta.(*ArmClient).publicIPClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["publicIPAddresses"]

	resp, err := publicIPClient.Get(resGroup, name, "")
	if err != nil {
		if utils.ResponseWasNotFound(resp.Response) {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error making Read request on Azure public ip %s: %s", name, err)
	}

	d.Set("resource_group_name", resGroup)
	d.Set("location", azureRMNormalizeLocation(*resp.Location))
	d.Set("name", resp.Name)
	d.Set("public_ip_address_allocation", strings.ToLower(string(resp.PublicIPAddressPropertiesFormat.PublicIPAllocationMethod)))

	if resp.PublicIPAddressPropertiesFormat.DNSSettings != nil && resp.PublicIPAddressPropertiesFormat.DNSSettings.Fqdn != nil && *resp.PublicIPAddressPropertiesFormat.DNSSettings.Fqdn != "" {
		d.Set("fqdn", resp.PublicIPAddressPropertiesFormat.DNSSettings.Fqdn)
	}

	if resp.PublicIPAddressPropertiesFormat.IPAddress != nil && *resp.PublicIPAddressPropertiesFormat.IPAddress != "" {
		d.Set("ip_address", resp.PublicIPAddressPropertiesFormat.IPAddress)
	}

	flattenAndSetTags(d, resp.Tags)

	return nil
}

func resourceArmPublicIpDelete(d *schema.ResourceData, meta interface{}) error {
	publicIPClient := meta.(*ArmClient).publicIPClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["publicIPAddresses"]

	_, error := publicIPClient.Delete(resGroup, name, make(chan struct{}))
	err = <-error

	return err
}

func validatePublicIpAllocation(v interface{}, k string) (ws []string, errors []error) {
	value := strings.ToLower(v.(string))
	allocations := map[string]bool{
		"static":  true,
		"dynamic": true,
	}

	if !allocations[value] {
		errors = append(errors, fmt.Errorf("Public IP Allocation can only be Static of Dynamic"))
	}
	return
}

func validatePublicIpDomainNameLabel(v interface{}, k string) (ws []string, errors []error) {
	value := v.(string)
	if !regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(value) {
		errors = append(errors, fmt.Errorf(
			"only lowercase alphanumeric characters and hyphens allowed in %q: %q",
			k, value))
	}

	if len(value) > 61 {
		errors = append(errors, fmt.Errorf(
			"%q cannot be longer than 61 characters: %q", k, value))
	}

	if len(value) == 0 {
		errors = append(errors, fmt.Errorf(
			"%q cannot be an empty string: %q", k, value))
	}
	if regexp.MustCompile(`-$`).MatchString(value) {
		errors = append(errors, fmt.Errorf(
			"%q cannot end with a hyphen: %q", k, value))
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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
//
// 	https://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 safehttp_test

import (
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-safeweb/safehttp"
)

func TestResponseWriterSetCookie(t *testing.T) {
	rr := newResponseRecorder(&strings.Builder{})
	rw := safehttp.NewResponseWriter(testDispatcher{}, rr, nil)

	c := safehttp.NewCookie("foo", "bar")
	err := rw.SetCookie(c)
	if err != nil {
		t.Errorf("rw.SetCookie(c) got: %v want: nil", err)
	}

	wantHeaders := map[string][]string{
		"Set-Cookie": {"foo=bar; HttpOnly; Secure; SameSite=Lax"},
	}
	if diff := cmp.Diff(wantHeaders, map[string][]string(rr.header)); diff != "" {
		t.Errorf("r.Header mismatch (-want +got):\n%s", diff)
	}
}

func TestResponseWriterSetInvalidCookie(t *testing.T) {
	rr := newResponseRecorder(&strings.Builder{})
	rw := safehttp.NewResponseWriter(testDispatcher{}, rr, nil)

	c := safehttp.NewCookie("f=oo", "bar")
	err := rw.SetCookie(c)
	if err == nil {
		t.Error("rw.SetCookie(c) got: nil want: error")
	}
}
<commit_msg>Updated incorrect test error message.<commit_after>// Copyright 2020 Google LLC
//
// 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
//
// 	https://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 safehttp_test

import (
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-safeweb/safehttp"
)

func TestResponseWriterSetCookie(t *testing.T) {
	rr := newResponseRecorder(&strings.Builder{})
	rw := safehttp.NewResponseWriter(testDispatcher{}, rr, nil)

	c := safehttp.NewCookie("foo", "bar")
	err := rw.SetCookie(c)
	if err != nil {
		t.Errorf("rw.SetCookie(c) got: %v want: nil", err)
	}

	wantHeaders := map[string][]string{
		"Set-Cookie": {"foo=bar; HttpOnly; Secure; SameSite=Lax"},
	}
	if diff := cmp.Diff(wantHeaders, map[string][]string(rr.header)); diff != "" {
		t.Errorf("rr.header mismatch (-want +got):\n%s", diff)
	}
}

func TestResponseWriterSetInvalidCookie(t *testing.T) {
	rr := newResponseRecorder(&strings.Builder{})
	rw := safehttp.NewResponseWriter(testDispatcher{}, rr, nil)

	c := safehttp.NewCookie("f=oo", "bar")
	err := rw.SetCookie(c)
	if err == nil {
		t.Error("rw.SetCookie(c) got: nil want: error")
	}
}
<|endoftext|>


<commit_before>// This example demonstrates how to authenticate with Spotify.
// In order to run this example yourself, you'll need to:
//
//  1. Register an application at: https://developer.spotify.com/my-applications/
//       - Use "http://localhost:8080/callback" as the redirect URI
//  2. Set the SPOTIFY_ID environment variable to the client ID you got in step 1.
//  3. Set the SPOTIFY_SECRET environment variable to the client secret from step 1.
package main

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"strconv"

	"github.com/coolbry95/partydj/backend/pool"
	"github.com/pressly/chi"
	"github.com/zmb3/spotify"
)

// redirectURI is the OAuth redirect URI for the application.
// You must register an application at Spotify's developer portal
// and enter this value.
const redirectURI = "https://linode.shellcode.in/callback"

var (
	auth = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserLibraryModify, spotify.ScopePlaylistModifyPrivate,
		spotify.ScopePlaylistModifyPublic)
	ch    = make(chan spotify.Client)
	state = "stateless"
)

type DI struct {
	client spotify.Client
	pool   pool.Pool
}

var PoolShortIDToLongID map[int]string
var UserIDToPoolID map[string]string

func main() {
	// Initialize the pool (single for demonstration)
	var d *DI
	d = new(DI)

	// setup the router and paths
	r := chi.NewRouter()
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		log.Println(r.URL.String())
	})
	r.Get("/callback", completeAuth)
	r.Get("/getpool", d.getPool)
	r.Post("/createpool", d.createPool)
	r.Post("/add_song/:poolID/:songID", d.addSong)
	r.Post("/upvote/:poolID/:songID", d.upVote)
	r.Post("/downvote/:poolID/:songID", d.downVote)
	r.Post("/join_pool", d.joinPool)

	r.FileServer("/files", http.Dir("/home/coolbry95/gosrc/src/github.com/coolbry95/partydj/website"))

	// Authenticate the users spotify account
	url := auth.AuthURL(state)
	fmt.Println("Please log in to Spotify by visiting the following page in your browser:", url)

	go http.ListenAndServe(":6060", r)

	// wait for auth to complete
	d.client = <-ch
	d.pool.PlaylistID = spotify.ID("0nXlYUH7zBAzubO9Yub4rR") // change to spartahack playlist

	// Initialize the short id to long id map
	PoolShortIDToLongID = make(map[int]string)
	// hard code the one join digit for this sample pool to 121
	PoolShortIDToLongID[121] = "0nXlYUH7zBAzubO9Yub4rR"

	// initialize the user map
	UserIDToPoolID = make(map[string]string)

	userID, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	d.pool.UserID = userID.ID

	// Begin the play loop when the server begins for spartahack
	d.BeginPlayLoop()

	block := make(chan struct{})
	<-block
}

func (d *DI) BeginPlayLoop() {
	//fmt.Println("Beginning play loop...")
	// Start the loop after 10 seconds of pool creation
	time.Sleep(10 * time.Second)

	go func() {
		song := heap.Pop(&d.pool).(*pool.Song)
		heap.Push(&d.pool, song)
		// song duration is in milliseconds
		songTime := time.Duration(((song.Duration)/1000) + 15) // 15 seconds + song length
		fmt.Print(songTime * time.Second)
		time.Sleep(songTime * time.Second)
		d.pool.AddNextSong(&d.client)
		//fmt.Println("Now playing new song...")
		d.BeginPlayLoop()
		//return // do this to kill the parent goroutine?
	}()
}

func (d *DI) joinPool(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "http://localhost")

	userIDNumber := r.PostFormValue("userId")
	poolIDNumberStr := r.PostFormValue("poolShortId")

	// Both must be present
	if len(userIDNumber) == 0 || len(poolIDNumberStr) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		fmt.Println("One of the parameters were empty: ", "userId:",
			userIDNumber, "poolShortId:", poolIDNumberStr)
		return
	}

	poolIDNumber, convErr := strconv.Atoi(poolIDNumberStr)

	// Make sure the poolIDNumber is a valid integer, otherwise
	// respond with Status partial content
	if convErr != nil {
		w.WriteHeader(http.StatusPartialContent)
		fmt.Println("Conversion error: ", convErr)
		return
	}

	// Check if pool exists, if not respond with status not found
	if _, ok := PoolShortIDToLongID[poolIDNumber]; ok != true {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	UserIDToPoolID[userIDNumber] = poolIDNumberStr
	// this is the long pool id that links to the long id
	w.Header().Add("long_pool_id", PoolShortIDToLongID[poolIDNumber])
	// Let the user know the request was accepted
	w.WriteHeader(http.StatusOK)
}

func (d *DI) addSong(w http.ResponseWriter, r *http.Request) {
	songID := chi.URLParam(r, "songID")
	//poolID := chi.URLParam(r, "poolID")

	s := &pool.Song{ID: spotify.ID(songID)}
	heap.Push(&d.pool, s)
}

func (d *DI) upVote(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "http://localhost")

	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songId")
	userID := r.PostFormValue("userId")

	if len(userID) == 0 || len(songID) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		return
	}

	if !d.pool.HasUserVoted(userID, songID) {
		d.pool.UpVote(spotify.ID(songID), userID)
		d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.WriteHeader(http.StatusOK)
}

func (d *DI) downVote(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "http://localhost")

	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songId")
	userID := r.PostFormValue("userId")
	//poolID := chi.URLParam(r, "poolID")

	if len(userID) == 0 || len(songID) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		return
	}

	if !d.pool.HasUserVoted(userID, songID) {
		d.pool.DownVote(spotify.ID(songID), userID)
		d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
		w.WriteHeader(http.StatusOK)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

func (d *DI) createPool(w http.ResponseWriter, r *http.Request) {
}

func (d *DI) getPool(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "http://localhost")

	userid, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	// TODO: instead of using existing playlist we require a new playlist to be created
	// playlist, err := d.client.CreatePlaylistForUser(userid.ID, playlistName, true)
	playlist, err := d.client.GetPlaylistTracks(userid.ID, "0nXlYUH7zBAzubO9Yub4rR")
	//spartahackPlaylistName := "Sparthack Sample Playlist!"
	//newPlaylist, err := d.client.CreatePlaylistForUser(userid.ID, spartahackPlaylistName, true)
	if err != nil {
		log.Println(err)
	}

	d.pool.SongHeap = make([]*pool.Song, 0, 100)
	for i, track := range playlist.Tracks {
		d.pool.SongHeap = append(d.pool.SongHeap, pool.TrackToSong(&track.Track, i))
	}
	d.pool.UserToVoteMap = make(map[string][]string)

	//TODO: only call this function only after the the current song finishes

	json.NewEncoder(w).Encode(d.pool)
	return
}

func completeAuth(w http.ResponseWriter, r *http.Request) {
	tok, err := auth.Token(state, r)
	if err != nil {
		http.Error(w, "Couldn't get token", http.StatusForbidden)
		log.Fatal(err)
	}

	if st := r.FormValue("state"); st != state {
		http.NotFound(w, r)
		log.Fatalf("State mismatch: %s != %s\n", st, state)
	}

	// use the token to get an authenticated client
	client := auth.NewClient(tok)
	ch <- client
	http.Redirect(w, r, "/getpool", 301)
}
<commit_msg>Search added and addsong cleaned up.<commit_after>// This example demonstrates how to authenticate with Spotify.
// In order to run this example yourself, you'll need to:
//
//  1. Register an application at: https://developer.spotify.com/my-applications/
//       - Use "http://localhost:8080/callback" as the redirect URI
//  2. Set the SPOTIFY_ID environment variable to the client ID you got in step 1.
//  3. Set the SPOTIFY_SECRET environment variable to the client secret from step 1.
package main

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"strconv"

	"github.com/coolbry95/partydj/backend/pool"
	"github.com/pressly/chi"
	"github.com/zmb3/spotify"
	"golang.org/x/net/html"
)

// redirectURI is the OAuth redirect URI for the application.
// You must register an application at Spotify's developer portal
// and enter this value.
const redirectURI = "http://localhost:8080/callback"

var (
	auth = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserLibraryModify, spotify.ScopePlaylistModifyPrivate,
		spotify.ScopePlaylistModifyPublic)
	ch    = make(chan spotify.Client)
	state = "stateless"
)

type DI struct {
	client spotify.Client
	pool   pool.Pool
}

var PoolShortIDToLongID map[int]string
var UserIDToPoolID map[string]string

func main() {
	// Initialize the pool (single for demonstration)
	var d *DI
	d = new(DI)

	// setup the router and paths
	r := chi.NewRouter()
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		log.Println(r.URL.String())
	})
	r.Get("/callback", completeAuth)
	r.Get("/getpool", d.getPool)
	r.Post("/createpool", d.createPool)
	r.Post("/add_song/:poolID/:songID", d.addSong)
	r.Post("/upvote/:poolID/:songID", d.upVote)
	r.Post("/downvote/:poolID/:songID", d.downVote)
	r.Post("/join_pool", d.joinPool)
	r.Post("/search_for_songs", d.searchForSongs)

	r.FileServer("/files", http.Dir("/home/coolbry95/gosrc/src/github.com/coolbry95/partydj/website"))

	// Authenticate the users spotify account
	url := auth.AuthURL(state)
	fmt.Println("Please log in to Spotify by visiting the following page in your browser:", url)

	go http.ListenAndServe(":8080", r)

	// wait for auth to complete
	d.client = <-ch
	d.pool.PlaylistID = spotify.ID("0nXlYUH7zBAzubO9Yub4rR") // change to spartahack playlist

	// Initialize the short id to long id map
	PoolShortIDToLongID = make(map[int]string)
	// hard code the one join digit for this sample pool to 121
	PoolShortIDToLongID[121] = "0nXlYUH7zBAzubO9Yub4rR"

	// initialize the user map
	UserIDToPoolID = make(map[string]string)

	userID, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	d.pool.UserID = userID.ID

	// Begin the play loop when the server begins for spartahack
	d.BeginPlayLoop()

	block := make(chan struct{})
	<-block
}

func (d *DI) searchForSongs(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "http://localhost")

	searchQuery := html.EscapeString(r.PostFormValue("search_query"))
	numberOfSongsStr := r.PostFormValue("number_of_results")

	// Both must be present
	if len(searchQuery) == 0 || len(numberOfSongsStr) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		fmt.Println("BADREQUEST: (searchForSongs) - One of the parameters were empty: ", "search query:",
			searchQuery, "num of songs:", numberOfSongsStr)
		return
	}

	numberOfSongs, err := strconv.Atoi(numberOfSongsStr)

	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	results, err := d.client.Search(searchQuery, spotify.SearchTypeTrack)

	if err != nil {
		fmt.Println("ERROR: (searchForSongs) - ", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	songsToSend := make([]*pool.Song, 0, numberOfSongs)
	for index, track := range results.Tracks.Tracks {
		if index > numberOfSongs {
			break
		} else {
			song := pool.TrackToSong(&track, 0)
			songsToSend = append(songsToSend, song)
		}
	}

	json.NewEncoder(w).Encode(songsToSend)
	w.WriteHeader(http.StatusOK)
}

func (d *DI) BeginPlayLoop() {
	//fmt.Println("Beginning play loop...")
	// Start the loop after 10 seconds of pool creation
	time.Sleep(10 * time.Second)

	go func() {
		song := heap.Pop(&d.pool).(*pool.Song)
		heap.Push(&d.pool, song)
		// song duration is in milliseconds
		songTime := time.Duration(((song.Duration)/1000) + 15) // 15 seconds + song length
		fmt.Print(songTime * time.Second)
		time.Sleep(songTime * time.Second)
		d.pool.AddNextSong(&d.client)
		//fmt.Println("Now playing new song...")
		d.BeginPlayLoop()
		//return // do this to kill the parent goroutine?
	}()
}

func (d *DI) joinPool(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "http://localhost")

	userIDNumber := r.PostFormValue("userId")
	poolIDNumberStr := r.PostFormValue("poolShortId")

	// Both must be present
	if len(userIDNumber) == 0 || len(poolIDNumberStr) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		fmt.Println("BADREQUEST: (joinPool) - One of the parameters were empty: ", "userId:",
			userIDNumber, "poolShortId:", poolIDNumberStr)
		return
	}

	poolIDNumber, convErr := strconv.Atoi(poolIDNumberStr)

	// Make sure the poolIDNumber is a valid integer, otherwise
	// respond with Status partial content
	if convErr != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Println("Conversion error: ", convErr)
		return
	}

	// Check if pool exists, if not respond with status not found
	if _, ok := PoolShortIDToLongID[poolIDNumber]; ok != true {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	UserIDToPoolID[userIDNumber] = poolIDNumberStr
	// this is the long pool id that links to the long id
	w.Header().Add("long_pool_id", PoolShortIDToLongID[poolIDNumber])
	// Let the user know the request was accepted
	w.WriteHeader(http.StatusOK)
}

func (d *DI) addSong(w http.ResponseWriter, r *http.Request) {
	userID := r.PostFormValue("userId")
	songID := chi.URLParam(r, "songID")

	// Both must be present
	if len(userID) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		fmt.Println("One of the parameters were empty: ", "userId:", userID)
		return
	}

	if _, ok := UserIDToPoolID[userID]; ok == false {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	s := &pool.Song{ID: spotify.ID(songID)}
	heap.Push(&d.pool, s)
}

func (d *DI) upVote(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "http://localhost")

	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songId")
	userID := r.PostFormValue("userId")

	if len(userID) == 0 || len(songID) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		return
	}

	if !d.pool.HasUserVoted(userID, songID) {
		d.pool.UpVote(spotify.ID(songID), userID)
		d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.WriteHeader(http.StatusOK)
}

func (d *DI) downVote(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "http://localhost")

	// TODO check for user ID to see if they already voted
	songID := chi.URLParam(r, "songId")
	userID := r.PostFormValue("userId")
	//poolID := chi.URLParam(r, "poolID")

	if len(userID) == 0 || len(songID) == 0 {
		w.WriteHeader(http.StatusPartialContent)
		return
	}

	if !d.pool.HasUserVoted(userID, songID) {
		d.pool.DownVote(spotify.ID(songID), userID)
		d.pool.UpdateSpotifyPlaylist(&d.client, d.pool.PlaylistID)
		w.WriteHeader(http.StatusOK)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

func (d *DI) createPool(w http.ResponseWriter, r *http.Request) {
}

func (d *DI) getPool(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "http://localhost")

	userid, err := d.client.CurrentUser()
	if err != nil {
		log.Println(err)
	}

	// TODO: instead of using existing playlist we require a new playlist to be created
	// playlist, err := d.client.CreatePlaylistForUser(userid.ID, playlistName, true)
	playlist, err := d.client.GetPlaylistTracks(userid.ID, "0nXlYUH7zBAzubO9Yub4rR")
	//spartahackPlaylistName := "Sparthack Sample Playlist!"
	//newPlaylist, err := d.client.CreatePlaylistForUser(userid.ID, spartahackPlaylistName, true)
	if err != nil {
		log.Println(err)
	}

	d.pool.SongHeap = make([]*pool.Song, 0, 100)
	for i, track := range playlist.Tracks {
		d.pool.SongHeap = append(d.pool.SongHeap, pool.TrackToSong(&track.Track, i))
	}
	d.pool.UserToVoteMap = make(map[string][]string)

	//TODO: only call this function only after the the current song finishes

	json.NewEncoder(w).Encode(d.pool)
	return
}

func completeAuth(w http.ResponseWriter, r *http.Request) {
	tok, err := auth.Token(state, r)
	if err != nil {
		http.Error(w, "Couldn't get token", http.StatusForbidden)
		log.Fatal(err)
	}

	if st := r.FormValue("state"); st != state {
		http.NotFound(w, r)
		log.Fatalf("State mismatch: %s != %s\n", st, state)
	}

	// use the token to get an authenticated client
	client := auth.NewClient(tok)
	ch <- client
	http.Redirect(w, r, "/getpool", 301)
}
<|endoftext|>


<commit_before>package main

import (
	"syscall"
	"path"
	"flag"
	"os"
	"fmt"

	"net/http"

	"github.com/gorilla/mux"
	"github.com/op/go-logging"
)

var log = logging.MustGetLogger("nomad-ui")

func init() {
	var format = logging.MustStringFormatter(
		`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{color:reset} %{message}`,
	)

	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	backend2Formatter := logging.NewBackendFormatter(logBackend, format)
	logging.SetBackend(backend2Formatter)
}

type Config struct {
	Address       string
	ListenAddress string
	Endpoint      string
}

func DefaultConfig() *Config {
	return &Config{
		Address: "http://127.0.0.1:4646",
		ListenAddress: ":3000",
		Endpoint: "/",
	}
}

func flagDefault(value string) string {
	return fmt.Sprintf("(default: \"%s\")", value)
}

var (
	defaultConfig = DefaultConfig()

	flagAddress = flag.String("address", "", "The address of the Nomad server. " +
		"Overrides the NOMAD_ADDR environment variable if set. " + flagDefault(defaultConfig.Address))
	flagListenAddress = flag.String("web.listen-address", "",
		"The address on which to expose the web interface. " + flagDefault(defaultConfig.ListenAddress))
	flagEndpoint = flag.String("web.path", "",
		"Path under which to expose the web interface. " + flagDefault(defaultConfig.Endpoint))
)

func (c *Config) Parse() {
	flag.Parse()

	address, ok := syscall.Getenv("NOMAD_ADDR")
	if ok {
		c.Address = address
	}
	if *flagAddress != "" {
		c.Address = *flagAddress
	}
	if *flagListenAddress != "" {
		c.ListenAddress = *flagListenAddress
	}
	if *flagEndpoint != "" {
		c.Endpoint = *flagEndpoint
	}
}

func main() {
	cfg := DefaultConfig()
	cfg.Parse()

	router := mux.NewRouter()

	broadcast := make(chan *Action)

	nomad := NewNomad(cfg.Address, broadcast)
	go nomad.watchAllocs()
	go nomad.watchEvals()
	go nomad.watchJobs()
	go nomad.watchNodes()
	go nomad.watchMembers()

	hub := NewHub(nomad, broadcast)
	go hub.Run()

	router.HandleFunc(path.Join(cfg.Endpoint, "ws"), hub.Handler)
	router.PathPrefix(cfg.Endpoint).Handler(http.FileServer(assetFS()))

	log.Infof("Listening on %s", cfg.ListenAddress)
	err := http.ListenAndServe(cfg.ListenAddress, router)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Improve logging.<commit_after>package main

import (
	"syscall"
	"path"
	"flag"
	"os"
	"fmt"

	"net/http"

	"github.com/gorilla/mux"
	"github.com/op/go-logging"
)

var log = logging.MustGetLogger("nomad-ui")

func init() {
	var format = logging.MustStringFormatter(
		`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{color:reset} %{message}`,
	)

	logBackend := logging.NewLogBackend(os.Stderr, "", 0)

	logBackendLeveled := logging.AddModuleLevel(logBackend)
	logBackendLeveled.SetLevel(logging.INFO, "")
	backend2Formatter := logging.NewBackendFormatter(logBackendLeveled, format)
	logging.SetBackend(backend2Formatter)
}

type Config struct {
	Address       string
	ListenAddress string
	Endpoint      string
}

func DefaultConfig() *Config {
	return &Config{
		Address: "http://127.0.0.1:4646",
		ListenAddress: "0.0.0.0:3000",
		Endpoint: "/",
	}
}

func flagDefault(value string) string {
	return fmt.Sprintf("(default: \"%s\")", value)
}

var (
	defaultConfig = DefaultConfig()

	flagAddress = flag.String("address", "", "The address of the Nomad server. " +
		"Overrides the NOMAD_ADDR environment variable if set. " + flagDefault(defaultConfig.Address))
	flagListenAddress = flag.String("web.listen-address", "",
		"The address on which to expose the web interface. " + flagDefault(defaultConfig.ListenAddress))
	flagEndpoint = flag.String("web.path", "",
		"Path under which to expose the web interface. " + flagDefault(defaultConfig.Endpoint))
)

func (c *Config) Parse() {
	flag.Parse()

	address, ok := syscall.Getenv("NOMAD_ADDR")
	if ok {
		c.Address = address
	}
	if *flagAddress != "" {
		c.Address = *flagAddress
	}
	if *flagListenAddress != "" {
		c.ListenAddress = *flagListenAddress
	}
	if *flagEndpoint != "" {
		c.Endpoint = *flagEndpoint
	}
}

func main() {
	cfg := DefaultConfig()
	cfg.Parse()
	log.Infof("----------------------------------------------------------------------")
	log.Infof("|                          NOMAD UI                                  |")
	log.Infof("----------------------------------------------------------------------")
	log.Infof("| address            : %-45s |", cfg.Address)
	log.Infof("| web.listen-address : %-45s |", cfg.ListenAddress)
	log.Infof("| web.path           : %-45s |", cfg.Endpoint)
	log.Infof("----------------------------------------------------------------------")
	log.Infof("")

	broadcast := make(chan *Action)

	log.Infof("Connecting to nomad ...")
	nomad := NewNomad(cfg.Address, broadcast)
	go nomad.watchAllocs()
	go nomad.watchEvals()
	go nomad.watchJobs()
	go nomad.watchNodes()
	go nomad.watchMembers()

	hub := NewHub(nomad, broadcast)
	go hub.Run()

	router := mux.NewRouter()
	router.HandleFunc(path.Join(cfg.Endpoint, "ws"), hub.Handler)
	router.PathPrefix(cfg.Endpoint).Handler(http.FileServer(assetFS()))

	log.Infof("Listening ...")
	err := http.ListenAndServe(cfg.ListenAddress, router)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package lua

import (
	"math"
)

const bitCount = 32

func trim(x uint) uint { return x & math.MaxUint32 }
func mask(n uint) uint { return ^((math.MaxUint32 << (n - 1)) << 1) }

func shift(l *State, r uint, i int) int {
	if i < 0 {
		if i, r = -i, trim(r); i >= bitCount {
			r = 0
		} else {
			r >>= uint(i)
		}
	} else {
		if i >= bitCount {
			r = 0
		} else {
			r <<= uint(i)
		}
		r = trim(r)
	}
	PushUnsigned(l, r)
	return 1
}

func rotate(l *State, i int) int {
	r := trim(CheckUnsigned(l, 1))
	if i &= bitCount - 1; i != 0 {
		r = trim((r << uint(i)) | (r >> uint(bitCount-i)))
	}
	PushUnsigned(l, r)
	return 1
}

func bitOp(l *State, init uint, f func(a, b uint) uint) uint {
	r := init
	for i, n := 1, Top(l); i <= n; i++ {
		r = f(r, CheckUnsigned(l, i))
	}
	return trim(r)
}

func andHelper(l *State) uint {
	x := bitOp(l, ^uint(0), func(a, b uint) uint { return a & b })
	return x
}

func fieldArguments(l *State, fieldIndex int) (uint, uint) {
	f, w := CheckInteger(l, fieldIndex), OptInteger(l, fieldIndex+1, 1)
	ArgumentCheck(l, 0 <= f, fieldIndex, "field cannot be negative")
	ArgumentCheck(l, 0 < w, fieldIndex+1, "width must be positive")
	if f+w > bitCount {
		Errorf(l, "trying to access non-existent bits")
	}
	return uint(f), uint(w)
}

var bitLibrary = []RegistryFunction{
	{"arshift", func(l *State) int {
		if r, i := CheckUnsigned(l, 1), CheckInteger(l, 2); i < 0 || 0 == (r&(1<<(bitCount-1))) {
			return shift(l, r, -i)
		} else {
			if i >= bitCount {
				r = math.MaxUint32
			} else {
				r = trim((r >> uint(i)) | ^(math.MaxUint32 >> uint(i)))
			}
			PushUnsigned(l, r)
		}
		return 1
	}},
	{"band", func(l *State) int { PushUnsigned(l, andHelper(l)); return 1 }},
	{"bnot", func(l *State) int { PushUnsigned(l, trim(^CheckUnsigned(l, 1))); return 1 }},
	{"bor", func(l *State) int { PushUnsigned(l, bitOp(l, 0, func(a, b uint) uint { return a | b })); return 1 }},
	{"bxor", func(l *State) int { PushUnsigned(l, bitOp(l, 0, func(a, b uint) uint { return a ^ b })); return 1 }},
	{"btest", func(l *State) int { PushBoolean(l, andHelper(l) != 0); return 1 }},
	{"extract", func(l *State) int {
		r := CheckUnsigned(l, 1)
		f, w := fieldArguments(l, 2)
		PushUnsigned(l, (r>>f)&mask(w))
		return 1
	}},
	{"lrotate", func(l *State) int { return rotate(l, CheckInteger(l, 2)) }},
	{"lshift", func(l *State) int { return shift(l, CheckUnsigned(l, 1), CheckInteger(l, 2)) }},
	{"replace", func(l *State) int {
		r, v := CheckUnsigned(l, 1), CheckUnsigned(l, 2)
		f, w := fieldArguments(l, 3)
		m := mask(w)
		v &= m
		PushUnsigned(l, (r & ^(m<<f))|(v<<f))
		return 1
	}},
	{"rrotate", func(l *State) int { return rotate(l, -CheckInteger(l, 2)) }},
	{"rshift", func(l *State) int { return shift(l, CheckUnsigned(l, 1), -CheckInteger(l, 2)) }},
}

func Bit32Open(l *State) int {
	NewLibrary(l, bitLibrary)
	return 1
}
<commit_msg>Switch to simplified mask(n) function<commit_after>package lua

import (
	"math"
)

const bitCount = 32

func trim(x uint) uint { return x & math.MaxUint32 }
func mask(n uint) uint { return ^(math.MaxUint32 << n) }

func shift(l *State, r uint, i int) int {
	if i < 0 {
		if i, r = -i, trim(r); i >= bitCount {
			r = 0
		} else {
			r >>= uint(i)
		}
	} else {
		if i >= bitCount {
			r = 0
		} else {
			r <<= uint(i)
		}
		r = trim(r)
	}
	PushUnsigned(l, r)
	return 1
}

func rotate(l *State, i int) int {
	r := trim(CheckUnsigned(l, 1))
	if i &= bitCount - 1; i != 0 {
		r = trim((r << uint(i)) | (r >> uint(bitCount-i)))
	}
	PushUnsigned(l, r)
	return 1
}

func bitOp(l *State, init uint, f func(a, b uint) uint) uint {
	r := init
	for i, n := 1, Top(l); i <= n; i++ {
		r = f(r, CheckUnsigned(l, i))
	}
	return trim(r)
}

func andHelper(l *State) uint {
	x := bitOp(l, ^uint(0), func(a, b uint) uint { return a & b })
	return x
}

func fieldArguments(l *State, fieldIndex int) (uint, uint) {
	f, w := CheckInteger(l, fieldIndex), OptInteger(l, fieldIndex+1, 1)
	ArgumentCheck(l, 0 <= f, fieldIndex, "field cannot be negative")
	ArgumentCheck(l, 0 < w, fieldIndex+1, "width must be positive")
	if f+w > bitCount {
		Errorf(l, "trying to access non-existent bits")
	}
	return uint(f), uint(w)
}

var bitLibrary = []RegistryFunction{
	{"arshift", func(l *State) int {
		if r, i := CheckUnsigned(l, 1), CheckInteger(l, 2); i < 0 || 0 == (r&(1<<(bitCount-1))) {
			return shift(l, r, -i)
		} else {
			if i >= bitCount {
				r = math.MaxUint32
			} else {
				r = trim((r >> uint(i)) | ^(math.MaxUint32 >> uint(i)))
			}
			PushUnsigned(l, r)
		}
		return 1
	}},
	{"band", func(l *State) int { PushUnsigned(l, andHelper(l)); return 1 }},
	{"bnot", func(l *State) int { PushUnsigned(l, trim(^CheckUnsigned(l, 1))); return 1 }},
	{"bor", func(l *State) int { PushUnsigned(l, bitOp(l, 0, func(a, b uint) uint { return a | b })); return 1 }},
	{"bxor", func(l *State) int { PushUnsigned(l, bitOp(l, 0, func(a, b uint) uint { return a ^ b })); return 1 }},
	{"btest", func(l *State) int { PushBoolean(l, andHelper(l) != 0); return 1 }},
	{"extract", func(l *State) int {
		r := CheckUnsigned(l, 1)
		f, w := fieldArguments(l, 2)
		PushUnsigned(l, (r>>f)&mask(w))
		return 1
	}},
	{"lrotate", func(l *State) int { return rotate(l, CheckInteger(l, 2)) }},
	{"lshift", func(l *State) int { return shift(l, CheckUnsigned(l, 1), CheckInteger(l, 2)) }},
	{"replace", func(l *State) int {
		r, v := CheckUnsigned(l, 1), CheckUnsigned(l, 2)
		f, w := fieldArguments(l, 3)
		m := mask(w)
		v &= m
		PushUnsigned(l, (r & ^(m<<f))|(v<<f))
		return 1
	}},
	{"rrotate", func(l *State) int { return rotate(l, -CheckInteger(l, 2)) }},
	{"rshift", func(l *State) int { return shift(l, CheckUnsigned(l, 1), -CheckInteger(l, 2)) }},
}

func Bit32Open(l *State) int {
	NewLibrary(l, bitLibrary)
	return 1
}
<|endoftext|>


<commit_before>package main

import (
	"strings"
	"unicode"
	"unicode/utf8"
)

//everything in this file was copied from the go/doc package as this is not exported
//It can be found in go/doc/comment.go

func indentLen(s string) int {
	i := 0
	for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
		i++
	}
	return i
}

func isBlank(s string) bool {
	return len(s) == 0 || (len(s) == 1 && s[0] == '\n')
}

func commonPrefix(a, b string) string {
	i := 0
	for i < len(a) && i < len(b) && a[i] == b[i] {
		i++
	}
	return a[0:i]
}

func unindent(block []string) {
	if len(block) == 0 {
		return
	}

	// compute maximum common white prefix
	prefix := block[0][0:indentLen(block[0])]
	for _, line := range block {
		if !isBlank(line) {
			prefix = commonPrefix(prefix, line[0:indentLen(line)])
		}
	}
	n := len(prefix)

	// remove
	for i, line := range block {
		if !isBlank(line) {
			block[i] = line[n:]
		}
	}
}

// heading returns the trimmed line if it passes as a section heading;
// otherwise it returns the empty string.
func heading(line string) string {
	line = strings.TrimSpace(line)
	if len(line) == 0 {
		return ""
	}

	// a heading must start with an uppercase letter
	r, _ := utf8.DecodeRuneInString(line)
	if !unicode.IsLetter(r) || !unicode.IsUpper(r) {
		return ""
	}

	// it must end in a letter or digit:
	r, _ = utf8.DecodeLastRuneInString(line)
	if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
		return ""
	}

	// exclude lines with illegal characters
	if strings.IndexAny(line, ",.;:!?+*/=()[]{}_^°&§~%#@<\">\\") >= 0 {
		return ""
	}

	// allow "'" for possessive "'s" only
	for b := line; ; {
		i := strings.IndexRune(b, '\'')
		if i < 0 {
			break
		}
		if i+1 >= len(b) || b[i+1] != 's' || (i+2 < len(b) && b[i+2] != ' ') {
			return "" // not followed by "s "
		}
		b = b[i+2:]
	}

	return line
}

type op int

const (
	opPara op = iota
	opHead
	opPre
)

type block struct {
	op    op
	lines []string
}

func blocks(text string) []block {
	var (
		out  []block
		para []string

		lastWasBlank   = false
		lastWasHeading = false
	)

	close := func() {
		if para != nil {
			out = append(out, block{opPara, para})
			para = nil
		}
	}

	lines := strings.SplitAfter(text, "\n")
	unindent(lines)
	for i := 0; i < len(lines); {
		line := lines[i]
		if isBlank(line) {
			// close paragraph
			close()
			i++
			lastWasBlank = true
			continue
		}
		if indentLen(line) > 0 {
			// close paragraph
			close()

			// count indented or blank lines
			j := i + 1
			for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
				j++
			}
			// but not trailing blank lines
			for j > i && isBlank(lines[j-1]) {
				j--
			}
			pre := lines[i:j]
			i = j

			unindent(pre)

			// put those lines in a pre block
			out = append(out, block{opPre, pre})
			lastWasHeading = false
			continue
		}

		if lastWasBlank && !lastWasHeading && i+2 < len(lines) &&
			isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 {
			// current line is non-blank, surrounded by blank lines
			// and the next non-blank line is not indented: this
			// might be a heading.
			if head := heading(line); head != "" {
				close()
				out = append(out, block{opHead, []string{head}})
				i += 2
				lastWasHeading = true
				continue
			}
		}

		// open paragraph
		lastWasBlank = false
		lastWasHeading = false
		para = append(para, lines[i])
		i++
	}
	close()

	return out
}
<commit_msg>Sync heading func with go1.16.5. (#11)<commit_after>package main

import (
	"strings"
	"unicode"
	"unicode/utf8"
)

//everything in this file was copied from the go/doc package as this is not exported
//It can be found in go/doc/comment.go

func indentLen(s string) int {
	i := 0
	for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
		i++
	}
	return i
}

func isBlank(s string) bool {
	return len(s) == 0 || (len(s) == 1 && s[0] == '\n')
}

func commonPrefix(a, b string) string {
	i := 0
	for i < len(a) && i < len(b) && a[i] == b[i] {
		i++
	}
	return a[0:i]
}

func unindent(block []string) {
	if len(block) == 0 {
		return
	}

	// compute maximum common white prefix
	prefix := block[0][0:indentLen(block[0])]
	for _, line := range block {
		if !isBlank(line) {
			prefix = commonPrefix(prefix, line[0:indentLen(line)])
		}
	}
	n := len(prefix)

	// remove
	for i, line := range block {
		if !isBlank(line) {
			block[i] = line[n:]
		}
	}
}

// heading returns the trimmed line if it passes as a section heading;
// otherwise it returns the empty string.
func heading(line string) string {
	line = strings.TrimSpace(line)
	if len(line) == 0 {
		return ""
	}

	// a heading must start with an uppercase letter
	r, _ := utf8.DecodeRuneInString(line)
	if !unicode.IsLetter(r) || !unicode.IsUpper(r) {
		return ""
	}

	// it must end in a letter or digit:
	r, _ = utf8.DecodeLastRuneInString(line)
	if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
		return ""
	}

	// exclude lines with illegal characters. we allow "(),"
	if strings.ContainsAny(line, ";:!?+*/=[]{}_^°&§~%#@<\">\\") {
		return ""
	}

	// allow "'" for possessive "'s" only
	for b := line; ; {
		i := strings.IndexRune(b, '\'')
		if i < 0 {
			break
		}
		if i+1 >= len(b) || b[i+1] != 's' || (i+2 < len(b) && b[i+2] != ' ') {
			return "" // not followed by "s "
		}
		b = b[i+2:]
	}

	// allow "." when followed by non-space
	for b := line; ; {
		i := strings.IndexRune(b, '.')
		if i < 0 {
			break
		}
		if i+1 >= len(b) || b[i+1] == ' ' {
			return "" // not followed by non-space
		}
		b = b[i+1:]
	}

	return line
}

type op int

const (
	opPara op = iota
	opHead
	opPre
)

type block struct {
	op    op
	lines []string
}

func blocks(text string) []block {
	var (
		out  []block
		para []string

		lastWasBlank   = false
		lastWasHeading = false
	)

	close := func() {
		if para != nil {
			out = append(out, block{opPara, para})
			para = nil
		}
	}

	lines := strings.SplitAfter(text, "\n")
	unindent(lines)
	for i := 0; i < len(lines); {
		line := lines[i]
		if isBlank(line) {
			// close paragraph
			close()
			i++
			lastWasBlank = true
			continue
		}
		if indentLen(line) > 0 {
			// close paragraph
			close()

			// count indented or blank lines
			j := i + 1
			for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
				j++
			}
			// but not trailing blank lines
			for j > i && isBlank(lines[j-1]) {
				j--
			}
			pre := lines[i:j]
			i = j

			unindent(pre)

			// put those lines in a pre block
			out = append(out, block{opPre, pre})
			lastWasHeading = false
			continue
		}

		if lastWasBlank && !lastWasHeading && i+2 < len(lines) &&
			isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 {
			// current line is non-blank, surrounded by blank lines
			// and the next non-blank line is not indented: this
			// might be a heading.
			if head := heading(line); head != "" {
				close()
				out = append(out, block{opHead, []string{head}})
				i += 2
				lastWasHeading = true
				continue
			}
		}

		// open paragraph
		lastWasBlank = false
		lastWasHeading = false
		para = append(para, lines[i])
		i++
	}
	close()

	return out
}
<|endoftext|>