<commit_before>package main

import (
	"fmt"

	"github.com/everdev/mack"
	"github.com/urfave/cli"
)

var commands = []cli.Command{
	{
		Name:    "play",
		Aliases: []string{"pl", "start"},
		Usage:   "Play current selected music",
		Action:  play,
	},
	{
		Name:    "pause",
		Aliases: []string{"pa", "stop"},
		Usage:   "Stop current playing music",
		Action:  pause,
	},
	{
		Name:    "next",
		Aliases: []string{"n", "ne"},
		Usage:   "Play next music",
		Action:  next,
	},
	{
		Name:    "prev",
		Aliases: []string{"pr"},
		Usage:   "Play previous music",
		Action:  nil,
	},
	{
		Name:    "back",
		Aliases: []string{"b"},
		Usage:   "Replay current music or play previous music",
		Action:  back,
	},
	{
		Name:    "vol",
		Aliases: []string{"v"},
		Usage:   "Change volume with an argument",
		Action:  nil,
	},
	{
		Name:    "find",
		Aliases: []string{"v"},
		Usage:   "Find a music (or playlist, artist, album) by fuzzy search apps",
		Action:  nil,
	},
}

func play(c *cli.Context) error {
	err := mack.Tell("iTunes", "play")
	if err != nil {
		return fmt.Errorf("cannot play music: %s", err)
	}

	return nil
}

func pause(c *cli.Context) error {
	err := mack.Tell("iTunes", "pause")
	if err != nil {
		return fmt.Errorf("cannot pause current music: %s", err)
	}

	return nil
}

func next(c *cli.Context) error {
	err := mack.Tell("iTunes", "next track")
	if err != nil {
		return fmt.Errorf("cannot play next music: %s", err)
	}

	return nil
}

func back(c *cli.Context) error {
	err := mack.Tell("iTunes", "back track")
	if err != nil {
		return fmt.Errorf("cannot back music: %s", err)
	}

	return nil
}
<commit_msg>[ADD] Implemented vol sub-command<commit_after>package main

import (
	"fmt"
	"strconv"

	"github.com/everdev/mack"
	"github.com/urfave/cli"
)

var commands = []cli.Command{
	{
		Name:    "play",
		Aliases: []string{"pl", "start"},
		Usage:   "Play current selected music",
		Action:  play,
	},
	{
		Name:    "pause",
		Aliases: []string{"pa", "stop"},
		Usage:   "Stop current playing music",
		Action:  pause,
	},
	{
		Name:    "next",
		Aliases: []string{"n", "ne"},
		Usage:   "Play next music",
		Action:  next,
	},
	{
		Name:    "prev",
		Aliases: []string{"pr"},
		Usage:   "Play previous music",
		Action:  prev,
	},
	{
		Name:    "back",
		Aliases: []string{"b"},
		Usage:   "Replay current music or play previous music",
		Action:  back,
	},
	{
		Name:      "vol",
		Aliases:   []string{"v"},
		Usage:     "Change volume with an argument (0 - 100)",
		Action:    vol,
		ArgsUsage: "volume",
	},
	{
		Name:    "find",
		Aliases: []string{"v"},
		Usage:   "Find a music (or playlist, artist, album) by fuzzy search apps",
		Action:  nil,
	},
}

func play(c *cli.Context) error {
	err := mack.Tell("iTunes", "play")
	if err != nil {
		return fmt.Errorf("cannot play music: %s", err)
	}

	return nil
}

func pause(c *cli.Context) error {
	err := mack.Tell("iTunes", "pause")
	if err != nil {
		return fmt.Errorf("cannot pause current music: %s", err)
	}

	return nil
}

func next(c *cli.Context) error {
	err := mack.Tell("iTunes", "next track")
	if err != nil {
		return fmt.Errorf("cannot play next music: %s", err)
	}

	return nil
}

func prev(c *cli.Context) error {
	err := mack.Tell("iTunes", "previous track")
	if err != nil {
		return fmt.Errorf("cannot play previous music: %s", err)
	}

	return nil
}

func back(c *cli.Context) error {
	err := mack.Tell("iTunes", "back track")
	if err != nil {
		return fmt.Errorf("cannot back music: %s", err)
	}

	return nil
}

func vol(c *cli.Context) error {
	if c.NArg() != 1 {
		cli.ShowCommandHelp(c, "vol")
		return fmt.Errorf("\ninvalid arguments number")
	}

	n, err := strconv.Atoi(c.Args()[0])
	if err != nil {
		return fmt.Errorf("cannot convert argument to number: %s", err)
	}

	if n < 0 || n > 100 {
		return fmt.Errorf("invalid range: %d", n)
	}

	err = mack.Tell("iTunes", fmt.Sprintf("set sound volume to %d", n))
	if err != nil {
		return fmt.Errorf("cannot change volume: %s", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package menu

import (
	"fmt"
	"time"

	"github.com/aws/aws-sdk-go/aws/endpoints"
	"github.com/fatih/color"

	"github.com/miquella/vaulted/lib"
)

// AWSMenu The menu type for the AWS edit tree
type AWSMenu struct {
	*Menu
}

func sdkRegions() map[string]bool {
	regions := make(map[string]bool)
	for _, partition := range endpoints.DefaultPartitions() {
		for region := range partition.Regions() {
			regions[region] = true
		}
	}
	return regions
}

func (m *AWSMenu) Help() {
	menuColor.Set()
	defer color.Unset()

	fmt.Println("k,key    - Key")
	fmt.Println("m,mfa    - MFA")
	fmt.Println("r,role   - Role")
	fmt.Println("R,region - Region")
	fmt.Println("t,temp   - Substitute with temporary credentials")
	fmt.Println("S,show   - Show/Hide Secrets")
	fmt.Println("D,delete - Delete")
	fmt.Println("?,help   - Help")
	fmt.Println("b,back   - Back")
	fmt.Println("q,quit   - Quit")
}

func (m *AWSMenu) Handler() error {
	var err error

	for {
		var input string
		m.Printer()
		if m.Vault.AWSKey == nil {
			input, err = interaction.ReadMenu("Edit AWS key [k,b]: ")
		} else {
			input, err = interaction.ReadMenu("Edit AWS key [k,m,r,R,t,S,D,b]: ")
		}

		if err != nil {
			return err
		}

		switch input {
		case "k", "add", "key", "keys":
			warningColor.Println("Note: For increased security, Vaulted defaults to substituting your credentials with temporary credentials.")
			warningColor.Println("      The key specified here may not match the key in your spawned session.")
			fmt.Println("")

			awsAccesskey, keyErr := interaction.ReadValue("Key ID: ")
			if keyErr != nil {
				return keyErr
			}
			awsSecretkey, secretErr := interaction.ReadValue("Secret: ")
			if secretErr != nil {
				return secretErr
			}

			// Save the old key in case the user aborts
			oldAWSKey := m.Vault.AWSKey
			m.Vault.AWSKey = &vaulted.AWSKey{
				AWSCredentials: vaulted.AWSCredentials{
					ID:     awsAccesskey,
					Secret: awsSecretkey,
				},
			}

			detectMFAMenu := DetectMFAMenu{Menu: m.Menu}
			detectErr := detectMFAMenu.Handler()
			if detectErr == ErrUserAbort {
				m.Vault.AWSKey = oldAWSKey
			}
		case "m", "mfa":
			if m.Vault.AWSKey != nil {
				// Save the old MFA in case the user aborts
				oldMFA := m.Vault.AWSKey.MFA
				m.Vault.AWSKey.MFA = ""

				detectMFAMenu := DetectMFAMenu{Menu: m.Menu}
				detectErr := detectMFAMenu.Handler()
				if detectErr == ErrUserAbort {
					m.Vault.AWSKey.MFA = oldMFA
					continue
				}

				if m.Vault.AWSKey.MFA == "" {
					var awsMfa string
					awsMfa, err = interaction.ReadValue("MFA ARN or serial number: ")
					if err == ErrUserAbort {
						m.Vault.AWSKey.MFA = oldMFA
						continue
					} else if err == nil {
						m.Vault.AWSKey.MFA = awsMfa
					}
				}
			} else {
				color.Red("Must associate an AWS key with the vault first")
			}
		case "r", "role":
			if m.Vault.AWSKey != nil {
				var awsRole string
				awsRole, err = interaction.ReadValue("Role ARN: ")
				if err == nil {
					m.Vault.AWSKey.Role = awsRole
				}
			} else {
				color.Red("Must associate an AWS key with the vault first")
			}
		case "R", "region":
			region, err := m.readRegion()
			if err != nil {
				return err
			}

			if region != "" {
				// Set the region
				if m.Vault.AWSKey == nil {
					m.Vault.AWSKey = &vaulted.AWSKey{}
				}
				m.Vault.AWSKey.Region = &region
			} else {
				// Unset the region
				if m.Vault.AWSKey != nil {
					m.Vault.AWSKey.Region = nil
				}
			}

		case "t", "temp", "temporary":
			if m.Vault.AWSKey != nil {
				forgoTempCredGeneration := !m.Vault.AWSKey.ForgoTempCredGeneration
				if !forgoTempCredGeneration && m.Vault.Duration > 36*time.Hour {
					var conf string
					warningColor.Println("Proceeding will adjust your vault duration to 36h (the maximum when using temporary creds).")
					conf, err = interaction.ReadPrompt("Do you wish to proceed? (y/n): ")
					if conf == "y" {
						m.Vault.Duration = 36 * time.Hour
					} else {
						fmt.Println("Temporary credentials not enabled.")
						continue
					}
				}

				m.Vault.AWSKey.ForgoTempCredGeneration = forgoTempCredGeneration
			} else {
				color.Red("Must associate an AWS key with the vault first")
			}
		case "S", "show", "hide":
			m.toggleHidden()
		case "D", "delete", "remove":
			if m.Vault.AWSKey != nil {
				var removeKey string
				removeKey, err = interaction.ReadValue("Delete your AWS key? (y/n): ")
				if err == nil {
					if removeKey == "y" {
						m.Vault.AWSKey = nil
					}
				}
			} else {
				color.Red("Must associate an AWS key with the vault first")
			}
		case "b", "back":
			return nil
		case "q", "quit", "exit":
			var confirm string
			confirm, err = interaction.ReadValue("Are you sure you wish to save and exit the vault? (y/n): ")
			if err == nil {
				if confirm == "y" {
					return ErrSaveAndExit
				}
			}
		case "?", "help":
			m.Help()
		default:
			color.Red("Command not recognized")
		}

		if err != nil {
			return err
		}
	}
}

func (m *AWSMenu) readRegion() (string, error) {
	region, err := interaction.ReadValue("Region: ")
	if err != nil {
		return "", err
	}

	if !sdkRegions()[region] {
		fmt.Printf("\n%s%s%s\n", warningColor.Sprint("WARNING: "), region, warningColor.Sprint(" doesn't appear to be a valid region."))
	}

	return region, nil
}

func (m *AWSMenu) Printer() {
	color.Cyan("\nAWS Key:")
	if m.Vault.AWSKey != nil {
		green.Printf("  Key ID: ")
		fmt.Printf("%s\n", m.Vault.AWSKey.ID)
		green.Printf("  Secret: ")
		if m.Menu.ShowHidden {
			fmt.Printf("%s\n", m.Vault.AWSKey.Secret)
		} else {
			fmt.Printf("%s\n", faintColor.Sprint("<hidden>"))
		}
		green.Printf("  MFA: ")
		if m.Vault.AWSKey.MFA == "" {
			var warning string
			if !m.Vault.AWSKey.ForgoTempCredGeneration {
				warning = warningColor.Sprint(" (warning: some APIs will not function without MFA (e.g. IAM))")
			}
			fmt.Printf("%s %s\n", faintColor.Sprint("<not configured>"), warning)
		} else {
			fmt.Printf("%s\n", m.Vault.AWSKey.MFA)
		}
		if m.Vault.AWSKey.Role != "" {
			green.Printf("  Role: ")
			fmt.Printf("%s\n", m.Vault.AWSKey.Role)
		}
		green.Printf("  Substitute with temporary credentials: ")
		fmt.Printf("%t\n", !m.Vault.AWSKey.ForgoTempCredGeneration)

		green.Printf("  Region: ")
		if m.Vault.AWSKey.Region == nil || *m.Vault.AWSKey.Region == "" {
			fmt.Printf("%s\n", faintColor.Sprint("<global>"))
		} else {
			var unrecognized string
			if !sdkRegions()[*m.Vault.AWSKey.Region] {
				unrecognized = warningColor.Sprintf(" (unrecognized region)")
			}

			fmt.Printf("%s%s\n", *m.Vault.AWSKey.Region, unrecognized)
		}
	} else {
		fmt.Println("  [Empty]")
	}
}
<commit_msg>Fix loss of AWS configuration when rotating keys<commit_after>package menu

import (
	"fmt"
	"time"

	"github.com/aws/aws-sdk-go/aws/endpoints"
	"github.com/fatih/color"

	"github.com/miquella/vaulted/lib"
)

// AWSMenu The menu type for the AWS edit tree
type AWSMenu struct {
	*Menu
}

func sdkRegions() map[string]bool {
	regions := make(map[string]bool)
	for _, partition := range endpoints.DefaultPartitions() {
		for region := range partition.Regions() {
			regions[region] = true
		}
	}
	return regions
}

func (m *AWSMenu) Help() {
	menuColor.Set()
	defer color.Unset()

	fmt.Println("k,key    - Key")
	fmt.Println("m,mfa    - MFA")
	fmt.Println("r,role   - Role")
	fmt.Println("R,region - Region")
	fmt.Println("t,temp   - Substitute with temporary credentials")
	fmt.Println("S,show   - Show/Hide Secrets")
	fmt.Println("D,delete - Delete")
	fmt.Println("?,help   - Help")
	fmt.Println("b,back   - Back")
	fmt.Println("q,quit   - Quit")
}

func (m *AWSMenu) Handler() error {
	var err error

	for {
		var input string
		m.Printer()
		if m.Vault.AWSKey == nil {
			input, err = interaction.ReadMenu("Edit AWS key [k,b]: ")
		} else {
			input, err = interaction.ReadMenu("Edit AWS key [k,m,r,R,t,S,D,b]: ")
		}

		if err != nil {
			return err
		}

		switch input {
		case "k", "add", "key", "keys":
			warningColor.Println("Note: For increased security, Vaulted defaults to substituting your credentials with temporary credentials.")
			warningColor.Println("      The key specified here may not match the key in your spawned session.")
			fmt.Println("")

			awsAccesskey, keyErr := interaction.ReadValue("Key ID: ")
			if keyErr != nil {
				return keyErr
			}
			awsSecretkey, secretErr := interaction.ReadValue("Secret: ")
			if secretErr != nil {
				return secretErr
			}

			// Copy the key to prevent other values from being lost
			oldAWSKey := m.Vault.AWSKey
			if oldAWSKey == nil {
				m.Vault.AWSKey = &vaulted.AWSKey{}
			} else {
				newAWSKey := *oldAWSKey
				m.Vault.AWSKey = &newAWSKey
			}

			m.Vault.AWSKey.ID = awsAccesskey
			m.Vault.AWSKey.Secret = awsSecretkey

			// Attempt to autoconfigure the MFA
			detectMFAMenu := DetectMFAMenu{Menu: m.Menu}
			detectErr := detectMFAMenu.Handler()
			if detectErr == ErrUserAbort {
				m.Vault.AWSKey = oldAWSKey
			}
		case "m", "mfa":
			if m.Vault.AWSKey != nil {
				// Save the old MFA in case the user aborts
				oldMFA := m.Vault.AWSKey.MFA
				m.Vault.AWSKey.MFA = ""

				detectMFAMenu := DetectMFAMenu{Menu: m.Menu}
				detectErr := detectMFAMenu.Handler()
				if detectErr == ErrUserAbort {
					m.Vault.AWSKey.MFA = oldMFA
					continue
				}

				if m.Vault.AWSKey.MFA == "" {
					var awsMfa string
					awsMfa, err = interaction.ReadValue("MFA ARN or serial number: ")
					if err == ErrUserAbort {
						m.Vault.AWSKey.MFA = oldMFA
						continue
					} else if err == nil {
						m.Vault.AWSKey.MFA = awsMfa
					}
				}
			} else {
				color.Red("Must associate an AWS key with the vault first")
			}
		case "r", "role":
			if m.Vault.AWSKey != nil {
				var awsRole string
				awsRole, err = interaction.ReadValue("Role ARN: ")
				if err == nil {
					m.Vault.AWSKey.Role = awsRole
				}
			} else {
				color.Red("Must associate an AWS key with the vault first")
			}
		case "R", "region":
			region, err := m.readRegion()
			if err != nil {
				return err
			}

			if region != "" {
				// Set the region
				if m.Vault.AWSKey == nil {
					m.Vault.AWSKey = &vaulted.AWSKey{}
				}
				m.Vault.AWSKey.Region = &region
			} else {
				// Unset the region
				if m.Vault.AWSKey != nil {
					m.Vault.AWSKey.Region = nil
				}
			}

		case "t", "temp", "temporary":
			if m.Vault.AWSKey != nil {
				forgoTempCredGeneration := !m.Vault.AWSKey.ForgoTempCredGeneration
				if !forgoTempCredGeneration && m.Vault.Duration > 36*time.Hour {
					var conf string
					warningColor.Println("Proceeding will adjust your vault duration to 36h (the maximum when using temporary creds).")
					conf, err = interaction.ReadPrompt("Do you wish to proceed? (y/n): ")
					if conf == "y" {
						m.Vault.Duration = 36 * time.Hour
					} else {
						fmt.Println("Temporary credentials not enabled.")
						continue
					}
				}

				m.Vault.AWSKey.ForgoTempCredGeneration = forgoTempCredGeneration
			} else {
				color.Red("Must associate an AWS key with the vault first")
			}
		case "S", "show", "hide":
			m.toggleHidden()
		case "D", "delete", "remove":
			if m.Vault.AWSKey != nil {
				var removeKey string
				removeKey, err = interaction.ReadValue("Delete your AWS key? (y/n): ")
				if err == nil {
					if removeKey == "y" {
						m.Vault.AWSKey = nil
					}
				}
			} else {
				color.Red("Must associate an AWS key with the vault first")
			}
		case "b", "back":
			return nil
		case "q", "quit", "exit":
			var confirm string
			confirm, err = interaction.ReadValue("Are you sure you wish to save and exit the vault? (y/n): ")
			if err == nil {
				if confirm == "y" {
					return ErrSaveAndExit
				}
			}
		case "?", "help":
			m.Help()
		default:
			color.Red("Command not recognized")
		}

		if err != nil {
			return err
		}
	}
}

func (m *AWSMenu) readRegion() (string, error) {
	region, err := interaction.ReadValue("Region: ")
	if err != nil {
		return "", err
	}

	if !sdkRegions()[region] {
		fmt.Printf("\n%s%s%s\n", warningColor.Sprint("WARNING: "), region, warningColor.Sprint(" doesn't appear to be a valid region."))
	}

	return region, nil
}

func (m *AWSMenu) Printer() {
	color.Cyan("\nAWS Key:")
	if m.Vault.AWSKey != nil {
		green.Printf("  Key ID: ")
		fmt.Printf("%s\n", m.Vault.AWSKey.ID)
		green.Printf("  Secret: ")
		if m.Menu.ShowHidden {
			fmt.Printf("%s\n", m.Vault.AWSKey.Secret)
		} else {
			fmt.Printf("%s\n", faintColor.Sprint("<hidden>"))
		}
		green.Printf("  MFA: ")
		if m.Vault.AWSKey.MFA == "" {
			var warning string
			if !m.Vault.AWSKey.ForgoTempCredGeneration {
				warning = warningColor.Sprint(" (warning: some APIs will not function without MFA (e.g. IAM))")
			}
			fmt.Printf("%s %s\n", faintColor.Sprint("<not configured>"), warning)
		} else {
			fmt.Printf("%s\n", m.Vault.AWSKey.MFA)
		}
		if m.Vault.AWSKey.Role != "" {
			green.Printf("  Role: ")
			fmt.Printf("%s\n", m.Vault.AWSKey.Role)
		}
		green.Printf("  Substitute with temporary credentials: ")
		fmt.Printf("%t\n", !m.Vault.AWSKey.ForgoTempCredGeneration)

		green.Printf("  Region: ")
		if m.Vault.AWSKey.Region == nil || *m.Vault.AWSKey.Region == "" {
			fmt.Printf("%s\n", faintColor.Sprint("<global>"))
		} else {
			var unrecognized string
			if !sdkRegions()[*m.Vault.AWSKey.Region] {
				unrecognized = warningColor.Sprintf(" (unrecognized region)")
			}

			fmt.Printf("%s%s\n", *m.Vault.AWSKey.Region, unrecognized)
		}
	} else {
		fmt.Println("  [Empty]")
	}
}
<|endoftext|>


<commit_before>package archive

import (
	"bytes"
	"fmt"
	"os"
	"path"

	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
)

type Archive struct {
	root string
}

func NewArchive(root string) Archive {
	return Archive{root: root}
}

func (a Archive) Suite(name string) (*Suite, error) {
	inRelease := path.Join(a.root, "dists", name, "InRelease")
	suite := Suite{componentEncoders: map[string]*encoderTarget{}}

	fd, err := os.Open(inRelease)
	if err != nil {
		return nil, err
	}

	defer fd.Close()
	return &suite, control.Unmarshal(&suite, fd)
}

func (a Archive) Engross(suite Suite) error {
	for _, target := range suite.componentEncoders {
		fmt.Printf("%s", target.Holder.String())
	}
	return nil
}

type Suite struct {
	control.Paragraph

	Description   string
	Origin        string
	Label         string
	Version       string
	Suite         string
	Codename      string
	Components    []string `delim:" "`
	Architectures []dependency.Arch

	componentEncoders map[string]*encoderTarget
}

type encoderTarget struct {
	Encoder *control.Encoder
	Holder  *bytes.Buffer
}

func (s Suite) HasComponent(component string) bool {
	for _, el := range s.Components {
		if component == el {
			return true
		}
	}
	return false
}

func (s Suite) getEncoder(component string) (*encoderTarget, error) {
	if encoder, ok := s.componentEncoders[component]; ok {
		return encoder, nil
	}

	if s.HasComponent(component) {
		target := encoderTarget{
			Encoder: nil,
			Holder:  &bytes.Buffer{},
			/* XXX: Add Hashers */
		}
		encoder, err := control.NewEncoder(target.Holder)
		if err != nil {
			return nil, err
		}
		target.Encoder = encoder
		s.componentEncoders[component] = &target
		return &target, nil
	} else {
		return nil, fmt.Errorf("No such component: '%s'", component)
	}

}

func (s Suite) AddPackageTo(component string, entry Package) error {
	encoder, err := s.getEncoder(component)
	if err != nil {
		return nil
	}
	return encoder.Encoder.Encode(entry)
}

// vim: foldmethod=marker
<commit_msg>clean up interface; still not happy<commit_after>package archive

import (
	"os"
	"path"

	"pault.ag/go/debian/control"
)

type Archive struct {
	root string
}

func NewArchive(root string) Archive {
	return Archive{root: root}
}

func (a Archive) Suite(name string) (*Suite, error) {
	inRelease := path.Join(a.root, "dists", name, "InRelease")
	suite := Suite{
		Packages: map[string][]Package{},
	}

	fd, err := os.Open(inRelease)
	if err != nil {
		return nil, err
	}

	defer fd.Close()
	return &suite, control.Unmarshal(&suite, fd)
}

type Suite struct {
	control.Paragraph

	Description string
	Origin      string
	Label       string
	Version     string
	Suite       string
	Codename    string

	Packages map[string][]Package
}

func (s Suite) Components() []string {
	components := []string{}
	for component, _ := range s.Packages {
		components = append(components, component)
	}
	return components
}

func (s Suite) AddPackageTo(component string, pkg Package) {
	if els, ok := s.Packages[component]; ok {
		s.Packages[component] = append(els, pkg)
	} else {
		s.Packages[component] = []Package{pkg}
	}
}

// vim: foldmethod=marker
<|endoftext|>


<commit_before>package main

import (
	"archive/zip"
	"bitbucket.org/pkg/inflect"
	"bytes"
	"encoding/base64"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"strings"
)

type ForceConnectedApps []ForceConnectedApp

type ForceConnectedApp struct {
	Name string `xml:"fullName"`
	Id   string `xml:"id"`
}

type ForceMetadataDeployProblem struct {
	Changed     bool   `xml:"changed"`
	Created     bool   `xml:"created"`
	Deleted     bool   `xml:"deleted"`
	Filename    string `xml:"fileName"`
	Name        string `xml:"fullName"`
	Problem     string `xml:"problem"`
	ProblemType string `xml:"problemType"`
	Success     bool   `xml:"success"`
}

type ForceMetadataQueryElement struct {
	Name    string
	Members string
}

type ForceMetadataQuery []ForceMetadataQueryElement

type ForceMetadataFiles map[string][]byte

type ForceMetadata struct {
	ApiVersion string
	Force      *Force
}

func NewForceMetadata(force *Force) (fm *ForceMetadata) {
	fm = &ForceMetadata{ApiVersion: "28.0", Force: force}
	return
}

func (fm *ForceMetadata) CheckStatus(id string) (err error) {
	body, err := fm.soapExecute("checkStatus", fmt.Sprintf("<id>%s</id>", id))
	if err != nil {
		return
	}
	var status struct {
		Done    bool   `xml:"Body>checkStatusResponse>result>done"`
		State   string `xml:"Body>checkStatusResponse>result>state"`
		Message string `xml:"Body>checkStatusResponse>result>message"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	switch {
	case !status.Done:
		return fm.CheckStatus(id)
	case status.State == "Error":
		return errors.New(status.Message)
	}
	return
}

func (fm *ForceMetadata) CheckDeployStatus(id string) (problems []ForceMetadataDeployProblem, err error) {
	body, err := fm.soapExecute("checkDeployStatus", fmt.Sprintf("<id>%s</id>", id))
	if err != nil {
		return
	}
	var result struct {
		Problems []ForceMetadataDeployProblem `xml:"Body>checkDeployStatusResponse>result>messages"`
	}
	if err = xml.Unmarshal(body, &result); err != nil {
		return
	}
	problems = result.Problems
	return
}

func (fm *ForceMetadata) CheckRetrieveStatus(id string) (files ForceMetadataFiles, err error) {
	body, err := fm.soapExecute("checkRetrieveStatus", fmt.Sprintf("<id>%s</id>", id))
	if err != nil {
		return
	}
	var status struct {
		ZipFile string `xml:"Body>checkRetrieveStatusResponse>result>zipFile"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	data, err := base64.StdEncoding.DecodeString(status.ZipFile)
	if err != nil {
		return
	}
	zipfiles, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
	if err != nil {
		return
	}
	files = make(map[string][]byte)
	for _, file := range zipfiles.File {
		fd, _ := file.Open()
		defer fd.Close()
		data, _ := ioutil.ReadAll(fd)
		files[file.Name] = data
	}
	return
}

func (fm *ForceMetadata) CreateConnectedApp(name, callback string) (err error) {
	soap := `
		<metadata xsi:type="ConnectedApp">
			<fullName>%s</fullName>
			<version>29.0</version>
			%s
			<contactEmail>%s</contactEmail>
			<oauthConfig>
				<callbackUrl>%s</callbackUrl>
				<scopes>Full</scopes>
				<scopes>RefreshToken</scopes>
			</oauthConfig>
		</metadata>
	`
	me, err := fm.Force.Whoami()
	if err != nil {
		return err
	}
	email := me["Email"]
	body, err := fm.soapExecute("create", fmt.Sprintf(soap, name, name, email, callback))
	if err != nil {
		return err
	}
	var status struct {
		Id string `xml:"Body>createResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	return
}

func (fm *ForceMetadata) CreateCustomField(object, field, typ string) (err error) {
	soap := `
		<metadata xsi:type="CustomField" xmlns:cmd="http://soap.sforce.com/2006/04/metadata">
			<fullName>%s.%s__c</fullName>
			%s
			%s
		</metadata>
	`
	soapField := ""
	switch strings.ToLower(typ) {
	case "text", "string":
		soapField = "<type>Text</type><length>255</length>"
	case "datetime":
		soapField = "<type>DateTime</type>"
	case "number", "int":
		soapField = "<type>Number</type><precision>10</precision><scale>0</scale>"
	case "float":
		soapField = "<type>Number</type><precision>10</precision><scale>2</scale>"
	default:
		ErrorAndExit("unable to create field type: %s", typ)
	}
	body, err := fm.soapExecute("create", fmt.Sprintf(soap, object, field, field, soapField))
	if err != nil {
		return err
	}
	var status struct {
		Id string `xml:"Body>createResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	return
}

func (fm *ForceMetadata) DeleteCustomField(object, field string) (err error) {
	soap := `
		<metadata xsi:type="CustomField" xmlns:cmd="http://soap.sforce.com/2006/04/metadata">
			<fullName>%s.%s</fullName>
		</metadata>
	`
	body, err := fm.soapExecute("delete", fmt.Sprintf(soap, object, field))
	if err != nil {
		return err
	}
	var status struct {
		Id string `xml:"Body>deleteResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	return
}

func (fm *ForceMetadata) CreateCustomObject(object string) (err error) {
	soap := `
		<metadata xsi:type="CustomObject" xmlns:cmd="http://soap.sforce.com/2006/04/metadata">
			<fullName>%s__c</fullName>
			%s
			<pluralLabel>%s</pluralLabel>
			<deploymentStatus>Deployed</deploymentStatus>
			<sharingModel>ReadWrite</sharingModel>
			<nameField>
				ID
				<type>AutoNumber</type>
			</nameField>
		</metadata>
	`
	body, err := fm.soapExecute("create", fmt.Sprintf(soap, object, object, inflect.Pluralize(object)))
	if err != nil {
		return err
	}
	var status struct {
		Id string `xml:"Body>createResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	return
}

func (fm *ForceMetadata) DeleteCustomObject(object string) (err error) {
	soap := `
		<metadata xsi:type="CustomObject" xmlns:cmd="http://soap.sforce.com/2006/04/metadata">
			<fullName>%s</fullName>
		</metadata>
	`
	body, err := fm.soapExecute("delete", fmt.Sprintf(soap, object))
	if err != nil {
		return err
	}
	var status struct {
		Id string `xml:"Body>deleteResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	return
}

func (fm *ForceMetadata) Deploy(files ForceMetadataFiles) (problems []ForceMetadataDeployProblem, err error) {
	soap := `
		<zipFile>%s</zipFile>
	`
	zipfile := new(bytes.Buffer)
	zipper := zip.NewWriter(zipfile)
	for name, data := range files {
		wr, err := zipper.Create(fmt.Sprintf("unpackaged/%s", name))
		if err != nil {
			return nil, err
		}
		wr.Write(data)
	}
	zipper.Close()
	encoded := base64.StdEncoding.EncodeToString(zipfile.Bytes())
	body, err := fm.soapExecute("deploy", fmt.Sprintf(soap, encoded))
	if err != nil {
		return
	}
	var status struct {
		Id string `xml:"Body>deployResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	messages, err := fm.CheckDeployStatus(status.Id)
	for _, problem := range messages {
		if !problem.Success {
			problems = append(problems, problem)
		}
	}
	return
}

func (fm *ForceMetadata) Retrieve(query ForceMetadataQuery) (files ForceMetadataFiles, err error) {
	soap := `
		<retrieveRequest>
			<apiVersion>29.0</apiVersion>
			<unpackaged>
				%s
			</unpackaged>
		</retrieveRequest>
	`
	soapType := `
		<types>
			<name>%s</name>
			<members>%s</members>
		</types>
	`
	types := ""
	for _, element := range query {
		types += fmt.Sprintf(soapType, element.Name, element.Members)
	}
	body, err := fm.soapExecute("retrieve", fmt.Sprintf(soap, types))
	if err != nil {
		return
	}
	var status struct {
		Id string `xml:"Body>retrieveResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	raw_files, err := fm.CheckRetrieveStatus(status.Id)
	if err != nil {
		return
	}
	files = make(ForceMetadataFiles)
	for raw_name, data := range raw_files {
		name := strings.Replace(raw_name, "unpackaged/", "", -1)
		files[name] = data
	}
	return
}

func (fm *ForceMetadata) ListMetadata(query string) (res []byte, err error) {
	return fm.soapExecute("listMetadata", fmt.Sprintf("<queries><type>%s</type></queries>", query))
}

func (fm *ForceMetadata) ListConnectedApps() (apps ForceConnectedApps, err error) {
	originalVersion := fm.ApiVersion
	fm.ApiVersion = "29.0"
	body, err := fm.ListMetadata("ConnectedApp")
	fm.ApiVersion = originalVersion
	if err != nil {
		return
	}
	var res struct {
		ConnectedApps []ForceConnectedApp `xml:"Body>listMetadataResponse>result"`
	}
	if err = xml.Unmarshal(body, &res); err != nil {
		return
	}
	apps = res.ConnectedApps
	return
}

func (fm *ForceMetadata) soapExecute(action, query string) (response []byte, err error) {
	login, err := fm.Force.Get(fm.Force.Credentials.Id)
	if err != nil {
		return
	}
	url := strings.Replace(login["urls"].(map[string]interface{})["metadata"].(string), "{version}", fm.ApiVersion, 1)
	soap := NewSoap(url, "http://soap.sforce.com/2006/04/metadata", fm.Force.Credentials.AccessToken)
	response, err = soap.Execute(action, query)
	return
}
<commit_msg>Adding ability to create TextArea fields<commit_after>package main

import (
	"archive/zip"
	"bitbucket.org/pkg/inflect"
	"bytes"
	"encoding/base64"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"strings"
)

type ForceConnectedApps []ForceConnectedApp

type ForceConnectedApp struct {
	Name string `xml:"fullName"`
	Id   string `xml:"id"`
}

type ForceMetadataDeployProblem struct {
	Changed     bool   `xml:"changed"`
	Created     bool   `xml:"created"`
	Deleted     bool   `xml:"deleted"`
	Filename    string `xml:"fileName"`
	Name        string `xml:"fullName"`
	Problem     string `xml:"problem"`
	ProblemType string `xml:"problemType"`
	Success     bool   `xml:"success"`
}

type ForceMetadataQueryElement struct {
	Name    string
	Members string
}

type ForceMetadataQuery []ForceMetadataQueryElement

type ForceMetadataFiles map[string][]byte

type ForceMetadata struct {
	ApiVersion string
	Force      *Force
}

func NewForceMetadata(force *Force) (fm *ForceMetadata) {
	fm = &ForceMetadata{ApiVersion: "28.0", Force: force}
	return
}

func (fm *ForceMetadata) CheckStatus(id string) (err error) {
	body, err := fm.soapExecute("checkStatus", fmt.Sprintf("<id>%s</id>", id))
	if err != nil {
		return
	}
	var status struct {
		Done    bool   `xml:"Body>checkStatusResponse>result>done"`
		State   string `xml:"Body>checkStatusResponse>result>state"`
		Message string `xml:"Body>checkStatusResponse>result>message"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	switch {
	case !status.Done:
		return fm.CheckStatus(id)
	case status.State == "Error":
		return errors.New(status.Message)
	}
	return
}

func (fm *ForceMetadata) CheckDeployStatus(id string) (problems []ForceMetadataDeployProblem, err error) {
	body, err := fm.soapExecute("checkDeployStatus", fmt.Sprintf("<id>%s</id>", id))
	if err != nil {
		return
	}
	var result struct {
		Problems []ForceMetadataDeployProblem `xml:"Body>checkDeployStatusResponse>result>messages"`
	}
	if err = xml.Unmarshal(body, &result); err != nil {
		return
	}
	problems = result.Problems
	return
}

func (fm *ForceMetadata) CheckRetrieveStatus(id string) (files ForceMetadataFiles, err error) {
	body, err := fm.soapExecute("checkRetrieveStatus", fmt.Sprintf("<id>%s</id>", id))
	if err != nil {
		return
	}
	var status struct {
		ZipFile string `xml:"Body>checkRetrieveStatusResponse>result>zipFile"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	data, err := base64.StdEncoding.DecodeString(status.ZipFile)
	if err != nil {
		return
	}
	zipfiles, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
	if err != nil {
		return
	}
	files = make(map[string][]byte)
	for _, file := range zipfiles.File {
		fd, _ := file.Open()
		defer fd.Close()
		data, _ := ioutil.ReadAll(fd)
		files[file.Name] = data
	}
	return
}

func (fm *ForceMetadata) CreateConnectedApp(name, callback string) (err error) {
	soap := `
		<metadata xsi:type="ConnectedApp">
			<fullName>%s</fullName>
			<version>29.0</version>
			%s
			<contactEmail>%s</contactEmail>
			<oauthConfig>
				<callbackUrl>%s</callbackUrl>
				<scopes>Full</scopes>
				<scopes>RefreshToken</scopes>
			</oauthConfig>
		</metadata>
	`
	me, err := fm.Force.Whoami()
	if err != nil {
		return err
	}
	email := me["Email"]
	body, err := fm.soapExecute("create", fmt.Sprintf(soap, name, name, email, callback))
	if err != nil {
		return err
	}
	var status struct {
		Id string `xml:"Body>createResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	return
}

func (fm *ForceMetadata) CreateCustomField(object, field, typ string) (err error) {
	soap := `
		<metadata xsi:type="CustomField" xmlns:cmd="http://soap.sforce.com/2006/04/metadata">
			<fullName>%s.%s__c</fullName>
			%s
			%s
		</metadata>
	`
	soapField := ""
	switch strings.ToLower(typ) {
	case "text", "string":
		soapField = "<type>Text</type><length>255</length>"
	case "datetime":
		soapField = "<type>DateTime</type>"
	case "number", "int":
		soapField = "<type>Number</type><precision>10</precision><scale>0</scale>"
	case "float":
		soapField = "<type>Number</type><precision>10</precision><scale>2</scale>"
	case "textarea":
		soapField = "<type>TextArea</type>"
	default:
		ErrorAndExit("unable to create field type: %s", typ)
	}
	body, err := fm.soapExecute("create", fmt.Sprintf(soap, object, field, field, soapField))
	if err != nil {
		return err
	}
	var status struct {
		Id string `xml:"Body>createResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	return
}

func (fm *ForceMetadata) DeleteCustomField(object, field string) (err error) {
	soap := `
		<metadata xsi:type="CustomField" xmlns:cmd="http://soap.sforce.com/2006/04/metadata">
			<fullName>%s.%s</fullName>
		</metadata>
	`
	body, err := fm.soapExecute("delete", fmt.Sprintf(soap, object, field))
	if err != nil {
		return err
	}
	var status struct {
		Id string `xml:"Body>deleteResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	return
}

func (fm *ForceMetadata) CreateCustomObject(object string) (err error) {
	soap := `
		<metadata xsi:type="CustomObject" xmlns:cmd="http://soap.sforce.com/2006/04/metadata">
			<fullName>%s__c</fullName>
			%s
			<pluralLabel>%s</pluralLabel>
			<deploymentStatus>Deployed</deploymentStatus>
			<sharingModel>ReadWrite</sharingModel>
			<nameField>
				ID
				<type>AutoNumber</type>
			</nameField>
		</metadata>
	`
	body, err := fm.soapExecute("create", fmt.Sprintf(soap, object, object, inflect.Pluralize(object)))
	if err != nil {
		return err
	}
	var status struct {
		Id string `xml:"Body>createResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	return
}

func (fm *ForceMetadata) DeleteCustomObject(object string) (err error) {
	soap := `
		<metadata xsi:type="CustomObject" xmlns:cmd="http://soap.sforce.com/2006/04/metadata">
			<fullName>%s</fullName>
		</metadata>
	`
	body, err := fm.soapExecute("delete", fmt.Sprintf(soap, object))
	if err != nil {
		return err
	}
	var status struct {
		Id string `xml:"Body>deleteResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	return
}

func (fm *ForceMetadata) Deploy(files ForceMetadataFiles) (problems []ForceMetadataDeployProblem, err error) {
	soap := `
		<zipFile>%s</zipFile>
	`
	zipfile := new(bytes.Buffer)
	zipper := zip.NewWriter(zipfile)
	for name, data := range files {
		wr, err := zipper.Create(fmt.Sprintf("unpackaged/%s", name))
		if err != nil {
			return nil, err
		}
		wr.Write(data)
	}
	zipper.Close()
	encoded := base64.StdEncoding.EncodeToString(zipfile.Bytes())
	body, err := fm.soapExecute("deploy", fmt.Sprintf(soap, encoded))
	if err != nil {
		return
	}
	var status struct {
		Id string `xml:"Body>deployResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	messages, err := fm.CheckDeployStatus(status.Id)
	for _, problem := range messages {
		if !problem.Success {
			problems = append(problems, problem)
		}
	}
	return
}

func (fm *ForceMetadata) Retrieve(query ForceMetadataQuery) (files ForceMetadataFiles, err error) {
	soap := `
		<retrieveRequest>
			<apiVersion>29.0</apiVersion>
			<unpackaged>
				%s
			</unpackaged>
		</retrieveRequest>
	`
	soapType := `
		<types>
			<name>%s</name>
			<members>%s</members>
		</types>
	`
	types := ""
	for _, element := range query {
		types += fmt.Sprintf(soapType, element.Name, element.Members)
	}
	body, err := fm.soapExecute("retrieve", fmt.Sprintf(soap, types))
	if err != nil {
		return
	}
	var status struct {
		Id string `xml:"Body>retrieveResponse>result>id"`
	}
	if err = xml.Unmarshal(body, &status); err != nil {
		return
	}
	if err = fm.CheckStatus(status.Id); err != nil {
		return
	}
	raw_files, err := fm.CheckRetrieveStatus(status.Id)
	if err != nil {
		return
	}
	files = make(ForceMetadataFiles)
	for raw_name, data := range raw_files {
		name := strings.Replace(raw_name, "unpackaged/", "", -1)
		files[name] = data
	}
	return
}

func (fm *ForceMetadata) ListMetadata(query string) (res []byte, err error) {
	return fm.soapExecute("listMetadata", fmt.Sprintf("<queries><type>%s</type></queries>", query))
}

func (fm *ForceMetadata) ListConnectedApps() (apps ForceConnectedApps, err error) {
	originalVersion := fm.ApiVersion
	fm.ApiVersion = "29.0"
	body, err := fm.ListMetadata("ConnectedApp")
	fm.ApiVersion = originalVersion
	if err != nil {
		return
	}
	var res struct {
		ConnectedApps []ForceConnectedApp `xml:"Body>listMetadataResponse>result"`
	}
	if err = xml.Unmarshal(body, &res); err != nil {
		return
	}
	apps = res.ConnectedApps
	return
}

func (fm *ForceMetadata) soapExecute(action, query string) (response []byte, err error) {
	login, err := fm.Force.Get(fm.Force.Credentials.Id)
	if err != nil {
		return
	}
	url := strings.Replace(login["urls"].(map[string]interface{})["metadata"].(string), "{version}", fm.ApiVersion, 1)
	soap := NewSoap(url, "http://soap.sforce.com/2006/04/metadata", fm.Force.Credentials.AccessToken)
	response, err = soap.Execute(action, query)
	return
}
<|endoftext|>


<commit_before>package kafka

import (
	"context"
	"fmt"
	"net"
	"time"

	metadataAPI "github.com/segmentio/kafka-go/protocol/metadata"
)

// MetadataRequest represents a request sent to a kafka broker to retrieve its
// cluster metadata.
type MetadataRequest struct {
	// Address of the kafka broker to send the request to.
	Addr net.Addr

	// The list of topics to retrieve metadata for.
	Topics []string
}

// MetadatResponse represents a response from a kafka broker to a metadata
// request.
type MetadataResponse struct {
	// The amount of time that the broker throttled the request.
	Throttle time.Duration

	// The list of brokers registered to the cluster.
	Brokers []Broker

	// The list of topics available on the cluster.
	Topics []Topic
}

// Metadata sends a metadata request to a kafka broker and returns the response.
func (c *Client) Metadata(ctx context.Context, req *MetadataRequest) (*MetadataResponse, error) {
	m, err := c.roundTrip(ctx, req.Addr, &metadataAPI.Request{
		TopicNames: req.Topics,
	})

	if err != nil {
		return nil, fmt.Errorf("kafka.(*Client).Metadata: %w", err)
	}

	res := m.(*metadataAPI.Response)
	ret := &MetadataResponse{
		Throttle: makeDuration(res.ThrottleTimeMs),
		Brokers:  make([]Broker, len(res.Brokers)),
		Topics:   make([]Topic, len(res.Topics)),
	}

	brokers := make(map[int32]Broker, len(res.Brokers))

	for i, b := range res.Brokers {
		ret.Brokers[i] = Broker{
			Host: b.Host,
			Port: int(b.Port),
			ID:   int(b.NodeID),
			Rack: b.Rack,
		}
		brokers[b.NodeID] = ret.Brokers[i]
	}

	for i, t := range res.Topics {
		ret.Topics[i] = Topic{
			Name:       t.Name,
			Internal:   t.IsInternal,
			Partitions: make([]Partition, len(t.Partitions)),
			Error:      makeError(t.ErrorCode, ""),
		}

		for j, p := range t.Partitions {
			partition := Partition{
				Topic:    t.Name,
				ID:       int(p.PartitionIndex),
				Leader:   brokers[p.LeaderID],
				Replicas: make([]Broker, len(p.ReplicaNodes)),
				Isr:      make([]Broker, len(p.IsrNodes)),
				Error:    makeError(p.ErrorCode, ""),
			}

			for i, id := range p.ReplicaNodes {
				partition.Replicas[i] = brokers[id]
			}

			for i, id := range p.IsrNodes {
				partition.Isr[i] = brokers[id]
			}

			ret.Topics[i].Partitions[j] = partition
		}
	}

	return ret, nil
}

type topicMetadataRequestV1 []string

func (r topicMetadataRequestV1) size() int32 {
	return sizeofStringArray([]string(r))
}

func (r topicMetadataRequestV1) writeTo(wb *writeBuffer) {
	// communicate nil-ness to the broker by passing -1 as the array length.
	// for this particular request, the broker interpets a zero length array
	// as a request for no topics whereas a nil array is for all topics.
	if r == nil {
		wb.writeArrayLen(-1)
	} else {
		wb.writeStringArray([]string(r))
	}
}

type metadataResponseV1 struct {
	Brokers      []brokerMetadataV1
	ControllerID int32
	Topics       []topicMetadataV1
}

func (r metadataResponseV1) size() int32 {
	n1 := sizeofArray(len(r.Brokers), func(i int) int32 { return r.Brokers[i].size() })
	n2 := sizeofArray(len(r.Topics), func(i int) int32 { return r.Topics[i].size() })
	return 4 + n1 + n2
}

func (r metadataResponseV1) writeTo(wb *writeBuffer) {
	wb.writeArray(len(r.Brokers), func(i int) { r.Brokers[i].writeTo(wb) })
	wb.writeInt32(r.ControllerID)
	wb.writeArray(len(r.Topics), func(i int) { r.Topics[i].writeTo(wb) })
}

type brokerMetadataV1 struct {
	NodeID int32
	Host   string
	Port   int32
	Rack   string
}

func (b brokerMetadataV1) size() int32 {
	return 4 + 4 + sizeofString(b.Host) + sizeofString(b.Rack)
}

func (b brokerMetadataV1) writeTo(wb *writeBuffer) {
	wb.writeInt32(b.NodeID)
	wb.writeString(b.Host)
	wb.writeInt32(b.Port)
	wb.writeString(b.Rack)
}

type topicMetadataV1 struct {
	TopicErrorCode int16
	TopicName      string
	Internal       bool
	Partitions     []partitionMetadataV1
}

func (t topicMetadataV1) size() int32 {
	return 2 + 1 +
		sizeofString(t.TopicName) +
		sizeofArray(len(t.Partitions), func(i int) int32 { return t.Partitions[i].size() })
}

func (t topicMetadataV1) writeTo(wb *writeBuffer) {
	wb.writeInt16(t.TopicErrorCode)
	wb.writeString(t.TopicName)
	wb.writeBool(t.Internal)
	wb.writeArray(len(t.Partitions), func(i int) { t.Partitions[i].writeTo(wb) })
}

type partitionMetadataV1 struct {
	PartitionErrorCode int16
	PartitionID        int32
	Leader             int32
	Replicas           []int32
	Isr                []int32
}

func (p partitionMetadataV1) size() int32 {
	return 2 + 4 + 4 + sizeofInt32Array(p.Replicas) + sizeofInt32Array(p.Isr)
}

func (p partitionMetadataV1) writeTo(wb *writeBuffer) {
	wb.writeInt16(p.PartitionErrorCode)
	wb.writeInt32(p.PartitionID)
	wb.writeInt32(p.Leader)
	wb.writeInt32Array(p.Replicas)
	wb.writeInt32Array(p.Isr)
}
<commit_msg>expose cluster id and controller in metadata response<commit_after>package kafka

import (
	"context"
	"fmt"
	"net"
	"time"

	metadataAPI "github.com/segmentio/kafka-go/protocol/metadata"
)

// MetadataRequest represents a request sent to a kafka broker to retrieve its
// cluster metadata.
type MetadataRequest struct {
	// Address of the kafka broker to send the request to.
	Addr net.Addr

	// The list of topics to retrieve metadata for.
	Topics []string
}

// MetadatResponse represents a response from a kafka broker to a metadata
// request.
type MetadataResponse struct {
	// The amount of time that the broker throttled the request.
	Throttle time.Duration

	// Name of the kafka cluster that client retrieved metadata from.
	ClusterID string

	// The broker which is currently the controller for the cluster.
	Controller Broker

	// The list of brokers registered to the cluster.
	Brokers []Broker

	// The list of topics available on the cluster.
	Topics []Topic
}

// Metadata sends a metadata request to a kafka broker and returns the response.
func (c *Client) Metadata(ctx context.Context, req *MetadataRequest) (*MetadataResponse, error) {
	m, err := c.roundTrip(ctx, req.Addr, &metadataAPI.Request{
		TopicNames: req.Topics,
	})

	if err != nil {
		return nil, fmt.Errorf("kafka.(*Client).Metadata: %w", err)
	}

	res := m.(*metadataAPI.Response)
	ret := &MetadataResponse{
		Throttle:  makeDuration(res.ThrottleTimeMs),
		Brokers:   make([]Broker, len(res.Brokers)),
		Topics:    make([]Topic, len(res.Topics)),
		ClusterID: res.ClusterID,
	}

	brokers := make(map[int32]Broker, len(res.Brokers))

	for i, b := range res.Brokers {
		broker := Broker{
			Host: b.Host,
			Port: int(b.Port),
			ID:   int(b.NodeID),
			Rack: b.Rack,
		}

		ret.Brokers[i] = broker
		brokers[b.NodeID] = broker

		if b.NodeID == res.ControllerID {
			ret.Controller = broker
		}
	}

	for i, t := range res.Topics {
		ret.Topics[i] = Topic{
			Name:       t.Name,
			Internal:   t.IsInternal,
			Partitions: make([]Partition, len(t.Partitions)),
			Error:      makeError(t.ErrorCode, ""),
		}

		for j, p := range t.Partitions {
			partition := Partition{
				Topic:    t.Name,
				ID:       int(p.PartitionIndex),
				Leader:   brokers[p.LeaderID],
				Replicas: make([]Broker, len(p.ReplicaNodes)),
				Isr:      make([]Broker, len(p.IsrNodes)),
				Error:    makeError(p.ErrorCode, ""),
			}

			for i, id := range p.ReplicaNodes {
				partition.Replicas[i] = brokers[id]
			}

			for i, id := range p.IsrNodes {
				partition.Isr[i] = brokers[id]
			}

			ret.Topics[i].Partitions[j] = partition
		}
	}

	return ret, nil
}

type topicMetadataRequestV1 []string

func (r topicMetadataRequestV1) size() int32 {
	return sizeofStringArray([]string(r))
}

func (r topicMetadataRequestV1) writeTo(wb *writeBuffer) {
	// communicate nil-ness to the broker by passing -1 as the array length.
	// for this particular request, the broker interpets a zero length array
	// as a request for no topics whereas a nil array is for all topics.
	if r == nil {
		wb.writeArrayLen(-1)
	} else {
		wb.writeStringArray([]string(r))
	}
}

type metadataResponseV1 struct {
	Brokers      []brokerMetadataV1
	ControllerID int32
	Topics       []topicMetadataV1
}

func (r metadataResponseV1) size() int32 {
	n1 := sizeofArray(len(r.Brokers), func(i int) int32 { return r.Brokers[i].size() })
	n2 := sizeofArray(len(r.Topics), func(i int) int32 { return r.Topics[i].size() })
	return 4 + n1 + n2
}

func (r metadataResponseV1) writeTo(wb *writeBuffer) {
	wb.writeArray(len(r.Brokers), func(i int) { r.Brokers[i].writeTo(wb) })
	wb.writeInt32(r.ControllerID)
	wb.writeArray(len(r.Topics), func(i int) { r.Topics[i].writeTo(wb) })
}

type brokerMetadataV1 struct {
	NodeID int32
	Host   string
	Port   int32
	Rack   string
}

func (b brokerMetadataV1) size() int32 {
	return 4 + 4 + sizeofString(b.Host) + sizeofString(b.Rack)
}

func (b brokerMetadataV1) writeTo(wb *writeBuffer) {
	wb.writeInt32(b.NodeID)
	wb.writeString(b.Host)
	wb.writeInt32(b.Port)
	wb.writeString(b.Rack)
}

type topicMetadataV1 struct {
	TopicErrorCode int16
	TopicName      string
	Internal       bool
	Partitions     []partitionMetadataV1
}

func (t topicMetadataV1) size() int32 {
	return 2 + 1 +
		sizeofString(t.TopicName) +
		sizeofArray(len(t.Partitions), func(i int) int32 { return t.Partitions[i].size() })
}

func (t topicMetadataV1) writeTo(wb *writeBuffer) {
	wb.writeInt16(t.TopicErrorCode)
	wb.writeString(t.TopicName)
	wb.writeBool(t.Internal)
	wb.writeArray(len(t.Partitions), func(i int) { t.Partitions[i].writeTo(wb) })
}

type partitionMetadataV1 struct {
	PartitionErrorCode int16
	PartitionID        int32
	Leader             int32
	Replicas           []int32
	Isr                []int32
}

func (p partitionMetadataV1) size() int32 {
	return 2 + 4 + 4 + sizeofInt32Array(p.Replicas) + sizeofInt32Array(p.Isr)
}

func (p partitionMetadataV1) writeTo(wb *writeBuffer) {
	wb.writeInt16(p.PartitionErrorCode)
	wb.writeInt32(p.PartitionID)
	wb.writeInt32(p.Leader)
	wb.writeInt32Array(p.Replicas)
	wb.writeInt32Array(p.Isr)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"

	"github.com/alphagov/metadata-api/content_api"
	"github.com/alphagov/metadata-api/need_api"
)

type ResponseInfo struct {
	Status string `json:"status"`
}

type Metadata struct {
	Artefact     *content_api.Artefact `json:"artefact"`
	Needs        []*need_api.Need      `json:"need"`
	ResponseInfo *ResponseInfo         `json:"_response_info"`
}

func ParseMetadataResponse(response []byte) (*Metadata, error) {
	metadata := &Metadata{}
	if err := json.Unmarshal(response, &metadata); err != nil {
		return nil, err
	}

	return metadata, nil
}
<commit_msg>Use the plural form of need as we're providing an array response<commit_after>package main

import (
	"encoding/json"

	"github.com/alphagov/metadata-api/content_api"
	"github.com/alphagov/metadata-api/need_api"
)

type ResponseInfo struct {
	Status string `json:"status"`
}

type Metadata struct {
	Artefact     *content_api.Artefact `json:"artefact"`
	Needs        []*need_api.Need      `json:"needs"`
	ResponseInfo *ResponseInfo         `json:"_response_info"`
}

func ParseMetadataResponse(response []byte) (*Metadata, error) {
	metadata := &Metadata{}
	if err := json.Unmarshal(response, &metadata); err != nil {
		return nil, err
	}

	return metadata, nil
}
<|endoftext|>


<commit_before>package logic

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"
	"github.com/stampzilla/stampzilla-go/v2/nodes/stampzilla-server/models/devices"
	"github.com/stampzilla/stampzilla-go/v2/nodes/stampzilla-server/websocket"
)

/* rules.json example:
{
    "e8092b86-1261-44cd-ab64-38121df58a79": {
        "name": "All off",
        "enabled": true,
        "active": false,
        "uuid": "e8092b86-1261-44cd-ab64-38121df58a79",
        "expression": "devices['fd230f30-6d84-4507-8ace-c1ec715be51e.1'].on == true",
        "for": "5m",
        "actions": [
            "1s",
            "c7d352bb-23f4-468c-b476-f76599c09a0d"
        ]
    },
    "1fd25327-f43c-4a00-aa67-3969dfed06b5": {
        "name": "chromecast p\u00e5",
        "enabled": true,
        "active": false,
        "uuid": "1fd25327-f43c-4a00-aa67-3969dfed06b5",
        "expression": "devices['asdf.123'].on == true ",
        "for": "5m",
        "actions": [
            "1m",
            "c7d352bb-23f4-468c-b476-f76599c09a0d"
        ],
		"labels": [
			"livingroom",
			"kitchen"
		]
    }
}
*/

// Rules is a list of rules.
type Rules map[string]*Rule

// Logic is the main struct.
type Logic struct {
	StateStore           *SavedStateStore
	Rules                map[string]*Rule
	devices              *devices.List
	onReportState        func(string, devices.State)
	onTriggerDestination func(string, string) error
	// ActionProgressChan chan ActionProgress
	sync.RWMutex
	sync.WaitGroup
	c               chan func()
	WebsocketSender websocket.Sender
}

/*
type ActionProgress struct {
	Address string `json:"address"`
	Uuid    string `json:"uuid"`
	Step    int    `json:"step"`
}
*/

// New returns a new logic that is ready to use.
func New(sss *SavedStateStore, websocketSender websocket.Sender) *Logic {
	l := &Logic{
		devices: devices.NewList(),
		// ActionProgressChan: make(chan ActionProgress, 100),
		Rules:                make(map[string]*Rule),
		StateStore:           sss,
		onReportState:        func(string, devices.State) {},
		onTriggerDestination: func(string, string) error { return nil },
		c:                    make(chan func()),
		WebsocketSender:      websocketSender,
	}
	return l
}

// AddRule adds a new logic rule. Mainly used in tests.
func (l *Logic) AddRule(name string) *Rule {
	r := &Rule{Name_: name, Uuid_: uuid.New().String()}
	l.Lock()
	defer l.Unlock()
	l.Rules[r.Uuid()] = r
	return r
}

// GetRules returns a list of Rules.
func (l *Logic) GetRules() Rules {
	l.RLock()
	defer l.RUnlock()
	return l.Rules
}

// SetRules overwrites all rules in logic.
func (l *Logic) SetRules(rules Rules) {
	l.Lock()
	l.Rules = rules
	l.Unlock()

	// Trigger an evaluation of the new rules
	l.c <- func() {}
}

func (l *Logic) OnReportState(callback func(string, devices.State)) {
	l.onReportState = callback
}

func (l *Logic) OnTriggerDestination(callback func(string, string) error) {
	l.onTriggerDestination = callback
}

// Start starts the logic worker.
func (l *Logic) Start(ctx context.Context) {
	l.Add(1)
	logrus.Info("logic: starting worker")
	go l.worker(ctx)
}

func (l *Logic) worker(ctx context.Context) {
	defer l.Done()
	for {
		select {
		case f := <-l.c:
			f()
			l.EvaluateRules(ctx)
		case <-ctx.Done():
			logrus.Info("logic: stopping worker")
			return
		}
	}
}

// UpdateDevice update the state in the logic store with the new state from the device.
func (l *Logic) UpdateDevice(dev *devices.Device) {
	l.c <- func() {
		l.updateDevice(dev)
	}
}

func (l *Logic) updateDevice(dev *devices.Device) {
	if oldDev := l.devices.Get(dev.ID); oldDev != nil {
		if diff := oldDev.State.Diff(dev.State); len(diff) > 0 {
			oldDev.Lock()
			oldDev.State.MergeWith(diff)
			oldDev.Unlock()
		}
		return
	}
	l.devices.Add(dev)
}

// EvaluateRules loops over each rule and run evaluation on them.
func (l *Logic) EvaluateRules(ctx context.Context) {
	for _, rule := range l.Rules {
		if !rule.Enabled {
			continue
		}
		evaluation := l.evaluateRule(rule)
		if rule.For_ == 0 {
			l.runNow(rule, evaluation)
			continue
		}
		l.runFor(ctx, rule, evaluation)
	}
}

func (l *Logic) runFor(ctx context.Context, rule *Rule, evaluation bool) {
	// TODO implement for 5m here. Do not run or set active until 5m passed.
	// go run sleep 5m. cancel go routine if we have new evaluation. After 5m run evaluateRule again before rule.Run

	if rule.stop == nil { // lazy initialized
		rule.stop = make(chan struct{})
	}

	if evaluation == rule.Pending() {
		return
	}

	rule.SetPending(evaluation)

	if evaluation {
		rule.Stop()
		l.Add(1)
		go func() {
			defer l.Done()

			l.onReportState(rule.Uuid(), map[string]interface{}{
				"pending": true,
			})
			logrus.Debug("Rule: ", rule.Name(), " (", rule.Uuid(), ") - sleeping for: ", rule.For())
			select {
			case <-time.After(time.Duration(rule.For())):
			case <-ctx.Done():
				return
			case <-rule.stop:
				return
			}

			if !l.evaluateRule(rule) {
				return
			}

			logrus.Info("Rule: ", rule.Name(), " (", rule.Uuid(), ") - running actions after for: ", rule.For())

			l.onReportState(rule.Uuid(), map[string]interface{}{
				"pending": false,
				"active":  true,
			})
			rule.SetActive(true)
			rule.Run(l.StateStore, l.WebsocketSender, l.onTriggerDestination)
		}()
		return
	}

	l.onReportState(rule.Uuid(), map[string]interface{}{
		"pending": false,
		"active":  false,
	})
	rule.SetActive(false)
}

func (l *Logic) runNow(rule *Rule, evaluation bool) {
	if evaluation != rule.Active() {
		l.onReportState(rule.Uuid(), map[string]interface{}{
			"pending": false,
			"active":  evaluation,
		})
		rule.SetActive(evaluation)
		if evaluation {
			l.Add(1)
			go func() {
				logrus.Info("Rule: ", rule.Name(), " (", rule.Uuid(), ") - running actions")
				rule.Run(l.StateStore, l.WebsocketSender, l.onTriggerDestination)
				l.Done()
			}()
		} else {
			rule.Cancel()
		}
	}
}

func (l *Logic) evaluateRule(r *Rule) bool {
	rules := make(map[string]bool)
	for _, v := range l.Rules {
		rules[v.Uuid()] = v.Active()
	}
	result, err := r.Eval(l.devices, rules)
	if err != nil {
		l.onReportState(r.Uuid(), map[string]interface{}{
			"error": err.Error(),
		})
		logrus.Errorf("Error evaluating rule %s: %s", r.Uuid(), err.Error())
		return false
	}
	l.onReportState(r.Uuid(), map[string]interface{}{
		"error": "",
	})
	return result
}

// Save saves the rules to rules.json.
func (l *Logic) Save() error {
	configFile, err := os.Create("rules.json")
	if err != nil {
		return fmt.Errorf("logic: error saving rules: %s", err.Error())
	}
	encoder := json.NewEncoder(configFile)
	encoder.SetIndent("", "\t")
	l.Lock()
	defer l.Unlock()
	err = encoder.Encode(l.Rules)
	if err != nil {
		return fmt.Errorf("logic: error saving rules: %s", err.Error())
	}
	return nil
}

// Load loads the rules from rules.json.
func (l *Logic) Load() error {
	logrus.Debug("logic: loading rules from rules.json")
	configFile, err := os.Open("rules.json")
	if err != nil {
		if os.IsNotExist(err) {
			logrus.Warn(err)
			return nil // We dont want to error our if the file does not exist when we start the server
		}
		return fmt.Errorf("logic: error loading rules.json: %s", err.Error())
	}

	l.Lock()
	defer l.Unlock()
	jsonParser := json.NewDecoder(configFile)
	if err = jsonParser.Decode(&l.Rules); err != nil {
		return fmt.Errorf("logic: error loading rules.json: %s", err.Error())
	}

	// TODO loop over rules and generate UUIDs if needed. If it was needed save the rules again

	return nil
}

/*

new way:
{
	"name": "All off",
	"enabled": true,
	"active": true|false,
	"uuid": "e8092b86-1261-44cd-ab64-38121df58a79",
	"expression": "devices["asdf.123"].on == true && devices["asdf.123"].temperature > 20.0",
	"conditions": { // this is implemented in expression instead. rules are available there
		"id på annan regel": true,
	}
	"for": "5m", // after for we must evaluate expression again
	"actions": [ // save index on where we are in running actions and send to gui over websocket
		"1m", //sleep , if rule stops being active during actions run. Stopp running!
		"c7d352bb-23f4-468c-b476-f76599c09a0d"
	]
},

savedState{
	"c7d352bb-23f4-468c-b476-f76599c09a0d": {
			"name": "tänd allt",
			"uuid": "c7d352bb-23f4-468c-b476-f76599c09a0d",
			"state": {
				"a.1" :{
					"on":true
				},
				"a.2" :{
					"on":true
				}
			}
		}
	}

old way:

{
	"name": "All off",
	"enabled": true,
	"uuid": "e8092b86-1261-44cd-ab64-38121df58a79",
	"conditions": [
		{
			"statePath": "Devices.fefe749b.Status",
			"comparator": "==",
			"value": "R1B0",
			"uuid": "efd2bd24-ac50-4147-bdf9-da3dd12c8f8a"
		}
	],
	"enterActions": [
		"c7d352bb-23f4-468c-b476-f76599c09a0d"
	]
},

*/
<commit_msg>Dont log very noisy errors.<commit_after>package logic

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"
	"github.com/stampzilla/stampzilla-go/v2/nodes/stampzilla-server/models/devices"
	"github.com/stampzilla/stampzilla-go/v2/nodes/stampzilla-server/websocket"
)

/* rules.json example:
{
    "e8092b86-1261-44cd-ab64-38121df58a79": {
        "name": "All off",
        "enabled": true,
        "active": false,
        "uuid": "e8092b86-1261-44cd-ab64-38121df58a79",
        "expression": "devices['fd230f30-6d84-4507-8ace-c1ec715be51e.1'].on == true",
        "for": "5m",
        "actions": [
            "1s",
            "c7d352bb-23f4-468c-b476-f76599c09a0d"
        ]
    },
    "1fd25327-f43c-4a00-aa67-3969dfed06b5": {
        "name": "chromecast p\u00e5",
        "enabled": true,
        "active": false,
        "uuid": "1fd25327-f43c-4a00-aa67-3969dfed06b5",
        "expression": "devices['asdf.123'].on == true ",
        "for": "5m",
        "actions": [
            "1m",
            "c7d352bb-23f4-468c-b476-f76599c09a0d"
        ],
		"labels": [
			"livingroom",
			"kitchen"
		]
    }
}
*/

// Rules is a list of rules.
type Rules map[string]*Rule

// Logic is the main struct.
type Logic struct {
	StateStore           *SavedStateStore
	Rules                map[string]*Rule
	devices              *devices.List
	onReportState        func(string, devices.State)
	onTriggerDestination func(string, string) error
	// ActionProgressChan chan ActionProgress
	sync.RWMutex
	sync.WaitGroup
	c               chan func()
	WebsocketSender websocket.Sender
}

/*
type ActionProgress struct {
	Address string `json:"address"`
	Uuid    string `json:"uuid"`
	Step    int    `json:"step"`
}
*/

// New returns a new logic that is ready to use.
func New(sss *SavedStateStore, websocketSender websocket.Sender) *Logic {
	l := &Logic{
		devices: devices.NewList(),
		// ActionProgressChan: make(chan ActionProgress, 100),
		Rules:                make(map[string]*Rule),
		StateStore:           sss,
		onReportState:        func(string, devices.State) {},
		onTriggerDestination: func(string, string) error { return nil },
		c:                    make(chan func()),
		WebsocketSender:      websocketSender,
	}
	return l
}

// AddRule adds a new logic rule. Mainly used in tests.
func (l *Logic) AddRule(name string) *Rule {
	r := &Rule{Name_: name, Uuid_: uuid.New().String()}
	l.Lock()
	defer l.Unlock()
	l.Rules[r.Uuid()] = r
	return r
}

// GetRules returns a list of Rules.
func (l *Logic) GetRules() Rules {
	l.RLock()
	defer l.RUnlock()
	return l.Rules
}

// SetRules overwrites all rules in logic.
func (l *Logic) SetRules(rules Rules) {
	l.Lock()
	l.Rules = rules
	l.Unlock()

	// Trigger an evaluation of the new rules
	l.c <- func() {}
}

func (l *Logic) OnReportState(callback func(string, devices.State)) {
	l.onReportState = callback
}

func (l *Logic) OnTriggerDestination(callback func(string, string) error) {
	l.onTriggerDestination = callback
}

// Start starts the logic worker.
func (l *Logic) Start(ctx context.Context) {
	l.Add(1)
	logrus.Info("logic: starting worker")
	go l.worker(ctx)
}

func (l *Logic) worker(ctx context.Context) {
	defer l.Done()
	for {
		select {
		case f := <-l.c:
			f()
			l.EvaluateRules(ctx)
		case <-ctx.Done():
			logrus.Info("logic: stopping worker")
			return
		}
	}
}

// UpdateDevice update the state in the logic store with the new state from the device.
func (l *Logic) UpdateDevice(dev *devices.Device) {
	l.c <- func() {
		l.updateDevice(dev)
	}
}

func (l *Logic) updateDevice(dev *devices.Device) {
	if oldDev := l.devices.Get(dev.ID); oldDev != nil {
		if diff := oldDev.State.Diff(dev.State); len(diff) > 0 {
			oldDev.Lock()
			oldDev.State.MergeWith(diff)
			oldDev.Unlock()
		}
		return
	}
	l.devices.Add(dev)
}

// EvaluateRules loops over each rule and run evaluation on them.
func (l *Logic) EvaluateRules(ctx context.Context) {
	for _, rule := range l.Rules {
		if !rule.Enabled {
			continue
		}
		evaluation := l.evaluateRule(rule)
		if rule.For_ == 0 {
			l.runNow(rule, evaluation)
			continue
		}
		l.runFor(ctx, rule, evaluation)
	}
}

func (l *Logic) runFor(ctx context.Context, rule *Rule, evaluation bool) {
	// TODO implement for 5m here. Do not run or set active until 5m passed.
	// go run sleep 5m. cancel go routine if we have new evaluation. After 5m run evaluateRule again before rule.Run

	if rule.stop == nil { // lazy initialized
		rule.stop = make(chan struct{})
	}

	if evaluation == rule.Pending() {
		return
	}

	rule.SetPending(evaluation)

	if evaluation {
		rule.Stop()
		l.Add(1)
		go func() {
			defer l.Done()

			l.onReportState(rule.Uuid(), map[string]interface{}{
				"pending": true,
			})
			logrus.Debug("Rule: ", rule.Name(), " (", rule.Uuid(), ") - sleeping for: ", rule.For())
			select {
			case <-time.After(time.Duration(rule.For())):
			case <-ctx.Done():
				return
			case <-rule.stop:
				return
			}

			if !l.evaluateRule(rule) {
				return
			}

			logrus.Info("Rule: ", rule.Name(), " (", rule.Uuid(), ") - running actions after for: ", rule.For())

			l.onReportState(rule.Uuid(), map[string]interface{}{
				"pending": false,
				"active":  true,
			})
			rule.SetActive(true)
			rule.Run(l.StateStore, l.WebsocketSender, l.onTriggerDestination)
		}()
		return
	}

	l.onReportState(rule.Uuid(), map[string]interface{}{
		"pending": false,
		"active":  false,
	})
	rule.SetActive(false)
}

func (l *Logic) runNow(rule *Rule, evaluation bool) {
	if evaluation != rule.Active() {
		l.onReportState(rule.Uuid(), map[string]interface{}{
			"pending": false,
			"active":  evaluation,
		})
		rule.SetActive(evaluation)
		if evaluation {
			l.Add(1)
			go func() {
				logrus.Info("Rule: ", rule.Name(), " (", rule.Uuid(), ") - running actions")
				rule.Run(l.StateStore, l.WebsocketSender, l.onTriggerDestination)
				l.Done()
			}()
		} else {
			rule.Cancel()
		}
	}
}

func (l *Logic) evaluateRule(r *Rule) bool {
	rules := make(map[string]bool)
	for _, v := range l.Rules {
		rules[v.Uuid()] = v.Active()
	}
	result, err := r.Eval(l.devices, rules)
	if err != nil {
		l.onReportState(r.Uuid(), map[string]interface{}{
			"error": err.Error(),
		})
		logrus.Debugf("Error evaluating rule %s: %s", r.Uuid(), err.Error())
		return false
	}
	l.onReportState(r.Uuid(), map[string]interface{}{
		"error": "",
	})
	return result
}

// Save saves the rules to rules.json.
func (l *Logic) Save() error {
	configFile, err := os.Create("rules.json")
	if err != nil {
		return fmt.Errorf("logic: error saving rules: %s", err.Error())
	}
	encoder := json.NewEncoder(configFile)
	encoder.SetIndent("", "\t")
	l.Lock()
	defer l.Unlock()
	err = encoder.Encode(l.Rules)
	if err != nil {
		return fmt.Errorf("logic: error saving rules: %s", err.Error())
	}
	return nil
}

// Load loads the rules from rules.json.
func (l *Logic) Load() error {
	logrus.Debug("logic: loading rules from rules.json")
	configFile, err := os.Open("rules.json")
	if err != nil {
		if os.IsNotExist(err) {
			logrus.Warn(err)
			return nil // We dont want to error our if the file does not exist when we start the server
		}
		return fmt.Errorf("logic: error loading rules.json: %s", err.Error())
	}

	l.Lock()
	defer l.Unlock()
	jsonParser := json.NewDecoder(configFile)
	if err = jsonParser.Decode(&l.Rules); err != nil {
		return fmt.Errorf("logic: error loading rules.json: %s", err.Error())
	}

	// TODO loop over rules and generate UUIDs if needed. If it was needed save the rules again

	return nil
}

/*

new way:
{
	"name": "All off",
	"enabled": true,
	"active": true|false,
	"uuid": "e8092b86-1261-44cd-ab64-38121df58a79",
	"expression": "devices["asdf.123"].on == true && devices["asdf.123"].temperature > 20.0",
	"conditions": { // this is implemented in expression instead. rules are available there
		"id på annan regel": true,
	}
	"for": "5m", // after for we must evaluate expression again
	"actions": [ // save index on where we are in running actions and send to gui over websocket
		"1m", //sleep , if rule stops being active during actions run. Stopp running!
		"c7d352bb-23f4-468c-b476-f76599c09a0d"
	]
},

savedState{
	"c7d352bb-23f4-468c-b476-f76599c09a0d": {
			"name": "tänd allt",
			"uuid": "c7d352bb-23f4-468c-b476-f76599c09a0d",
			"state": {
				"a.1" :{
					"on":true
				},
				"a.2" :{
					"on":true
				}
			}
		}
	}

old way:

{
	"name": "All off",
	"enabled": true,
	"uuid": "e8092b86-1261-44cd-ab64-38121df58a79",
	"conditions": [
		{
			"statePath": "Devices.fefe749b.Status",
			"comparator": "==",
			"value": "R1B0",
			"uuid": "efd2bd24-ac50-4147-bdf9-da3dd12c8f8a"
		}
	],
	"enterActions": [
		"c7d352bb-23f4-468c-b476-f76599c09a0d"
	]
},

*/
<|endoftext|>


<commit_before>package exporter

import (
	"strconv"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

// UwsgiExporter collects uwsgi metrics for prometheus.
type UwsgiExporter struct {
	mutex sync.RWMutex

	uri          string
	timeout      time.Duration
	collectCores bool
	statsReader  StatsReader

	scrapeDurations *prometheus.SummaryVec
	descriptorsMap  DescriptorsMap
}

// Descriptors is a map for `prometheus.Desc` pointer.
type Descriptors map[string]*prometheus.Desc

// DescriptorsMap is a map for `Descriptors`.
type DescriptorsMap map[string]Descriptors

const (
	namespace = "uwsgi"

	mainSubsystem       = ""
	socketSubsystem     = "socket"
	workerSubsystem     = "worker"
	workerAppSubsystem  = "worker_app"
	workerCoreSubsystem = "worker_core"

	usDivider = float64(time.Second / time.Microsecond)
)

var (
	metricsMap = map[string]map[string]string{
		mainSubsystem: {
			"listen_queue_length": "Length of listen queue.",
			"listen_queue_errors": "Number of listen queue errors.",
			"signal_queue_length": "Length of signal queue.",
			"workers":             "Number of workers.",
		},

		socketSubsystem: {
			"queue_length":     "Length of socket queue.",
			"max_queue_length": "Max length of socket queue.",
			"shared":           "Is shared socket?",
			"can_offload":      "Can socket offload?",
		},

		workerSubsystem: {
			"accepting":                     "Is this worker accepting requests?",
			"delta_requests":                "Number of delta requests",
			"signal_queue_length":           "Length of signal queue.",
			"rss_bytes":                     "Worker RSS bytes.",
			"vsz_bytes":                     "Worker VSZ bytes.",
			"running_time_seconds":          "Worker running time in seconds.",
			"last_spawn_time_seconds":       "Last spawn time in seconds since epoch.",
			"average_response_time_seconds": "Average response time in seconds.",
			"apps":  "Number of apps.",
			"cores": "Number of cores.",

			"requests_total":          "Total number of requests.",
			"exceptions_total":        "Total number of exceptions.",
			"harakiri_count_total":    "Total number of harakiri count.",
			"signals_total":           "Total number of signals.",
			"respawn_count_total":     "Total number of respawn count.",
			"transmitted_bytes_total": "Worker transmitted bytes.",
		},

		workerAppSubsystem: {
			"startup_time_seconds": "How long this app took to start.",

			"requests_total":   "Total number of requests.",
			"exceptions_total": "Total number of exceptions.",
		},

		workerCoreSubsystem: {
			"in_requests": "In requests?",

			"requests_total":           "Total number of requests.",
			"static_requests_total":    "Total number of static requests.",
			"routed_reqeusts_total":    "Total number of routed requests.",
			"offloaded_requests_total": "Total number of offloaded requests.",
			"write_errors_total":       "Total number of write errors.",
			"read_errors_total":        "Total number of read errors.",
		},
	}

	// Please note that stats_uri is omitted here, because it's const
	labelsMap = map[string][]string{
		mainSubsystem:       {},
		socketSubsystem:     {"name", "proto"},
		workerSubsystem:     {"worker_id"},
		workerAppSubsystem:  {"worker_id", "app_id", "mountpoint", "chdir"},
		workerCoreSubsystem: {"worker_id", "core_id"},
	}
)

// NewExporter creates a new uwsgi exporter.
func NewExporter(uri string, timeout time.Duration, collectCores bool) *UwsgiExporter {
	descriptorsMap := DescriptorsMap{}
	constLabels := prometheus.Labels{"stats_uri": uri}

	for subsystem, metrics := range metricsMap {
		descriptors := Descriptors{}
		for name, help := range metrics {
			descriptors[name] = prometheus.NewDesc(
				prometheus.BuildFQName(namespace, subsystem, name), help, labelsMap[subsystem], constLabels)
		}

		descriptorsMap[subsystem] = descriptors
	}

	statsReader, err := NewStatsReader(uri, timeout)
	if err != nil {
		log.Fatal(err)
	}

	return &UwsgiExporter{
		uri:          uri,
		timeout:      timeout,
		collectCores: collectCores,
		statsReader:  statsReader,

		scrapeDurations: prometheus.NewSummaryVec(prometheus.SummaryOpts{
			Namespace: namespace,
			Subsystem: "exporter",
			Name:      "scrape_duration_seconds",
			Help:      "uwsgi_exporter: Duration of a scrape job.",
		}, []string{"result"}),
		descriptorsMap: descriptorsMap,
	}
}

// Describe describes all the metrics ever exported by the exporter.
// It implements prometheus.Collector.
func (e *UwsgiExporter) Describe(ch chan<- *prometheus.Desc) {
	e.scrapeDurations.Describe(ch)

	for _, descs := range e.descriptorsMap {
		for _, desc := range descs {
			ch <- desc
		}
	}
}

// Collect fetches the stats from configured uwsgi stats location and
// delivers them as Prometheus metrics. It implements prometheus.Collector.
func (e *UwsgiExporter) Collect(ch chan<- prometheus.Metric) {
	begin := time.Now()
	err := e.execute(ch)
	duration := time.Since(begin)
	var result string

	if err != nil {
		log.Errorf("ERROR: scrape failed after %fs: %s", duration.Seconds(), err)
		result = "error"
	} else {
		log.Debugf("OK: scrape successful after %fs.", duration.Seconds())
		result = "success"
	}

	e.scrapeDurations.WithLabelValues(result).Observe(duration.Seconds())
	e.scrapeDurations.Collect(ch)
}

func (e *UwsgiExporter) execute(ch chan<- prometheus.Metric) error {
	e.mutex.Lock() // To prevent metrics from concurrent collects.
	defer e.mutex.Unlock()

	// Read (and parse) stats from uwsgi server
	uwsgiStats, err := e.statsReader.Read()
	if err != nil {
		return err
	}

	// Collect metrics from stats
	e.collectMetrics(ch, uwsgiStats)

	return nil
}

func newGaugeMetric(desc *prometheus.Desc, value float64, labelValues ...string) prometheus.Metric {
	return prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, value, labelValues...)
}

func newCounterMetric(desc *prometheus.Desc, value float64, labelsValues ...string) prometheus.Metric {
	return prometheus.MustNewConstMetric(desc, prometheus.CounterValue, value, labelsValues...)
}

func (e *UwsgiExporter) collectMetrics(ch chan<- prometheus.Metric, stats *UwsgiStats) {
	// Main
	mainDescs := e.descriptorsMap[mainSubsystem]

	ch <- newGaugeMetric(mainDescs["listen_queue_length"], float64(stats.ListenQueue))
	ch <- newGaugeMetric(mainDescs["listen_queue_errors"], float64(stats.ListenQueueErrors))
	ch <- newGaugeMetric(mainDescs["signal_queue_length"], float64(stats.SignalQueue))
	ch <- newGaugeMetric(mainDescs["workers"], float64(len(stats.Workers)))

	// Sockets
	socketDescs := e.descriptorsMap[socketSubsystem]
	for _, socketStats := range stats.Sockets {
		labelValues := []string{socketStats.Name, socketStats.Proto}

		ch <- newGaugeMetric(socketDescs["queue_length"], float64(socketStats.Queue), labelValues...)
		ch <- newGaugeMetric(socketDescs["max_queue_length"], float64(socketStats.MaxQueue), labelValues...)
		ch <- newGaugeMetric(socketDescs["shared"], float64(socketStats.Shared), labelValues...)
		ch <- newGaugeMetric(socketDescs["can_offload"], float64(socketStats.CanOffload), labelValues...)
	}

	// Workers
	workerDescs := e.descriptorsMap[workerSubsystem]
	workerAppDescs := e.descriptorsMap[workerAppSubsystem]
	workerCoreDescs := e.descriptorsMap[workerCoreSubsystem]
	for _, workerStats := range stats.Workers {
		labelValues := []string{strconv.Itoa(workerStats.ID)}

		ch <- newGaugeMetric(workerDescs["accepting"], float64(workerStats.Accepting), labelValues...)
		ch <- newGaugeMetric(workerDescs["delta_requests"], float64(workerStats.DeltaRequests), labelValues...)
		ch <- newGaugeMetric(workerDescs["signal_queue_length"], float64(workerStats.SignalQueue), labelValues...)
		ch <- newGaugeMetric(workerDescs["rss_bytes"], float64(workerStats.RSS), labelValues...)
		ch <- newGaugeMetric(workerDescs["vsz_bytes"], float64(workerStats.VSZ), labelValues...)
		ch <- newGaugeMetric(workerDescs["running_time_seconds"], float64(workerStats.RunningTime)/usDivider, labelValues...)
		ch <- newGaugeMetric(workerDescs["last_spawn_time_seconds"], float64(workerStats.LastSpawn), labelValues...)
		ch <- newGaugeMetric(workerDescs["average_response_time_seconds"], float64(workerStats.AvgRt)/usDivider, labelValues...)

		ch <- newCounterMetric(workerDescs["requests_total"], float64(workerStats.Requests), labelValues...)
		ch <- newCounterMetric(workerDescs["exceptions_total"], float64(workerStats.Exceptions), labelValues...)
		ch <- newCounterMetric(workerDescs["harakiri_count_total"], float64(workerStats.HarakiriCount), labelValues...)
		ch <- newCounterMetric(workerDescs["signals_total"], float64(workerStats.Signals), labelValues...)
		ch <- newCounterMetric(workerDescs["respawn_count_total"], float64(workerStats.RespawnCount), labelValues...)
		ch <- newCounterMetric(workerDescs["transmitted_bytes_total"], float64(workerStats.TX), labelValues...)

		// Worker Apps
		ch <- newGaugeMetric(workerDescs["apps"], float64(len(workerStats.Apps)), labelValues...)
		for _, appStats := range workerStats.Apps {
			labelValues := []string{strconv.Itoa(workerStats.ID), strconv.Itoa(appStats.ID), appStats.Mountpoint, appStats.Chdir}
			ch <- newGaugeMetric(workerAppDescs["startup_time_seconds"], float64(appStats.StartupTime), labelValues...)

			ch <- newCounterMetric(workerAppDescs["requests_total"], float64(appStats.Requests), labelValues...)
			ch <- newCounterMetric(workerAppDescs["exceptions_total"], float64(appStats.Exceptions), labelValues...)
		}

		// Worker Cores
		ch <- newGaugeMetric(workerDescs["cores"], float64(len(workerStats.Cores)), labelValues...)
		if e.collectCores {
			for _, coreStats := range workerStats.Cores {
				labelValues := []string{strconv.Itoa(workerStats.ID), strconv.Itoa(coreStats.ID)}
				ch <- newGaugeMetric(workerCoreDescs["in_requests"], float64(coreStats.InRequests), labelValues...)

				ch <- newCounterMetric(workerCoreDescs["requests_total"], float64(coreStats.Requests), labelValues...)
				ch <- newCounterMetric(workerCoreDescs["static_requests_total"], float64(coreStats.StaticRequests), labelValues...)
				ch <- newCounterMetric(workerCoreDescs["routed_reqeusts_total"], float64(coreStats.RoutedRequests), labelValues...)
				ch <- newCounterMetric(workerCoreDescs["offloaded_requests_total"], float64(coreStats.OffloadedRequests), labelValues...)
				ch <- newCounterMetric(workerCoreDescs["write_errors_total"], float64(coreStats.WriteErrors), labelValues...)
				ch <- newCounterMetric(workerCoreDescs["read_errors_total"], float64(coreStats.ReadErrors), labelValues...)
			}
		}
	}
}
<commit_msg>Quick fix for worker in cheap mode<commit_after>package exporter

import (
	"strconv"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

// UwsgiExporter collects uwsgi metrics for prometheus.
type UwsgiExporter struct {
	mutex sync.RWMutex

	uri          string
	timeout      time.Duration
	collectCores bool
	statsReader  StatsReader

	scrapeDurations *prometheus.SummaryVec
	descriptorsMap  DescriptorsMap
}

// Descriptors is a map for `prometheus.Desc` pointer.
type Descriptors map[string]*prometheus.Desc

// DescriptorsMap is a map for `Descriptors`.
type DescriptorsMap map[string]Descriptors

const (
	namespace = "uwsgi"

	mainSubsystem       = ""
	socketSubsystem     = "socket"
	workerSubsystem     = "worker"
	workerAppSubsystem  = "worker_app"
	workerCoreSubsystem = "worker_core"

	usDivider = float64(time.Second / time.Microsecond)
)

var (
	metricsMap = map[string]map[string]string{
		mainSubsystem: {
			"listen_queue_length": "Length of listen queue.",
			"listen_queue_errors": "Number of listen queue errors.",
			"signal_queue_length": "Length of signal queue.",
			"workers":             "Number of workers.",
		},

		socketSubsystem: {
			"queue_length":     "Length of socket queue.",
			"max_queue_length": "Max length of socket queue.",
			"shared":           "Is shared socket?",
			"can_offload":      "Can socket offload?",
		},

		workerSubsystem: {
			"accepting":                     "Is this worker accepting requests?",
			"delta_requests":                "Number of delta requests",
			"signal_queue_length":           "Length of signal queue.",
			"rss_bytes":                     "Worker RSS bytes.",
			"vsz_bytes":                     "Worker VSZ bytes.",
			"running_time_seconds":          "Worker running time in seconds.",
			"last_spawn_time_seconds":       "Last spawn time in seconds since epoch.",
			"average_response_time_seconds": "Average response time in seconds.",
			"apps":  "Number of apps.",
			"cores": "Number of cores.",

			"requests_total":          "Total number of requests.",
			"exceptions_total":        "Total number of exceptions.",
			"harakiri_count_total":    "Total number of harakiri count.",
			"signals_total":           "Total number of signals.",
			"respawn_count_total":     "Total number of respawn count.",
			"transmitted_bytes_total": "Worker transmitted bytes.",
		},

		workerAppSubsystem: {
			"startup_time_seconds": "How long this app took to start.",

			"requests_total":   "Total number of requests.",
			"exceptions_total": "Total number of exceptions.",
		},

		workerCoreSubsystem: {
			"in_requests": "In requests?",

			"requests_total":           "Total number of requests.",
			"static_requests_total":    "Total number of static requests.",
			"routed_reqeusts_total":    "Total number of routed requests.",
			"offloaded_requests_total": "Total number of offloaded requests.",
			"write_errors_total":       "Total number of write errors.",
			"read_errors_total":        "Total number of read errors.",
		},
	}

	// Please note that stats_uri is omitted here, because it's const
	labelsMap = map[string][]string{
		mainSubsystem:       {},
		socketSubsystem:     {"name", "proto"},
		workerSubsystem:     {"worker_id"},
		workerAppSubsystem:  {"worker_id", "app_id", "mountpoint", "chdir"},
		workerCoreSubsystem: {"worker_id", "core_id"},
	}
)

// NewExporter creates a new uwsgi exporter.
func NewExporter(uri string, timeout time.Duration, collectCores bool) *UwsgiExporter {
	descriptorsMap := DescriptorsMap{}
	constLabels := prometheus.Labels{"stats_uri": uri}

	for subsystem, metrics := range metricsMap {
		descriptors := Descriptors{}
		for name, help := range metrics {
			descriptors[name] = prometheus.NewDesc(
				prometheus.BuildFQName(namespace, subsystem, name), help, labelsMap[subsystem], constLabels)
		}

		descriptorsMap[subsystem] = descriptors
	}

	statsReader, err := NewStatsReader(uri, timeout)
	if err != nil {
		log.Fatal(err)
	}

	return &UwsgiExporter{
		uri:          uri,
		timeout:      timeout,
		collectCores: collectCores,
		statsReader:  statsReader,

		scrapeDurations: prometheus.NewSummaryVec(prometheus.SummaryOpts{
			Namespace: namespace,
			Subsystem: "exporter",
			Name:      "scrape_duration_seconds",
			Help:      "uwsgi_exporter: Duration of a scrape job.",
		}, []string{"result"}),
		descriptorsMap: descriptorsMap,
	}
}

// Describe describes all the metrics ever exported by the exporter.
// It implements prometheus.Collector.
func (e *UwsgiExporter) Describe(ch chan<- *prometheus.Desc) {
	e.scrapeDurations.Describe(ch)

	for _, descs := range e.descriptorsMap {
		for _, desc := range descs {
			ch <- desc
		}
	}
}

// Collect fetches the stats from configured uwsgi stats location and
// delivers them as Prometheus metrics. It implements prometheus.Collector.
func (e *UwsgiExporter) Collect(ch chan<- prometheus.Metric) {
	begin := time.Now()
	err := e.execute(ch)
	duration := time.Since(begin)
	var result string

	if err != nil {
		log.Errorf("ERROR: scrape failed after %fs: %s", duration.Seconds(), err)
		result = "error"
	} else {
		log.Debugf("OK: scrape successful after %fs.", duration.Seconds())
		result = "success"
	}

	e.scrapeDurations.WithLabelValues(result).Observe(duration.Seconds())
	e.scrapeDurations.Collect(ch)
}

func (e *UwsgiExporter) execute(ch chan<- prometheus.Metric) error {
	e.mutex.Lock() // To prevent metrics from concurrent collects.
	defer e.mutex.Unlock()

	// Read (and parse) stats from uwsgi server
	uwsgiStats, err := e.statsReader.Read()
	if err != nil {
		return err
	}

	// Collect metrics from stats
	e.collectMetrics(ch, uwsgiStats)

	return nil
}

func newGaugeMetric(desc *prometheus.Desc, value float64, labelValues ...string) prometheus.Metric {
	return prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, value, labelValues...)
}

func newCounterMetric(desc *prometheus.Desc, value float64, labelsValues ...string) prometheus.Metric {
	return prometheus.MustNewConstMetric(desc, prometheus.CounterValue, value, labelsValues...)
}

func (e *UwsgiExporter) collectMetrics(ch chan<- prometheus.Metric, stats *UwsgiStats) {
	// Main
	mainDescs := e.descriptorsMap[mainSubsystem]

	ch <- newGaugeMetric(mainDescs["listen_queue_length"], float64(stats.ListenQueue))
	ch <- newGaugeMetric(mainDescs["listen_queue_errors"], float64(stats.ListenQueueErrors))
	ch <- newGaugeMetric(mainDescs["signal_queue_length"], float64(stats.SignalQueue))
	ch <- newGaugeMetric(mainDescs["workers"], float64(len(stats.Workers)))

	// Sockets
	socketDescs := e.descriptorsMap[socketSubsystem]
	for _, socketStats := range stats.Sockets {
		labelValues := []string{socketStats.Name, socketStats.Proto}

		ch <- newGaugeMetric(socketDescs["queue_length"], float64(socketStats.Queue), labelValues...)
		ch <- newGaugeMetric(socketDescs["max_queue_length"], float64(socketStats.MaxQueue), labelValues...)
		ch <- newGaugeMetric(socketDescs["shared"], float64(socketStats.Shared), labelValues...)
		ch <- newGaugeMetric(socketDescs["can_offload"], float64(socketStats.CanOffload), labelValues...)
	}

	// Workers
	workerDescs := e.descriptorsMap[workerSubsystem]
	workerAppDescs := e.descriptorsMap[workerAppSubsystem]
	workerCoreDescs := e.descriptorsMap[workerCoreSubsystem]
	for _, workerStats := range stats.Workers {
		if workerStats.ID == 0 {
			continue
		}

		labelValues := []string{strconv.Itoa(workerStats.ID)}

		ch <- newGaugeMetric(workerDescs["accepting"], float64(workerStats.Accepting), labelValues...)
		ch <- newGaugeMetric(workerDescs["delta_requests"], float64(workerStats.DeltaRequests), labelValues...)
		ch <- newGaugeMetric(workerDescs["signal_queue_length"], float64(workerStats.SignalQueue), labelValues...)
		ch <- newGaugeMetric(workerDescs["rss_bytes"], float64(workerStats.RSS), labelValues...)
		ch <- newGaugeMetric(workerDescs["vsz_bytes"], float64(workerStats.VSZ), labelValues...)
		ch <- newGaugeMetric(workerDescs["running_time_seconds"], float64(workerStats.RunningTime)/usDivider, labelValues...)
		ch <- newGaugeMetric(workerDescs["last_spawn_time_seconds"], float64(workerStats.LastSpawn), labelValues...)
		ch <- newGaugeMetric(workerDescs["average_response_time_seconds"], float64(workerStats.AvgRt)/usDivider, labelValues...)

		ch <- newCounterMetric(workerDescs["requests_total"], float64(workerStats.Requests), labelValues...)
		ch <- newCounterMetric(workerDescs["exceptions_total"], float64(workerStats.Exceptions), labelValues...)
		ch <- newCounterMetric(workerDescs["harakiri_count_total"], float64(workerStats.HarakiriCount), labelValues...)
		ch <- newCounterMetric(workerDescs["signals_total"], float64(workerStats.Signals), labelValues...)
		ch <- newCounterMetric(workerDescs["respawn_count_total"], float64(workerStats.RespawnCount), labelValues...)
		ch <- newCounterMetric(workerDescs["transmitted_bytes_total"], float64(workerStats.TX), labelValues...)

		// Worker Apps
		ch <- newGaugeMetric(workerDescs["apps"], float64(len(workerStats.Apps)), labelValues...)
		for _, appStats := range workerStats.Apps {
			labelValues := []string{strconv.Itoa(workerStats.ID), strconv.Itoa(appStats.ID), appStats.Mountpoint, appStats.Chdir}
			ch <- newGaugeMetric(workerAppDescs["startup_time_seconds"], float64(appStats.StartupTime), labelValues...)

			ch <- newCounterMetric(workerAppDescs["requests_total"], float64(appStats.Requests), labelValues...)
			ch <- newCounterMetric(workerAppDescs["exceptions_total"], float64(appStats.Exceptions), labelValues...)
		}

		// Worker Cores
		ch <- newGaugeMetric(workerDescs["cores"], float64(len(workerStats.Cores)), labelValues...)
		if e.collectCores {
			for _, coreStats := range workerStats.Cores {
				labelValues := []string{strconv.Itoa(workerStats.ID), strconv.Itoa(coreStats.ID)}
				ch <- newGaugeMetric(workerCoreDescs["in_requests"], float64(coreStats.InRequests), labelValues...)

				ch <- newCounterMetric(workerCoreDescs["requests_total"], float64(coreStats.Requests), labelValues...)
				ch <- newCounterMetric(workerCoreDescs["static_requests_total"], float64(coreStats.StaticRequests), labelValues...)
				ch <- newCounterMetric(workerCoreDescs["routed_reqeusts_total"], float64(coreStats.RoutedRequests), labelValues...)
				ch <- newCounterMetric(workerCoreDescs["offloaded_requests_total"], float64(coreStats.OffloadedRequests), labelValues...)
				ch <- newCounterMetric(workerCoreDescs["write_errors_total"], float64(coreStats.WriteErrors), labelValues...)
				ch <- newCounterMetric(workerCoreDescs["read_errors_total"], float64(coreStats.ReadErrors), labelValues...)
			}
		}
	}
}
<|endoftext|>


<commit_before>package zlog

import (
	"errors"
	"fmt"
	"os"
	"runtime"
	"strings"
	"time"
)

const (
	DEBUG_LOG = iota
	TRACE_LOG
	INFO_LOG
	ERROR_LOG
	FATAL_LOG
)

var severityName = []string{
	DEBUG_LOG: "Debug",
	TRACE_LOG: "Trace",
	INFO_LOG:  " Info",
	ERROR_LOG: "Error",
	FATAL_LOG: "Fatal",
}

type Logger struct {
	logLevel int
	depth    int
}

var logger Logger

const defaultCallDepth int = 2

var nextCreateFileTime int64

var messages chan string

func InitLogger(rootPath string, level int) {

	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	logger = Logger{}
	logger.depth = defaultCallDepth
	logFileProperty.rootPath = rootPath

	if level < DEBUG_LOG || level > FATAL_LOG {
		panic("Logger is not supported")
	}
	logger.logLevel = level

	err := logFileProperty.getLogFile()
	if err != nil {
		panic(err)
	}

	messages = make(chan string)
}

// call after InitLogger function
// generally, you needn't change it
func SetCallDepth(depth int) {
	if depth > 0 {
		logger.depth = depth
	}
}

func Debug(format string, args ...interface{}) {
	if DEBUG_LOG < logger.logLevel {
		return
	}
	logger.logFormat(DEBUG_LOG, fmt.Sprintf(format, args...))
}

func Info(format string, args ...interface{}) {
	if INFO_LOG < logger.logLevel {
		return
	}

	logger.logFormat(INFO_LOG, fmt.Sprintf(format, args...))
}

func Error(format string, args ...interface{}) {
	if ERROR_LOG < logger.logLevel {
		return
	}

	logger.logFormat(ERROR_LOG, fmt.Sprintf(format, args...))
}

func Trace(format string, args ...interface{}) {
	if TRACE_LOG < logger.logLevel {
		return
	}

	logger.logFormat(TRACE_LOG, fmt.Sprintf(format, args...))
}

func Fatal(format string, args ...interface{}) {
	if FATAL_LOG < logger.logLevel {
		return
	}

	logger.logFormat(FATAL_LOG, fmt.Sprintf(format, args...))
	os.Exit(-1)
}

func (logger *Logger) logFormat(level int, log string) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	now := time.Now()
	if now.Unix() > nextCreateFileTime {
		if err := logFileProperty.getLogFile(); err != nil {
			panic(err)
		}
	}

	time := now.Format("20060102 15:04:05")
	time = fmt.Sprintf("%s.%09d", time, now.Nanosecond())
	_, file, line, ok := runtime.Caller(logger.depth)
	if ok == false {
		panic(errors.New("get the line failed"))
	}

	tmp := strings.Split(file, "/")
	file = tmp[len(tmp)-1]

	_, err := Write(logFileProperty.logFile, fmt.Sprintf("%s [%s]: %s (%s:%d) \n", time, severityName[level], log, file, line))
	if err != nil {
		panic(err)
	}
}
<commit_msg>change some definition name<commit_after>package zlog

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"runtime"
	"strings"
	"time"
)

const (
	DebugLevel = iota
	TraceLevel
	InfoLevel
	ErrorLevel
	FatalLevel
)

// return string base of log level
var severityName = []string{
	DebugLevel: "Debug",
	TraceLevel: "Trace",
	InfoLevel:  " Info",
	ErrorLevel: "Error",
	FatalLevel: "Fatal",
}

type Logger struct {
	logLevel int
	depth    int
	bufOne   bytes.Buffer
	bufTwo   bytes.Buffer
	curbuf   *bytes.Buffer
}

var logger Logger

const defaultCallDepth int = 2

var nextCreateFileTime int64

var messages chan string

func InitLogger(rootPath string, level int) {

	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	logger = Logger{}
	logger.depth = defaultCallDepth
	logFileProperty.rootPath = rootPath

	if level < DebugLevel || level > FatalLevel {
		panic("Logger is not supported")
	}
	logger.logLevel = level

	err := logFileProperty.getLogFile()
	if err != nil {
		panic(err)
	}

	messages = make(chan string)
}

func SetOutput(out io.Writer) {

}

func timeoutFlush(timeout time.Duration) {

}

// call after InitLogger function
// generally, you needn't change it
func SetCallDepth(depth int) {
	if depth > 0 {
		logger.depth = depth
	}
}

func Debug(format string, args ...interface{}) {
	if DebugLevel < logger.logLevel {
		return
	}
	logger.logFormat(DebugLevel, fmt.Sprintf(format, args...))
}

func Info(format string, args ...interface{}) {
	if InfoLevel < logger.logLevel {
		return
	}

	logger.logFormat(InfoLevel, fmt.Sprintf(format, args...))
}

func Error(format string, args ...interface{}) {
	if ErrorLevel < logger.logLevel {
		return
	}

	logger.logFormat(ErrorLevel, fmt.Sprintf(format, args...))
}

func Trace(format string, args ...interface{}) {
	if TraceLevel < logger.logLevel {
		return
	}

	logger.logFormat(TraceLevel, fmt.Sprintf(format, args...))
}

func Fatal(format string, args ...interface{}) {
	if FatalLevel < logger.logLevel {
		return
	}

	logger.logFormat(FatalLevel, fmt.Sprintf(format, args...))
	os.Exit(-1)
}

func (logger *Logger) logFormat(level int, log string) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	now := time.Now()
	if now.Unix() > nextCreateFileTime {
		if err := logFileProperty.getLogFile(); err != nil {
			panic(err)
		}
	}

	time := now.Format("20060102 15:04:05")
	time = fmt.Sprintf("%s.%09d", time, now.Nanosecond())
	_, file, line, ok := runtime.Caller(logger.depth)
	if ok == false {
		panic(errors.New("get the line failed"))
	}

	tmp := strings.Split(file, "/")
	file = tmp[len(tmp)-1]

	_, err := Write(logFileProperty.logFile, fmt.Sprintf("%s [%s]: %s (%s:%d) \n", time, severityName[level], log, file, line))
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// Package gogitlog implements a very simple wrapper around the
// Go "log" package, providing support for a toggle-able debug flag
// and a couple of functions that log or not based on that flag.
package gologit

import (
	"log"
	"os"
	"os/signal"
	"sync"
)

// A DebugLogger represents a logging object, that embeds log.Logger, and
// provides support for a toggle-able debug flag.
type DebugLogger struct {
	*log.Logger
	debug bool
	mx sync.Mutex
}

// New creates a new DebugLogger.
// The debug argument specifies whether debug should be set or not.
func New(debug bool) *DebugLogger {
	return &DebugLogger{log.New(os.Stderr, "", log.LstdFlags), debug, sync.Mutex{}}
}

// Toggles the debug state.
// If debug is true, sets it to false.
// If debug is false, sets it to true.
func (l *DebugLogger) Toggle() {
	l.mx.Lock()
	defer l.mx.Unlock()
	if l.debug == false {
		l.debug = true
	} else {
		l.debug = false
	}
}

func (l *DebugLogger) ToggleOnSignal(sig os.Signal) {
	debugSig := make(chan os.Signal, 1)
	// spawn goroutine to handle signal/toggle of debug logging
	go func() {
		for {
			<-debugSig
			l.Toggle()
			if l.State() {
				l.Printf("Debug logging enabled")
			} else {
				l.Printf("Debug logging disabled")
			}
		}
	}()
	// notify send to debug sign channel on signusr1
	signal.Notify(debugSig, sig)
}

func (l *DebugLogger) State() bool {
	return l.debug
}

// Set the debug state directly.
func (l *DebugLogger) Set(debug bool) {
	l.mx.Lock()
	defer l.mx.Unlock()
	l.debug = debug
}

// Debugf calls log.Printf if debug is true.
// If debug is false, does nothing.
func (l *DebugLogger) Debugf(format string, v ...interface{}) {
	if l.debug == true {
		l.Printf(format, v...)
	}
}

// Debug calls log.Print if debug is true.
// If debug is false, does nothing.
func (l *DebugLogger) Debug(v ...interface{}) {
	if l.debug == true {
		l.Print(v...)
	}
}

// Debugln calls log.Println if debug is true.
// If debug is false, does nothing.
func (l *DebugLogger) Debugln(v ...interface{}) {
	if l.debug == true {
		l.Println(v...)
	}
}
<commit_msg>add a module level logger<commit_after>// Package gogitlog implements a very simple wrapper around the
// Go "log" package, providing support for a toggle-able debug flag
// and a couple of functions that log or not based on that flag.
package gologit

import (
	"log"
	"os"
	"os/signal"
	"sync"
)

var Logger = New(false)

// A DebugLogger represents a logging object, that embeds log.Logger, and
// provides support for a toggle-able debug flag.
type DebugLogger struct {
	*log.Logger
	debug bool
	mx sync.Mutex
}

// New creates a new DebugLogger.
// The debug argument specifies whether debug should be set or not.
func New(debug bool) *DebugLogger {
	return &DebugLogger{log.New(os.Stderr, "", log.LstdFlags), debug, sync.Mutex{}}
}

// Toggles the debug state.
// If debug is true, sets it to false.
// If debug is false, sets it to true.
func (l *DebugLogger) Toggle() {
	l.mx.Lock()
	defer l.mx.Unlock()
	if l.debug == false {
		l.debug = true
	} else {
		l.debug = false
	}
}

func (l *DebugLogger) ToggleOnSignal(sig os.Signal) {
	debugSig := make(chan os.Signal, 1)
	// spawn goroutine to handle signal/toggle of debug logging
	go func() {
		for {
			<-debugSig
			l.Toggle()
			if l.State() {
				l.Printf("Debug logging enabled")
			} else {
				l.Printf("Debug logging disabled")
			}
		}
	}()
	// notify send to debug sign channel on signusr1
	signal.Notify(debugSig, sig)
}

func (l *DebugLogger) State() bool {
	return l.debug
}

// Set the debug state directly.
func (l *DebugLogger) Set(debug bool) {
	l.mx.Lock()
	defer l.mx.Unlock()
	l.debug = debug
}

// Debugf calls log.Printf if debug is true.
// If debug is false, does nothing.
func (l *DebugLogger) Debugf(format string, v ...interface{}) {
	if l.debug == true {
		l.Printf(format, v...)
	}
}

// Debug calls log.Print if debug is true.
// If debug is false, does nothing.
func (l *DebugLogger) Debug(v ...interface{}) {
	if l.debug == true {
		l.Print(v...)
	}
}

// Debugln calls log.Println if debug is true.
// If debug is false, does nothing.
func (l *DebugLogger) Debugln(v ...interface{}) {
	if l.debug == true {
		l.Println(v...)
	}
}
<|endoftext|>