<commit_before>package util

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	g "github.com/onsi/ginkgo"
	"github.com/spf13/cobra"

	kapi "k8s.io/kubernetes/pkg/api"
	kclient "k8s.io/kubernetes/pkg/client/unversioned"
	clientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	"k8s.io/kubernetes/test/e2e"

	_ "github.com/openshift/origin/pkg/api/install"
	"github.com/openshift/origin/pkg/client"
	"github.com/openshift/origin/pkg/cmd/cli/config"
	configapi "github.com/openshift/origin/pkg/cmd/server/api"
	projectapi "github.com/openshift/origin/pkg/project/api"
	testutil "github.com/openshift/origin/test/util"
)

// CLI provides function to call the OpenShift CLI and Kubernetes and OpenShift
// REST clients.
type CLI struct {
	execPath        string
	verb            string
	configPath      string
	adminConfigPath string
	username        string
	outputDir       string
	globalArgs      []string
	commandArgs     []string
	finalArgs       []string
	stdin           *bytes.Buffer
	stdout          io.Writer
	stderr          io.Writer
	verbose         bool
	cmd             *cobra.Command
	kubeFramework   *e2e.Framework
}

// NewCLI initialize the upstream E2E framework and set the namespace to match
// with the project name. Note that this function does not initialize the project
// role bindings for the namespace.
func NewCLI(project, adminConfigPath string) *CLI {
	// Avoid every caller needing to provide a unique project name
	// SetupProject already treats this as a baseName
	uniqueProject := kapi.SimpleNameGenerator.GenerateName(fmt.Sprintf("%s-", project))

	client := &CLI{}
	client.kubeFramework = e2e.NewDefaultFramework(uniqueProject)
	client.outputDir = os.TempDir()
	client.username = "admin"
	client.execPath = "oc"
	if len(adminConfigPath) == 0 {
		FatalErr(fmt.Errorf("You must set the KUBECONFIG variable to admin kubeconfig."))
	}
	client.adminConfigPath = adminConfigPath

	// Register custom ns setup func
	setCreateTestingNSFunc(uniqueProject, client.SetupProject)

	return client
}

// KubeFramework returns Kubernetes framework which contains helper functions
// specific for Kubernetes resources
func (c *CLI) KubeFramework() *e2e.Framework {
	return c.kubeFramework
}

// Username returns the name of currently logged user. If there is no user assigned
// for the current session, it returns 'admin'.
func (c *CLI) Username() string {
	return c.username
}

// AsAdmin changes current config file path to the admin config.
func (c *CLI) AsAdmin() *CLI {
	nc := *c
	nc.configPath = c.adminConfigPath
	return &nc
}

// ChangeUser changes the user used by the current CLI session.
func (c *CLI) ChangeUser(name string) *CLI {
	adminClientConfig, err := testutil.GetClusterAdminClientConfig(c.adminConfigPath)
	if err != nil {
		FatalErr(err)
	}
	_, _, clientConfig, err := testutil.GetClientForUser(*adminClientConfig, name)
	if err != nil {
		FatalErr(err)
	}

	kubeConfig, err := config.CreateConfig(c.Namespace(), clientConfig)
	if err != nil {
		FatalErr(err)
	}

	c.configPath = filepath.Join(c.outputDir, name+".kubeconfig")
	err = clientcmd.WriteToFile(*kubeConfig, c.configPath)
	if err != nil {
		FatalErr(err)
	}

	c.username = name
	e2e.Logf("configPath is now %q", c.configPath)
	return c
}

// SetNamespace sets a new namespace
func (c *CLI) SetNamespace(ns string) *CLI {
	c.kubeFramework.Namespace = &kapi.Namespace{
		ObjectMeta: kapi.ObjectMeta{
			Name: ns,
		},
	}
	return c
}

// SetOutputDir change the default output directory for temporary files
func (c *CLI) SetOutputDir(dir string) *CLI {
	c.outputDir = dir
	return c
}

// SetupProject creates a new project and assign a random user to the project.
// All resources will be then created within this project and Kubernetes E2E
// suite will destroy the project after test case finish.
func (c *CLI) SetupProject(name string, kubeClient *kclient.Client, _ map[string]string) (*kapi.Namespace, error) {
	newNamespace := kapi.SimpleNameGenerator.GenerateName(fmt.Sprintf("extended-test-%s-", name))
	c.SetNamespace(newNamespace).ChangeUser(fmt.Sprintf("%s-user", c.Namespace()))
	e2e.Logf("The user is now %q", c.Username())

	e2e.Logf("Creating project %q", c.Namespace())
	_, err := c.REST().ProjectRequests().Create(&projectapi.ProjectRequest{
		ObjectMeta: kapi.ObjectMeta{Name: c.Namespace()},
	})
	if err != nil {
		e2e.Logf("Failed to create a project and namespace %q: %v", c.Namespace(), err)
		return nil, err
	}
	return &kapi.Namespace{ObjectMeta: kapi.ObjectMeta{Name: c.Namespace()}}, err
}

// Verbose turns on printing verbose messages when executing OpenShift commands
func (c *CLI) Verbose() *CLI {
	c.verbose = true
	return c
}

// REST provides an OpenShift REST client for the current user. If the user is not
// set, then it provides REST client for the cluster admin user
func (c *CLI) REST() *client.Client {
	_, clientConfig, err := configapi.GetKubeClient(c.configPath)
	osClient, err := client.New(clientConfig)
	if err != nil {
		FatalErr(err)
	}
	return osClient
}

// AdminREST provides an OpenShift REST client for the cluster admin user.
func (c *CLI) AdminREST() *client.Client {
	_, clientConfig, err := configapi.GetKubeClient(c.adminConfigPath)
	osClient, err := client.New(clientConfig)
	if err != nil {
		FatalErr(err)
	}
	return osClient
}

// KubeREST provides a Kubernetes REST client for the current namespace
func (c *CLI) KubeREST() *kclient.Client {
	kubeClient, _, err := configapi.GetKubeClient(c.configPath)
	if err != nil {
		FatalErr(err)
	}
	return kubeClient
}

// AdminKubeREST provides a Kubernetes REST client for the cluster admin user.
func (c *CLI) AdminKubeREST() *kclient.Client {
	kubeClient, _, err := configapi.GetKubeClient(c.adminConfigPath)
	if err != nil {
		FatalErr(err)
	}
	return kubeClient
}

// Namespace returns the name of the namespace used in the current test case.
// If the namespace is not set, an empty string is returned.
func (c *CLI) Namespace() string {
	if c.kubeFramework.Namespace == nil {
		return ""
	}
	return c.kubeFramework.Namespace.Name
}

// setOutput allows to override the default command output
func (c *CLI) setOutput(out io.Writer) *CLI {
	c.stdout = out
	return c
}

// Run executes given OpenShift CLI command verb (iow. "oc <verb>").
// This function also override the default 'stdout' to redirect all output
// to a buffer and prepare the global flags such as namespace and config path.
func (c *CLI) Run(commands ...string) *CLI {
	in, out, errout := &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}
	nc := &CLI{
		execPath:        c.execPath,
		verb:            commands[0],
		kubeFramework:   c.KubeFramework(),
		adminConfigPath: c.adminConfigPath,
		configPath:      c.configPath,
		username:        c.username,
		outputDir:       c.outputDir,
		globalArgs: append(commands, []string{
			fmt.Sprintf("--namespace=%s", c.Namespace()),
			fmt.Sprintf("--config=%s", c.configPath),
		}...),
	}
	nc.stdin, nc.stdout, nc.stderr = in, out, errout
	return nc.setOutput(c.stdout)
}

// Template sets a Go template for the OpenShift CLI command.
// This is equivalent of running "oc get foo -o template --template='{{ .spec }}'"
func (c *CLI) Template(t string) *CLI {
	if c.verb != "get" {
		FatalErr("Cannot use Template() for non-get verbs.")
	}
	templateArgs := []string{"--output=template", fmt.Sprintf("--template=%s", t)}
	commandArgs := append(c.commandArgs, templateArgs...)
	c.finalArgs = append(c.globalArgs, commandArgs...)
	return c
}

// InputString adds expected input to the command
func (c *CLI) InputString(input string) *CLI {
	c.stdin.WriteString(input)
	return c
}

// Args sets the additional arguments for the OpenShift CLI command
func (c *CLI) Args(args ...string) *CLI {
	c.commandArgs = args
	c.finalArgs = append(c.globalArgs, c.commandArgs...)
	return c
}

func (c *CLI) printCmd() string {
	return strings.Join(c.finalArgs, " ")
}

// Output executes the command and return the output as string
func (c *CLI) Output() (string, error) {
	if c.verbose {
		fmt.Printf("DEBUG: oc %s\n", c.printCmd())
	}
	cmd := exec.Command(c.execPath, c.finalArgs...)
	cmd.Stdin = c.stdin
	e2e.Logf("Running '%s %s'", c.execPath, strings.Join(c.finalArgs, " "))
	out, err := cmd.CombinedOutput()
	trimmed := strings.TrimSpace(string(out))
	switch err.(type) {
	case nil:
		c.stdout = bytes.NewBuffer(out)
		return trimmed, nil
	case *exec.ExitError:
		e2e.Logf("Error running %v:\n%s", cmd, trimmed)
		return trimmed, err
	default:
		FatalErr(fmt.Errorf("unable to execute %q: %v", c.execPath, err))
		// unreachable code
		return "", nil
	}
}

// Stdout returns the current stdout writer
func (c *CLI) Stdout() io.Writer {
	return c.stdout
}

// OutputToFile executes the command and store output to a file
func (c *CLI) OutputToFile(filename string) (string, error) {
	content, err := c.Output()
	if err != nil {
		return "", err
	}
	path := filepath.Join(c.outputDir, c.Namespace()+"-"+filename)
	return path, ioutil.WriteFile(path, []byte(content), 0644)
}

// Execute executes the current command and return error if the execution failed
// This function will set the default output to Ginkgo writer.
func (c *CLI) Execute() error {
	out, err := c.Output()
	if _, err := io.Copy(g.GinkgoWriter, strings.NewReader(out+"\n")); err != nil {
		fmt.Printf("ERROR: Unable to copy the output to ginkgo writer")
	}
	os.Stdout.Sync()
	return err
}

// FatalErr exits the test in case a fatal error has occurred.
func FatalErr(msg interface{}) {
	e2e.Failf("%v", msg)
}
<commit_msg>Wait until user has access to project in extended<commit_after>package util

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	g "github.com/onsi/ginkgo"
	"github.com/spf13/cobra"

	kapi "k8s.io/kubernetes/pkg/api"
	apierrs "k8s.io/kubernetes/pkg/api/errors"
	kclient "k8s.io/kubernetes/pkg/client/unversioned"
	clientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e"

	_ "github.com/openshift/origin/pkg/api/install"
	"github.com/openshift/origin/pkg/client"
	"github.com/openshift/origin/pkg/cmd/cli/config"
	configapi "github.com/openshift/origin/pkg/cmd/server/api"
	projectapi "github.com/openshift/origin/pkg/project/api"
	testutil "github.com/openshift/origin/test/util"
)

// CLI provides function to call the OpenShift CLI and Kubernetes and OpenShift
// REST clients.
type CLI struct {
	execPath        string
	verb            string
	configPath      string
	adminConfigPath string
	username        string
	outputDir       string
	globalArgs      []string
	commandArgs     []string
	finalArgs       []string
	stdin           *bytes.Buffer
	stdout          io.Writer
	stderr          io.Writer
	verbose         bool
	cmd             *cobra.Command
	kubeFramework   *e2e.Framework
}

// NewCLI initialize the upstream E2E framework and set the namespace to match
// with the project name. Note that this function does not initialize the project
// role bindings for the namespace.
func NewCLI(project, adminConfigPath string) *CLI {
	// Avoid every caller needing to provide a unique project name
	// SetupProject already treats this as a baseName
	uniqueProject := kapi.SimpleNameGenerator.GenerateName(fmt.Sprintf("%s-", project))

	client := &CLI{}
	client.kubeFramework = e2e.NewDefaultFramework(uniqueProject)
	client.outputDir = os.TempDir()
	client.username = "admin"
	client.execPath = "oc"
	if len(adminConfigPath) == 0 {
		FatalErr(fmt.Errorf("You must set the KUBECONFIG variable to admin kubeconfig."))
	}
	client.adminConfigPath = adminConfigPath

	// Register custom ns setup func
	setCreateTestingNSFunc(uniqueProject, client.SetupProject)

	return client
}

// KubeFramework returns Kubernetes framework which contains helper functions
// specific for Kubernetes resources
func (c *CLI) KubeFramework() *e2e.Framework {
	return c.kubeFramework
}

// Username returns the name of currently logged user. If there is no user assigned
// for the current session, it returns 'admin'.
func (c *CLI) Username() string {
	return c.username
}

// AsAdmin changes current config file path to the admin config.
func (c *CLI) AsAdmin() *CLI {
	nc := *c
	nc.configPath = c.adminConfigPath
	return &nc
}

// ChangeUser changes the user used by the current CLI session.
func (c *CLI) ChangeUser(name string) *CLI {
	adminClientConfig, err := testutil.GetClusterAdminClientConfig(c.adminConfigPath)
	if err != nil {
		FatalErr(err)
	}
	_, _, clientConfig, err := testutil.GetClientForUser(*adminClientConfig, name)
	if err != nil {
		FatalErr(err)
	}

	kubeConfig, err := config.CreateConfig(c.Namespace(), clientConfig)
	if err != nil {
		FatalErr(err)
	}

	c.configPath = filepath.Join(c.outputDir, name+".kubeconfig")
	err = clientcmd.WriteToFile(*kubeConfig, c.configPath)
	if err != nil {
		FatalErr(err)
	}

	c.username = name
	e2e.Logf("configPath is now %q", c.configPath)
	return c
}

// SetNamespace sets a new namespace
func (c *CLI) SetNamespace(ns string) *CLI {
	c.kubeFramework.Namespace = &kapi.Namespace{
		ObjectMeta: kapi.ObjectMeta{
			Name: ns,
		},
	}
	return c
}

// SetOutputDir change the default output directory for temporary files
func (c *CLI) SetOutputDir(dir string) *CLI {
	c.outputDir = dir
	return c
}

// SetupProject creates a new project and assign a random user to the project.
// All resources will be then created within this project and Kubernetes E2E
// suite will destroy the project after test case finish.
func (c *CLI) SetupProject(name string, kubeClient *kclient.Client, _ map[string]string) (*kapi.Namespace, error) {
	newNamespace := kapi.SimpleNameGenerator.GenerateName(fmt.Sprintf("extended-test-%s-", name))
	c.SetNamespace(newNamespace).ChangeUser(fmt.Sprintf("%s-user", c.Namespace()))
	e2e.Logf("The user is now %q", c.Username())

	e2e.Logf("Creating project %q", c.Namespace())
	_, err := c.REST().ProjectRequests().Create(&projectapi.ProjectRequest{
		ObjectMeta: kapi.ObjectMeta{Name: c.Namespace()},
	})
	if err != nil {
		e2e.Logf("Failed to create a project and namespace %q: %v", c.Namespace(), err)
		return nil, err
	}
	if err := wait.ExponentialBackoff(kclient.DefaultBackoff, func() (bool, error) {
		if _, err := c.KubeREST().Pods(c.Namespace()).List(kapi.ListOptions{}); err != nil {
			if apierrs.IsForbidden(err) {
				e2e.Logf("Waiting for user to have access to the namespace")
				return false, nil
			}
		}
		return true, nil
	}); err != nil {
		return nil, err
	}
	return &kapi.Namespace{ObjectMeta: kapi.ObjectMeta{Name: c.Namespace()}}, err
}

// Verbose turns on printing verbose messages when executing OpenShift commands
func (c *CLI) Verbose() *CLI {
	c.verbose = true
	return c
}

// REST provides an OpenShift REST client for the current user. If the user is not
// set, then it provides REST client for the cluster admin user
func (c *CLI) REST() *client.Client {
	_, clientConfig, err := configapi.GetKubeClient(c.configPath)
	osClient, err := client.New(clientConfig)
	if err != nil {
		FatalErr(err)
	}
	return osClient
}

// AdminREST provides an OpenShift REST client for the cluster admin user.
func (c *CLI) AdminREST() *client.Client {
	_, clientConfig, err := configapi.GetKubeClient(c.adminConfigPath)
	osClient, err := client.New(clientConfig)
	if err != nil {
		FatalErr(err)
	}
	return osClient
}

// KubeREST provides a Kubernetes REST client for the current namespace
func (c *CLI) KubeREST() *kclient.Client {
	kubeClient, _, err := configapi.GetKubeClient(c.configPath)
	if err != nil {
		FatalErr(err)
	}
	return kubeClient
}

// AdminKubeREST provides a Kubernetes REST client for the cluster admin user.
func (c *CLI) AdminKubeREST() *kclient.Client {
	kubeClient, _, err := configapi.GetKubeClient(c.adminConfigPath)
	if err != nil {
		FatalErr(err)
	}
	return kubeClient
}

// Namespace returns the name of the namespace used in the current test case.
// If the namespace is not set, an empty string is returned.
func (c *CLI) Namespace() string {
	if c.kubeFramework.Namespace == nil {
		return ""
	}
	return c.kubeFramework.Namespace.Name
}

// setOutput allows to override the default command output
func (c *CLI) setOutput(out io.Writer) *CLI {
	c.stdout = out
	return c
}

// Run executes given OpenShift CLI command verb (iow. "oc <verb>").
// This function also override the default 'stdout' to redirect all output
// to a buffer and prepare the global flags such as namespace and config path.
func (c *CLI) Run(commands ...string) *CLI {
	in, out, errout := &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}
	nc := &CLI{
		execPath:        c.execPath,
		verb:            commands[0],
		kubeFramework:   c.KubeFramework(),
		adminConfigPath: c.adminConfigPath,
		configPath:      c.configPath,
		username:        c.username,
		outputDir:       c.outputDir,
		globalArgs: append(commands, []string{
			fmt.Sprintf("--namespace=%s", c.Namespace()),
			fmt.Sprintf("--config=%s", c.configPath),
		}...),
	}
	nc.stdin, nc.stdout, nc.stderr = in, out, errout
	return nc.setOutput(c.stdout)
}

// Template sets a Go template for the OpenShift CLI command.
// This is equivalent of running "oc get foo -o template --template='{{ .spec }}'"
func (c *CLI) Template(t string) *CLI {
	if c.verb != "get" {
		FatalErr("Cannot use Template() for non-get verbs.")
	}
	templateArgs := []string{"--output=template", fmt.Sprintf("--template=%s", t)}
	commandArgs := append(c.commandArgs, templateArgs...)
	c.finalArgs = append(c.globalArgs, commandArgs...)
	return c
}

// InputString adds expected input to the command
func (c *CLI) InputString(input string) *CLI {
	c.stdin.WriteString(input)
	return c
}

// Args sets the additional arguments for the OpenShift CLI command
func (c *CLI) Args(args ...string) *CLI {
	c.commandArgs = args
	c.finalArgs = append(c.globalArgs, c.commandArgs...)
	return c
}

func (c *CLI) printCmd() string {
	return strings.Join(c.finalArgs, " ")
}

// Output executes the command and return the output as string
func (c *CLI) Output() (string, error) {
	if c.verbose {
		fmt.Printf("DEBUG: oc %s\n", c.printCmd())
	}
	cmd := exec.Command(c.execPath, c.finalArgs...)
	cmd.Stdin = c.stdin
	e2e.Logf("Running '%s %s'", c.execPath, strings.Join(c.finalArgs, " "))
	out, err := cmd.CombinedOutput()
	trimmed := strings.TrimSpace(string(out))
	switch err.(type) {
	case nil:
		c.stdout = bytes.NewBuffer(out)
		return trimmed, nil
	case *exec.ExitError:
		e2e.Logf("Error running %v:\n%s", cmd, trimmed)
		return trimmed, err
	default:
		FatalErr(fmt.Errorf("unable to execute %q: %v", c.execPath, err))
		// unreachable code
		return "", nil
	}
}

// Stdout returns the current stdout writer
func (c *CLI) Stdout() io.Writer {
	return c.stdout
}

// OutputToFile executes the command and store output to a file
func (c *CLI) OutputToFile(filename string) (string, error) {
	content, err := c.Output()
	if err != nil {
		return "", err
	}
	path := filepath.Join(c.outputDir, c.Namespace()+"-"+filename)
	return path, ioutil.WriteFile(path, []byte(content), 0644)
}

// Execute executes the current command and return error if the execution failed
// This function will set the default output to Ginkgo writer.
func (c *CLI) Execute() error {
	out, err := c.Output()
	if _, err := io.Copy(g.GinkgoWriter, strings.NewReader(out+"\n")); err != nil {
		fmt.Printf("ERROR: Unable to copy the output to ginkgo writer")
	}
	os.Stdout.Sync()
	return err
}

// FatalErr exits the test in case a fatal error has occurred.
func FatalErr(msg interface{}) {
	e2e.Failf("%v", msg)
}
<|endoftext|>


<commit_before>package protocol

import (
	"context"
	"fmt"
	"time"

	"github.com/golang/protobuf/proto"
	"go.uber.org/zap"

	"github.com/status-im/status-go/protocol/common"
	"github.com/status-im/status-go/protocol/communities"
	"github.com/status-im/status-go/protocol/protobuf"
	"github.com/status-im/status-go/protocol/transport"
)

func (m *Messenger) GetCurrentUserStatus() (*UserStatus, error) {

	status := &UserStatus{
		StatusType: int(protobuf.StatusUpdate_AUTOMATIC),
		Clock:      0,
		CustomText: "",
	}

	err := m.settings.GetCurrentStatus(status)
	if err != nil {
		m.logger.Debug("Error obtaining latest status", zap.Error(err))
		return nil, err
	}

	return status, nil
}

func (m *Messenger) sendUserStatus(ctx context.Context, status UserStatus) error {
	shouldBroadcastUserStatus, err := m.settings.ShouldBroadcastUserStatus()
	if err != nil {
		return err
	}

	if !shouldBroadcastUserStatus {
		m.logger.Debug("user status should not be broadcasted")
		return nil
	}

	status.Clock = uint64(time.Now().Unix())

	err = m.settings.SaveSetting("current-user-status", status)
	if err != nil {
		return err
	}

	statusUpdate := &protobuf.StatusUpdate{
		Clock:      status.Clock,
		StatusType: protobuf.StatusUpdate_StatusType(status.StatusType),
		CustomText: status.CustomText,
	}

	encodedMessage, err := proto.Marshal(statusUpdate)
	if err != nil {
		return err
	}

	contactCodeTopic := transport.ContactCodeTopic(&m.identity.PublicKey)

	rawMessage := common.RawMessage{
		LocalChatID:         contactCodeTopic,
		Payload:             encodedMessage,
		MessageType:         protobuf.ApplicationMetadataMessage_STATUS_UPDATE,
		ResendAutomatically: true,
	}

	_, err = m.sender.SendPublic(ctx, contactCodeTopic, rawMessage)
	if err != nil {
		return err
	}

	joinedCommunities, err := m.communitiesManager.Joined()
	if err != nil {
		return err
	}
	for _, community := range joinedCommunities {
		rawMessage.LocalChatID = community.StatusUpdatesChannelID()
		_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
		if err != nil {
			return err
		}
	}

	return nil
}

func (m *Messenger) sendCurrentUserStatus(ctx context.Context) {
	err := m.persistence.CleanOlderStatusUpdates()
	if err != nil {
		m.logger.Debug("Error cleaning status updates", zap.Error(err))
		return
	}

	shouldBroadcastUserStatus, err := m.settings.ShouldBroadcastUserStatus()
	if err != nil {
		m.logger.Debug("Error while getting status broadcast setting", zap.Error(err))
		return
	}

	if !shouldBroadcastUserStatus {
		m.logger.Debug("user status should not be broadcasted")
		return
	}

	currStatus, err := m.GetCurrentUserStatus()
	if err != nil {
		m.logger.Debug("Error obtaining latest status", zap.Error(err))
		return
	}

	if err := m.sendUserStatus(ctx, *currStatus); err != nil {
		m.logger.Debug("Error when sending the latest user status", zap.Error(err))
	}
}

func (m *Messenger) sendCurrentUserStatusToCommunity(ctx context.Context, community *communities.Community) error {
	logger := m.logger.Named("sendCurrentUserStatusToCommunity")

	shouldBroadcastUserStatus, err := m.settings.ShouldBroadcastUserStatus()
	if err != nil {
		logger.Debug("m.settings.ShouldBroadcastUserStatus error", zap.Error(err))
		return err
	}

	if !shouldBroadcastUserStatus {
		logger.Debug("user status should not be broadcasted")
		return nil
	}

	status, err := m.GetCurrentUserStatus()
	if err != nil {
		logger.Debug("Error obtaining latest status", zap.Error(err))
		return err
	}

	status.Clock = uint64(time.Now().Unix())

	err = m.settings.SaveSetting("current-user-status", status)
	if err != nil {
		logger.Debug("m.settings.SaveSetting error",
			zap.Any("current-user-status", status),
			zap.Error(err))
		return err
	}

	statusUpdate := &protobuf.StatusUpdate{
		Clock:      status.Clock,
		StatusType: protobuf.StatusUpdate_StatusType(status.StatusType),
		CustomText: status.CustomText,
	}

	encodedMessage, err := proto.Marshal(statusUpdate)
	if err != nil {
		logger.Debug("proto.Marshal error",
			zap.Any("protobuf.StatusUpdate", statusUpdate),
			zap.Error(err))
		return err
	}

	rawMessage := common.RawMessage{
		LocalChatID:         community.StatusUpdatesChannelID(),
		Payload:             encodedMessage,
		MessageType:         protobuf.ApplicationMetadataMessage_STATUS_UPDATE,
		ResendAutomatically: true,
	}

	_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
	if err != nil {
		logger.Debug("m.sender.SendPublic error", zap.Error(err))
		return err
	}

	return nil
}

func (m *Messenger) broadcastLatestUserStatus() {
	m.logger.Debug("broadcasting user status")
	ctx := context.Background()
	m.sendCurrentUserStatus(ctx)
	go func() {
		for {
			select {
			case <-time.After(5 * time.Minute):
				m.sendCurrentUserStatus(ctx)
			case <-m.quit:
				return
			}
		}
	}()
}

func (m *Messenger) SetUserStatus(ctx context.Context, newStatus int, newCustomText string) error {
	if len([]rune(newCustomText)) > maxStatusMessageText {
		return fmt.Errorf("custom text shouldn't be longer than %d", maxStatusMessageText)
	}

	if newStatus != int(protobuf.StatusUpdate_AUTOMATIC) &&
		newStatus != int(protobuf.StatusUpdate_DO_NOT_DISTURB) &&
		newStatus != int(protobuf.StatusUpdate_ALWAYS_ONLINE) &&
		newStatus != int(protobuf.StatusUpdate_INACTIVE) {
		return fmt.Errorf("unknown status type")
	}

	currStatus, err := m.GetCurrentUserStatus()
	if err != nil {
		m.logger.Debug("Error obtaining latest status", zap.Error(err))
		return err
	}

	if newStatus == currStatus.StatusType && newCustomText == currStatus.CustomText {
		m.logger.Debug("Status type did not change")
		return nil
	}

	currStatus.StatusType = newStatus
	currStatus.CustomText = newCustomText

	return m.sendUserStatus(ctx, *currStatus)
}

func (m *Messenger) HandleStatusUpdate(state *ReceivedMessageState, statusMessage protobuf.StatusUpdate) error {
	if err := ValidateStatusUpdate(statusMessage); err != nil {
		return err
	}

	if common.IsPubKeyEqual(state.CurrentMessageState.PublicKey, &m.identity.PublicKey) { // Status message is ours
		currentStatus, err := m.GetCurrentUserStatus()
		if err != nil {
			m.logger.Debug("Error obtaining latest status", zap.Error(err))
			return err
		}

		if currentStatus.Clock >= statusMessage.Clock {
			return nil // older status message, or status does not change ignoring it
		}
		newStatus := ToUserStatus(statusMessage)
		err = m.settings.SaveSetting("current-user-status", newStatus)
		if err != nil {
			return err
		}
		state.Response.SetCurrentStatus(newStatus)
	} else {
		statusUpdate := ToUserStatus(statusMessage)
		statusUpdate.PublicKey = state.CurrentMessageState.Contact.ID

		err := m.persistence.InsertStatusUpdate(statusUpdate)
		if err != nil {
			return err
		}
		state.Response.AddStatusUpdate(statusUpdate)
	}

	return nil
}

func (m *Messenger) StatusUpdates() ([]UserStatus, error) {
	return m.persistence.StatusUpdates()
}
<commit_msg>fix: only broadcast status once we are connected (#2456)<commit_after>package protocol

import (
	"context"
	"fmt"
	"time"

	"github.com/golang/protobuf/proto"
	"go.uber.org/zap"

	"github.com/status-im/status-go/protocol/common"
	"github.com/status-im/status-go/protocol/communities"
	"github.com/status-im/status-go/protocol/protobuf"
	"github.com/status-im/status-go/protocol/transport"
)

func (m *Messenger) GetCurrentUserStatus() (*UserStatus, error) {

	status := &UserStatus{
		StatusType: int(protobuf.StatusUpdate_AUTOMATIC),
		Clock:      0,
		CustomText: "",
	}

	err := m.settings.GetCurrentStatus(status)
	if err != nil {
		m.logger.Debug("Error obtaining latest status", zap.Error(err))
		return nil, err
	}

	return status, nil
}

func (m *Messenger) sendUserStatus(ctx context.Context, status UserStatus) error {
	shouldBroadcastUserStatus, err := m.settings.ShouldBroadcastUserStatus()
	if err != nil {
		return err
	}

	if !shouldBroadcastUserStatus {
		m.logger.Debug("user status should not be broadcasted")
		return nil
	}

	status.Clock = uint64(time.Now().Unix())

	err = m.settings.SaveSetting("current-user-status", status)
	if err != nil {
		return err
	}

	statusUpdate := &protobuf.StatusUpdate{
		Clock:      status.Clock,
		StatusType: protobuf.StatusUpdate_StatusType(status.StatusType),
		CustomText: status.CustomText,
	}

	encodedMessage, err := proto.Marshal(statusUpdate)
	if err != nil {
		return err
	}

	contactCodeTopic := transport.ContactCodeTopic(&m.identity.PublicKey)

	rawMessage := common.RawMessage{
		LocalChatID:         contactCodeTopic,
		Payload:             encodedMessage,
		MessageType:         protobuf.ApplicationMetadataMessage_STATUS_UPDATE,
		ResendAutomatically: true,
	}

	_, err = m.sender.SendPublic(ctx, contactCodeTopic, rawMessage)
	if err != nil {
		return err
	}

	joinedCommunities, err := m.communitiesManager.Joined()
	if err != nil {
		return err
	}
	for _, community := range joinedCommunities {
		rawMessage.LocalChatID = community.StatusUpdatesChannelID()
		_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
		if err != nil {
			return err
		}
	}

	return nil
}

func (m *Messenger) sendCurrentUserStatus(ctx context.Context) {
	err := m.persistence.CleanOlderStatusUpdates()
	if err != nil {
		m.logger.Debug("Error cleaning status updates", zap.Error(err))
		return
	}

	shouldBroadcastUserStatus, err := m.settings.ShouldBroadcastUserStatus()
	if err != nil {
		m.logger.Debug("Error while getting status broadcast setting", zap.Error(err))
		return
	}

	if !shouldBroadcastUserStatus {
		m.logger.Debug("user status should not be broadcasted")
		return
	}

	currStatus, err := m.GetCurrentUserStatus()
	if err != nil {
		m.logger.Debug("Error obtaining latest status", zap.Error(err))
		return
	}

	if err := m.sendUserStatus(ctx, *currStatus); err != nil {
		m.logger.Debug("Error when sending the latest user status", zap.Error(err))
	}
}

func (m *Messenger) sendCurrentUserStatusToCommunity(ctx context.Context, community *communities.Community) error {
	logger := m.logger.Named("sendCurrentUserStatusToCommunity")

	shouldBroadcastUserStatus, err := m.settings.ShouldBroadcastUserStatus()
	if err != nil {
		logger.Debug("m.settings.ShouldBroadcastUserStatus error", zap.Error(err))
		return err
	}

	if !shouldBroadcastUserStatus {
		logger.Debug("user status should not be broadcasted")
		return nil
	}

	status, err := m.GetCurrentUserStatus()
	if err != nil {
		logger.Debug("Error obtaining latest status", zap.Error(err))
		return err
	}

	status.Clock = uint64(time.Now().Unix())

	err = m.settings.SaveSetting("current-user-status", status)
	if err != nil {
		logger.Debug("m.settings.SaveSetting error",
			zap.Any("current-user-status", status),
			zap.Error(err))
		return err
	}

	statusUpdate := &protobuf.StatusUpdate{
		Clock:      status.Clock,
		StatusType: protobuf.StatusUpdate_StatusType(status.StatusType),
		CustomText: status.CustomText,
	}

	encodedMessage, err := proto.Marshal(statusUpdate)
	if err != nil {
		logger.Debug("proto.Marshal error",
			zap.Any("protobuf.StatusUpdate", statusUpdate),
			zap.Error(err))
		return err
	}

	rawMessage := common.RawMessage{
		LocalChatID:         community.StatusUpdatesChannelID(),
		Payload:             encodedMessage,
		MessageType:         protobuf.ApplicationMetadataMessage_STATUS_UPDATE,
		ResendAutomatically: true,
	}

	_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
	if err != nil {
		logger.Debug("m.sender.SendPublic error", zap.Error(err))
		return err
	}

	return nil
}

func (m *Messenger) broadcastLatestUserStatus() {
	m.logger.Debug("broadcasting user status")
	ctx := context.Background()
	go func() {
		// Ensure that we are connected before sending a message
		time.Sleep(5 * time.Second)
		m.sendCurrentUserStatus(ctx)
	}()

	go func() {
		for {
			select {
			case <-time.After(5 * time.Minute):
				m.sendCurrentUserStatus(ctx)
			case <-m.quit:
				return
			}
		}
	}()
}

func (m *Messenger) SetUserStatus(ctx context.Context, newStatus int, newCustomText string) error {
	if len([]rune(newCustomText)) > maxStatusMessageText {
		return fmt.Errorf("custom text shouldn't be longer than %d", maxStatusMessageText)
	}

	if newStatus != int(protobuf.StatusUpdate_AUTOMATIC) &&
		newStatus != int(protobuf.StatusUpdate_DO_NOT_DISTURB) &&
		newStatus != int(protobuf.StatusUpdate_ALWAYS_ONLINE) &&
		newStatus != int(protobuf.StatusUpdate_INACTIVE) {
		return fmt.Errorf("unknown status type")
	}

	currStatus, err := m.GetCurrentUserStatus()
	if err != nil {
		m.logger.Debug("Error obtaining latest status", zap.Error(err))
		return err
	}

	if newStatus == currStatus.StatusType && newCustomText == currStatus.CustomText {
		m.logger.Debug("Status type did not change")
		return nil
	}

	currStatus.StatusType = newStatus
	currStatus.CustomText = newCustomText

	return m.sendUserStatus(ctx, *currStatus)
}

func (m *Messenger) HandleStatusUpdate(state *ReceivedMessageState, statusMessage protobuf.StatusUpdate) error {
	if err := ValidateStatusUpdate(statusMessage); err != nil {
		return err
	}

	if common.IsPubKeyEqual(state.CurrentMessageState.PublicKey, &m.identity.PublicKey) { // Status message is ours
		currentStatus, err := m.GetCurrentUserStatus()
		if err != nil {
			m.logger.Debug("Error obtaining latest status", zap.Error(err))
			return err
		}

		if currentStatus.Clock >= statusMessage.Clock {
			return nil // older status message, or status does not change ignoring it
		}
		newStatus := ToUserStatus(statusMessage)
		err = m.settings.SaveSetting("current-user-status", newStatus)
		if err != nil {
			return err
		}
		state.Response.SetCurrentStatus(newStatus)
	} else {
		statusUpdate := ToUserStatus(statusMessage)
		statusUpdate.PublicKey = state.CurrentMessageState.Contact.ID

		err := m.persistence.InsertStatusUpdate(statusUpdate)
		if err != nil {
			return err
		}
		state.Response.AddStatusUpdate(statusUpdate)
	}

	return nil
}

func (m *Messenger) StatusUpdates() ([]UserStatus, error) {
	return m.persistence.StatusUpdates()
}
<|endoftext|>


<commit_before>// Package rackspace implements a DNS provider for solving the DNS-01
// challenge using rackspace DNS.
package rackspace

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/xenolf/lego/acme"
)

// rackspaceAPIURL represents the Identity API endpoint to call
var rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens"

// DNSProvider is an implementation of the acme.ChallengeProvider interface
// used to store the reusable token and DNS API endpoint
type DNSProvider struct {
	token            string
	cloudDNSEndpoint string
}

// NewDNSProvider returns a DNSProvider instance configured for Rackspace.
// Credentials must be passed in the environment variables: RACKSPACE_USER
// and RACKSPACE_API_KEY.
func NewDNSProvider() (*DNSProvider, error) {
	user := os.Getenv("RACKSPACE_USER")
	key := os.Getenv("RACKSPACE_API_KEY")
	return NewDNSProviderCredentials(user, key)
}

// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for Rackspace. It authenticates against
// the API, also grabbing the DNS Endpoint.
func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) {
	if user == "" || key == "" {
		return nil, fmt.Errorf("Rackspace credentials missing")
	}

	type APIKeyCredentials struct {
		Username string `json:"username"`
		APIKey   string `json:"apiKey"`
	}

	type Auth struct {
		APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"`
	}

	type RackspaceAuthData struct {
		Auth `json:"auth"`
	}

	type RackspaceIdentity struct {
		Access struct {
			ServiceCatalog []struct {
				Endpoints []struct {
					PublicURL string `json:"publicURL"`
					TenantID  string `json:"tenantId"`
				} `json:"endpoints"`
				Name string `json:"name"`
			} `json:"serviceCatalog"`
			Token struct {
				ID string `json:"id"`
			} `json:"token"`
		} `json:"access"`
	}

	authData := RackspaceAuthData{
		Auth: Auth{
			APIKeyCredentials: APIKeyCredentials{
				Username: user,
				APIKey:   key,
			},
		},
	}

	body, err := json.Marshal(authData)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", rackspaceAPIURL, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	client := http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("Error querying Rackspace Identity API: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Rackspace Authentication failed. Response code: %d", resp.StatusCode)
	}

	var rackspaceIdentity RackspaceIdentity
	err = json.NewDecoder(resp.Body).Decode(&rackspaceIdentity)
	if err != nil {
		return nil, err
	}

	// Iterate through the Service Catalog to get the DNS Endpoint
	var dnsEndpoint string
	for _, service := range rackspaceIdentity.Access.ServiceCatalog {
		if service.Name == "cloudDNS" {
			dnsEndpoint = service.Endpoints[0].PublicURL
			break
		}
	}
	if dnsEndpoint == "" {
		return nil, fmt.Errorf("Failed to populate DNS endpoint, check Rackspace API for changes.")
	}

	return &DNSProvider{
		token:            rackspaceIdentity.Access.Token.ID,
		cloudDNSEndpoint: dnsEndpoint,
	}, nil
}

// Present creates a TXT record to fulfil the dns-01 challenge
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
	fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
	zoneID, err := c.getHostedZoneID(fqdn)
	if err != nil {
		return err
	}

	rec := RackspaceRecords{
		RackspaceRecord: []RackspaceRecord{{
			Name: acme.UnFqdn(fqdn),
			Type: "TXT",
			Data: value,
			TTL:  300,
		}},
	}

	body, err := json.Marshal(rec)
	if err != nil {
		return err
	}

	_, err = c.makeRequest("POST", fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body))
	if err != nil {
		return err
	}

	return nil
}

// CleanUp removes the TXT record matching the specified parameters
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
	fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
	zoneID, err := c.getHostedZoneID(fqdn)
	if err != nil {
		return err
	}

	record, err := c.findTxtRecord(fqdn, zoneID)
	if err != nil {
		return err
	}

	_, err = c.makeRequest("DELETE", fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil)
	if err != nil {
		return err
	}

	return nil
}

// getHostedZoneID performs a lookup to get the DNS zone which needs
// modifying for a given FQDN
func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
	// HostedZones represents the response when querying Rackspace DNS zones
	type ZoneSearchResponse struct {
		TotalEntries int `json:"totalEntries"`
		HostedZones  []struct {
			ID   int    `json:"id"`
			Name string `json:"name"`
		} `json:"domains"`
	}

	authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
	if err != nil {
		return 0, err
	}

	result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil)
	if err != nil {
		return 0, err
	}

	var zoneSearchResponse ZoneSearchResponse
	err = json.Unmarshal(result, &zoneSearchResponse)
	if err != nil {
		return 0, err
	}

	// If nothing was returned, or for whatever reason more than 1 was returned (the search uses exact match, so should not occur)
	if zoneSearchResponse.TotalEntries != 1 {
		return 0, fmt.Errorf("Found %d zones for %s in Rackspace for domain %s", zoneSearchResponse.TotalEntries, authZone, fqdn)
	}

	return zoneSearchResponse.HostedZones[0].ID, nil
}

// findTxtRecord searches a DNS zone for a TXT record with a specific name
func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*RackspaceRecord, error) {
	result, err := c.makeRequest("GET", fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil)
	if err != nil {
		return nil, err
	}

	var records RackspaceRecords
	err = json.Unmarshal(result, &records)
	if err != nil {
		return nil, err
	}

	recordsLength := len(records.RackspaceRecord)
	switch recordsLength {
	case 1:
		break
	case 0:
		return nil, fmt.Errorf("No TXT record found for %s", fqdn)
	default:
		return nil, fmt.Errorf("More than 1 TXT record found for %s", fqdn)
	}

	return &records.RackspaceRecord[0], nil
}

// makeRequest is a wrapper function used for making DNS API requests
func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
	url := c.cloudDNSEndpoint + uri
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("X-Auth-Token", c.token)
	req.Header.Set("Content-Type", "application/json")

	client := http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("Error querying DNS API: %v", err)
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted {
		return nil, fmt.Errorf("Request failed for %s %s. Response code: %d", method, url, resp.StatusCode)
	}

	var r json.RawMessage
	err = json.NewDecoder(resp.Body).Decode(&r)
	if err != nil {
		return nil, fmt.Errorf("JSON decode failed for %s %s. Response code: %d", method, url, resp.StatusCode)
	}

	return r, nil
}

// RackspaceRecords is the list of records sent/recieved from the DNS API
type RackspaceRecords struct {
	RackspaceRecord []RackspaceRecord `json:"records"`
}

// RackspaceRecord represents a Rackspace DNS record
type RackspaceRecord struct {
	Name string `json:"name"`
	Type string `json:"type"`
	Data string `json:"data"`
	TTL  int    `json:"ttl,omitempty"`
	ID   string `json:"id,omitempty"`
}
<commit_msg>correct spelling mistake (#424)<commit_after>// Package rackspace implements a DNS provider for solving the DNS-01
// challenge using rackspace DNS.
package rackspace

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/xenolf/lego/acme"
)

// rackspaceAPIURL represents the Identity API endpoint to call
var rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens"

// DNSProvider is an implementation of the acme.ChallengeProvider interface
// used to store the reusable token and DNS API endpoint
type DNSProvider struct {
	token            string
	cloudDNSEndpoint string
}

// NewDNSProvider returns a DNSProvider instance configured for Rackspace.
// Credentials must be passed in the environment variables: RACKSPACE_USER
// and RACKSPACE_API_KEY.
func NewDNSProvider() (*DNSProvider, error) {
	user := os.Getenv("RACKSPACE_USER")
	key := os.Getenv("RACKSPACE_API_KEY")
	return NewDNSProviderCredentials(user, key)
}

// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for Rackspace. It authenticates against
// the API, also grabbing the DNS Endpoint.
func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) {
	if user == "" || key == "" {
		return nil, fmt.Errorf("Rackspace credentials missing")
	}

	type APIKeyCredentials struct {
		Username string `json:"username"`
		APIKey   string `json:"apiKey"`
	}

	type Auth struct {
		APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"`
	}

	type RackspaceAuthData struct {
		Auth `json:"auth"`
	}

	type RackspaceIdentity struct {
		Access struct {
			ServiceCatalog []struct {
				Endpoints []struct {
					PublicURL string `json:"publicURL"`
					TenantID  string `json:"tenantId"`
				} `json:"endpoints"`
				Name string `json:"name"`
			} `json:"serviceCatalog"`
			Token struct {
				ID string `json:"id"`
			} `json:"token"`
		} `json:"access"`
	}

	authData := RackspaceAuthData{
		Auth: Auth{
			APIKeyCredentials: APIKeyCredentials{
				Username: user,
				APIKey:   key,
			},
		},
	}

	body, err := json.Marshal(authData)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", rackspaceAPIURL, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	client := http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("Error querying Rackspace Identity API: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Rackspace Authentication failed. Response code: %d", resp.StatusCode)
	}

	var rackspaceIdentity RackspaceIdentity
	err = json.NewDecoder(resp.Body).Decode(&rackspaceIdentity)
	if err != nil {
		return nil, err
	}

	// Iterate through the Service Catalog to get the DNS Endpoint
	var dnsEndpoint string
	for _, service := range rackspaceIdentity.Access.ServiceCatalog {
		if service.Name == "cloudDNS" {
			dnsEndpoint = service.Endpoints[0].PublicURL
			break
		}
	}
	if dnsEndpoint == "" {
		return nil, fmt.Errorf("Failed to populate DNS endpoint, check Rackspace API for changes.")
	}

	return &DNSProvider{
		token:            rackspaceIdentity.Access.Token.ID,
		cloudDNSEndpoint: dnsEndpoint,
	}, nil
}

// Present creates a TXT record to fulfil the dns-01 challenge
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
	fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
	zoneID, err := c.getHostedZoneID(fqdn)
	if err != nil {
		return err
	}

	rec := RackspaceRecords{
		RackspaceRecord: []RackspaceRecord{{
			Name: acme.UnFqdn(fqdn),
			Type: "TXT",
			Data: value,
			TTL:  300,
		}},
	}

	body, err := json.Marshal(rec)
	if err != nil {
		return err
	}

	_, err = c.makeRequest("POST", fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body))
	if err != nil {
		return err
	}

	return nil
}

// CleanUp removes the TXT record matching the specified parameters
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
	fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
	zoneID, err := c.getHostedZoneID(fqdn)
	if err != nil {
		return err
	}

	record, err := c.findTxtRecord(fqdn, zoneID)
	if err != nil {
		return err
	}

	_, err = c.makeRequest("DELETE", fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil)
	if err != nil {
		return err
	}

	return nil
}

// getHostedZoneID performs a lookup to get the DNS zone which needs
// modifying for a given FQDN
func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
	// HostedZones represents the response when querying Rackspace DNS zones
	type ZoneSearchResponse struct {
		TotalEntries int `json:"totalEntries"`
		HostedZones  []struct {
			ID   int    `json:"id"`
			Name string `json:"name"`
		} `json:"domains"`
	}

	authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
	if err != nil {
		return 0, err
	}

	result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil)
	if err != nil {
		return 0, err
	}

	var zoneSearchResponse ZoneSearchResponse
	err = json.Unmarshal(result, &zoneSearchResponse)
	if err != nil {
		return 0, err
	}

	// If nothing was returned, or for whatever reason more than 1 was returned (the search uses exact match, so should not occur)
	if zoneSearchResponse.TotalEntries != 1 {
		return 0, fmt.Errorf("Found %d zones for %s in Rackspace for domain %s", zoneSearchResponse.TotalEntries, authZone, fqdn)
	}

	return zoneSearchResponse.HostedZones[0].ID, nil
}

// findTxtRecord searches a DNS zone for a TXT record with a specific name
func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*RackspaceRecord, error) {
	result, err := c.makeRequest("GET", fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil)
	if err != nil {
		return nil, err
	}

	var records RackspaceRecords
	err = json.Unmarshal(result, &records)
	if err != nil {
		return nil, err
	}

	recordsLength := len(records.RackspaceRecord)
	switch recordsLength {
	case 1:
		break
	case 0:
		return nil, fmt.Errorf("No TXT record found for %s", fqdn)
	default:
		return nil, fmt.Errorf("More than 1 TXT record found for %s", fqdn)
	}

	return &records.RackspaceRecord[0], nil
}

// makeRequest is a wrapper function used for making DNS API requests
func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
	url := c.cloudDNSEndpoint + uri
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("X-Auth-Token", c.token)
	req.Header.Set("Content-Type", "application/json")

	client := http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("Error querying DNS API: %v", err)
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted {
		return nil, fmt.Errorf("Request failed for %s %s. Response code: %d", method, url, resp.StatusCode)
	}

	var r json.RawMessage
	err = json.NewDecoder(resp.Body).Decode(&r)
	if err != nil {
		return nil, fmt.Errorf("JSON decode failed for %s %s. Response code: %d", method, url, resp.StatusCode)
	}

	return r, nil
}

// RackspaceRecords is the list of records sent/received from the DNS API
type RackspaceRecords struct {
	RackspaceRecord []RackspaceRecord `json:"records"`
}

// RackspaceRecord represents a Rackspace DNS record
type RackspaceRecord struct {
	Name string `json:"name"`
	Type string `json:"type"`
	Data string `json:"data"`
	TTL  int    `json:"ttl,omitempty"`
	ID   string `json:"id,omitempty"`
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/fatih/color"
	text "github.com/tonnerre/golang-text"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func getPath(p string) string {
	path, _ := filepath.Abs(p)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		log.Fatal(err)
	}
	return path
}

func asKey(p string) string {
	basename := filepath.Base(p)
	return strings.TrimSuffix(basename, filepath.Ext(basename))
}

// LoadInfo loads an Info object from a file path
func LoadInfo(p string) (i Info, err error) {
	data, err := ioutil.ReadFile(p)
	if err != nil {
		log.Fatal("Reading file failed: ", p)
	}

	i = Info{ID: asKey(p)}
	yaml.Unmarshal(data, &i)
	return
}

// Info is the main storage for information. All yaml files map to this.
type Info struct {
	ID      string
	Type    string `yaml:"type"`
	Summary string `yaml:"summary"`
	Body    string `yaml:"body"`
	Command string `yaml:"command"`
	Host    Host   `yaml:"host"`
}

func (i Info) String() string {
	return fmt.Sprintf("I: %s", i.ID)
}

// Execute will figure out the type of the info and execute accordingly
func (i *Info) Execute() {
	if i.Type == "info" {
		i.PrintBody()
	} else if i.Type == "command" {
		i.ExecuteCommand()
	} else if i.Type == "host" {
		i.ExecuteHost()
	}
}

// PrintBody will pretty format the body of the item
func (i *Info) PrintBody() {
	out := text.Wrap(i.Body, 80)
	fmt.Println(out)
}

// ExecuteCommand will execute the command specified by the item.
//
// If the `host` attribute is set, the command will be executed on the host(s)
// specified.
func (i *Info) ExecuteCommand() {
	blue := color.New(color.FgBlue, color.Bold).SprintfFunc()
	magenta := color.New(color.FgMagenta, color.Bold).SprintfFunc()
	yellow := color.New(color.FgYellow, color.Bold).SprintfFunc()
	green := color.New(color.FgGreen, color.Bold).SprintfFunc()

	fmt.Println(
		fmt.Sprintf("%s: %s\nRuns %s on %s\n",
			blue(i.ID),
			magenta(i.Summary),
			yellow(i.Command),
			green(i.Host.getHost()),
		),
	)

	if !ask("Do you want to continue? [y/N] ") {
		fmt.Println("Doing nothing.")
		os.Exit(1)
	}

	if i.Host.hasHost() {
		i.Host.Execute(i.Command)
		return
	}

	sh, _ := exec.LookPath("sh")
	args := []string{sh, "-c", i.Command}

	cmd := exec.Cmd{
		Path:   sh,
		Args:   args,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}
	err := cmd.Run()
	if err != nil {
		log.Fatal("oh noes :(")
	}

}

// ExecuteHost opens a ssh connection to the specified host
func (i *Info) ExecuteHost() {
	i.Host.Execute("") // Called with no args - new ssh session
}
<commit_msg>Add fatal error for unspecified Info types<commit_after>package main

import (
	"fmt"
	"github.com/fatih/color"
	text "github.com/tonnerre/golang-text"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func getPath(p string) string {
	path, _ := filepath.Abs(p)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		log.Fatal(err)
	}
	return path
}

func asKey(p string) string {
	basename := filepath.Base(p)
	return strings.TrimSuffix(basename, filepath.Ext(basename))
}

// LoadInfo loads an Info object from a file path
func LoadInfo(p string) (i Info, err error) {
	data, err := ioutil.ReadFile(p)
	if err != nil {
		log.Fatal("Reading file failed: ", p)
	}

	i = Info{ID: asKey(p)}
	yaml.Unmarshal(data, &i)
	return
}

// Info is the main storage for information. All yaml files map to this.
type Info struct {
	ID      string
	Type    string `yaml:"type"`
	Summary string `yaml:"summary"`
	Body    string `yaml:"body"`
	Command string `yaml:"command"`
	Host    Host   `yaml:"host"`
}

func (i Info) String() string {
	return fmt.Sprintf("I: %s", i.ID)
}

// Execute will figure out the type of the info and execute accordingly
func (i *Info) Execute() {
	if i.Type == "info" {
		i.PrintBody()
	} else if i.Type == "command" {
		i.ExecuteCommand()
	} else if i.Type == "host" {
		i.ExecuteHost()
	} else {
		log.Fatal("Unknown type:", i.Type)
	}
}

// PrintBody will pretty format the body of the item
func (i *Info) PrintBody() {
	out := text.Wrap(i.Body, 80)
	fmt.Println(out)
}

// ExecuteCommand will execute the command specified by the item.
//
// If the `host` attribute is set, the command will be executed on the host(s)
// specified.
func (i *Info) ExecuteCommand() {
	blue := color.New(color.FgBlue, color.Bold).SprintfFunc()
	magenta := color.New(color.FgMagenta, color.Bold).SprintfFunc()
	yellow := color.New(color.FgYellow, color.Bold).SprintfFunc()
	green := color.New(color.FgGreen, color.Bold).SprintfFunc()

	fmt.Println(
		fmt.Sprintf("%s: %s\nRuns %s on %s\n",
			blue(i.ID),
			magenta(i.Summary),
			yellow(i.Command),
			green(i.Host.getHost()),
		),
	)

	if !ask("Do you want to continue? [y/N] ") {
		fmt.Println("Doing nothing.")
		os.Exit(1)
	}

	if i.Host.hasHost() {
		i.Host.Execute(i.Command)
		return
	}

	sh, _ := exec.LookPath("sh")
	args := []string{sh, "-c", i.Command}

	cmd := exec.Cmd{
		Path:   sh,
		Args:   args,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}
	err := cmd.Run()
	if err != nil {
		log.Fatal("oh noes :(")
	}

}

// ExecuteHost opens a ssh connection to the specified host
func (i *Info) ExecuteHost() {
	i.Host.Execute("") // Called with no args - new ssh session
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package internal

import (
	"fmt"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/kube-openapi/pkg/schemaconv"
	"k8s.io/kube-openapi/pkg/util/proto"
	"sigs.k8s.io/structured-merge-diff/typed"
)

// groupVersionKindExtensionKey is the key used to lookup the
// GroupVersionKind value for an object definition from the
// definition's "extensions" map.
const groupVersionKindExtensionKey = "x-kubernetes-group-version-kind"

type gvkParser struct {
	gvks   map[schema.GroupVersionKind]string
	parser typed.Parser
}

func (p *gvkParser) Type(gvk schema.GroupVersionKind) typed.ParseableType {
	typeName, ok := p.gvks[gvk]
	if !ok {
		return nil
	}
	return p.parser.Type(typeName)
}

func newGVKParser(models proto.Models) (*gvkParser, error) {
	typeSchema, err := schemaconv.ToSchema(models)
	if err != nil {
		return nil, fmt.Errorf("failed to convert models to schema: %v", err)
	}
	parser := gvkParser{
		gvks: map[schema.GroupVersionKind]string{},
	}
	parser.parser = typed.Parser{Schema: *typeSchema}
	for _, modelName := range models.ListModels() {
		model := models.LookupModel(modelName)
		if model == nil {
			panic("ListModels returns a model that can't be looked-up.")
		}
		gvkList := parseGroupVersionKind(model)
		for _, gvk := range gvkList {
			if len(gvk.Kind) > 0 {
				parser.gvks[gvk] = modelName
			}
		}
	}
	return &parser, nil
}

// Get and parse GroupVersionKind from the extension. Returns empty if it doesn't have one.
func parseGroupVersionKind(s proto.Schema) []schema.GroupVersionKind {
	extensions := s.GetExtensions()

	gvkListResult := []schema.GroupVersionKind{}

	// Get the extensions
	gvkExtension, ok := extensions[groupVersionKindExtensionKey]
	if !ok {
		return []schema.GroupVersionKind{}
	}

	// gvk extension must be a list of at least 1 element.
	gvkList, ok := gvkExtension.([]interface{})
	if !ok {
		return []schema.GroupVersionKind{}
	}

	for _, gvk := range gvkList {
		// gvk extension list must be a map with group, version, and
		// kind fields
		gvkMap, ok := gvk.(map[interface{}]interface{})
		if !ok {
			continue
		}
		group, ok := gvkMap["group"].(string)
		if !ok {
			continue
		}
		version, ok := gvkMap["version"].(string)
		if !ok {
			continue
		}
		kind, ok := gvkMap["kind"].(string)
		if !ok {
			continue
		}

		gvkListResult = append(gvkListResult, schema.GroupVersionKind{
			Group:   group,
			Version: version,
			Kind:    kind,
		})
	}

	return gvkListResult
}
<commit_msg>Misc fix for feature-serverside-apply<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package internal

import (
	"fmt"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/kube-openapi/pkg/schemaconv"
	"k8s.io/kube-openapi/pkg/util/proto"
	"sigs.k8s.io/structured-merge-diff/typed"
)

// groupVersionKindExtensionKey is the key used to lookup the
// GroupVersionKind value for an object definition from the
// definition's "extensions" map.
const groupVersionKindExtensionKey = "x-kubernetes-group-version-kind"

type gvkParser struct {
	gvks   map[schema.GroupVersionKind]string
	parser typed.Parser
}

func (p *gvkParser) Type(gvk schema.GroupVersionKind) typed.ParseableType {
	typeName, ok := p.gvks[gvk]
	if !ok {
		return nil
	}
	return p.parser.Type(typeName)
}

func newGVKParser(models proto.Models) (*gvkParser, error) {
	typeSchema, err := schemaconv.ToSchema(models)
	if err != nil {
		return nil, fmt.Errorf("failed to convert models to schema: %v", err)
	}
	parser := gvkParser{
		gvks: map[schema.GroupVersionKind]string{},
	}
	parser.parser = typed.Parser{Schema: *typeSchema}
	for _, modelName := range models.ListModels() {
		model := models.LookupModel(modelName)
		if model == nil {
			panic(fmt.Sprintf("ListModels returns a model that can't be looked-up for: %v", modelName))
		}
		gvkList := parseGroupVersionKind(model)
		for _, gvk := range gvkList {
			if len(gvk.Kind) > 0 {
	                        _, ok := parser.gvks[gvk]
                                if ok {
                                    return nil, fmt.Errorf("Duplicate entry for %v", gvk)
                                }
				parser.gvks[gvk] = modelName
			}
		}
	}
	return &parser, nil
}

// Get and parse GroupVersionKind from the extension. Returns empty if it doesn't have one.
func parseGroupVersionKind(s proto.Schema) []schema.GroupVersionKind {
	extensions := s.GetExtensions()

	gvkListResult := []schema.GroupVersionKind{}

	// Get the extensions
	gvkExtension, ok := extensions[groupVersionKindExtensionKey]
	if !ok {
		return []schema.GroupVersionKind{}
	}

	// gvk extension must be a list of at least 1 element.
	gvkList, ok := gvkExtension.([]interface{})
	if !ok {
		return []schema.GroupVersionKind{}
	}

	for _, gvk := range gvkList {
		// gvk extension list must be a map with group, version, and
		// kind fields
		gvkMap, ok := gvk.(map[interface{}]interface{})
		if !ok {
			continue
		}
		group, ok := gvkMap["group"].(string)
		if !ok {
			continue
		}
		version, ok := gvkMap["version"].(string)
		if !ok {
			continue
		}
		kind, ok := gvkMap["kind"].(string)
		if !ok {
			continue
		}

		gvkListResult = append(gvkListResult, schema.GroupVersionKind{
			Group:   group,
			Version: version,
			Kind:    kind,
		})
	}

	return gvkListResult
}
<|endoftext|>


<commit_before>// +build !providerless

/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package fileclient

import (
	"context"
	"fmt"

	"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage"

	"k8s.io/klog/v2"
	azclients "k8s.io/legacy-cloud-providers/azure/clients"
)

// Client implements the azure file client interface
type Client struct {
	fileSharesClient storage.FileSharesClient
}

// New creates a azure file client
func New(config *azclients.ClientConfig) *Client {
	client := storage.NewFileSharesClientWithBaseURI(config.ResourceManagerEndpoint, config.SubscriptionID)
	client.Authorizer = config.Authorizer

	return &Client{
		fileSharesClient: client,
	}
}

// CreateFileShare creates a file share
func (c *Client) CreateFileShare(resourceGroupName, accountName, name string, sizeGiB int) error {
	quota := int32(sizeGiB)
	fileShare := storage.FileShare{
		Name: &name,
		FileShareProperties: &storage.FileShareProperties{
			ShareQuota: &quota,
		},
	}
	_, err := c.fileSharesClient.Create(context.Background(), resourceGroupName, accountName, name, fileShare)

	return err
}

// DeleteFileShare deletes a file share
func (c *Client) DeleteFileShare(resourceGroupName, accountName, name string) error {
	_, err := c.fileSharesClient.Delete(context.Background(), resourceGroupName, accountName, name)

	return err
}

// ResizeFileShare resizes a file share
func (c *Client) ResizeFileShare(resourceGroupName, accountName, name string, sizeGiB int) error {
	quota := int32(sizeGiB)

	share, err := c.fileSharesClient.Get(context.Background(), resourceGroupName, accountName, name)
	if err != nil {
		return fmt.Errorf("failed to get file share(%s), : %v", name, err)
	}
	if *share.FileShareProperties.ShareQuota >= quota {
		klog.Warningf("file share size(%dGi) is already greater or equal than requested size(%dGi), accountName: %s, shareName: %s",
			share.FileShareProperties.ShareQuota, sizeGiB, accountName, name)
		return nil

	}

	share.FileShareProperties.ShareQuota = &quota
	_, err = c.fileSharesClient.Update(context.Background(), resourceGroupName, accountName, name, share)
	if err != nil {
		return fmt.Errorf("failed to update quota on file share(%s), err: %v", name, err)
	}

	klog.V(4).Infof("resize file share completed, resourceGroupName(%s), accountName: %s, shareName: %s, sizeGiB: %d", resourceGroupName, accountName, name, sizeGiB)

	return nil
}

// GetFileShare gets a file share
func (c *Client) GetFileShare(resourceGroupName, accountName, name string) (storage.FileShare, error) {
	return c.fileSharesClient.Get(context.Background(), resourceGroupName, accountName, name)
}
<commit_msg>chore: fix build failure due to sdk upgrade<commit_after>// +build !providerless

/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package fileclient

import (
	"context"
	"fmt"

	"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage"

	"k8s.io/klog/v2"
	azclients "k8s.io/legacy-cloud-providers/azure/clients"
)

// Client implements the azure file client interface
type Client struct {
	fileSharesClient storage.FileSharesClient
}

// New creates a azure file client
func New(config *azclients.ClientConfig) *Client {
	client := storage.NewFileSharesClientWithBaseURI(config.ResourceManagerEndpoint, config.SubscriptionID)
	client.Authorizer = config.Authorizer

	return &Client{
		fileSharesClient: client,
	}
}

// CreateFileShare creates a file share
func (c *Client) CreateFileShare(resourceGroupName, accountName, name string, sizeGiB int) error {
	quota := int32(sizeGiB)
	fileShare := storage.FileShare{
		Name: &name,
		FileShareProperties: &storage.FileShareProperties{
			ShareQuota: &quota,
		},
	}
	_, err := c.fileSharesClient.Create(context.Background(), resourceGroupName, accountName, name, fileShare)

	return err
}

// DeleteFileShare deletes a file share
func (c *Client) DeleteFileShare(resourceGroupName, accountName, name string) error {
	_, err := c.fileSharesClient.Delete(context.Background(), resourceGroupName, accountName, name)

	return err
}

// ResizeFileShare resizes a file share
func (c *Client) ResizeFileShare(resourceGroupName, accountName, name string, sizeGiB int) error {
	quota := int32(sizeGiB)

	share, err := c.fileSharesClient.Get(context.Background(), resourceGroupName, accountName, name, storage.Stats)
	if err != nil {
		return fmt.Errorf("failed to get file share(%s), : %v", name, err)
	}
	if *share.FileShareProperties.ShareQuota >= quota {
		klog.Warningf("file share size(%dGi) is already greater or equal than requested size(%dGi), accountName: %s, shareName: %s",
			share.FileShareProperties.ShareQuota, sizeGiB, accountName, name)
		return nil

	}

	share.FileShareProperties.ShareQuota = &quota
	_, err = c.fileSharesClient.Update(context.Background(), resourceGroupName, accountName, name, share)
	if err != nil {
		return fmt.Errorf("failed to update quota on file share(%s), err: %v", name, err)
	}

	klog.V(4).Infof("resize file share completed, resourceGroupName(%s), accountName: %s, shareName: %s, sizeGiB: %d", resourceGroupName, accountName, name, sizeGiB)

	return nil
}

// GetFileShare gets a file share
func (c *Client) GetFileShare(resourceGroupName, accountName, name string) (storage.FileShare, error) {
	return c.fileSharesClient.Get(context.Background(), resourceGroupName, accountName, name, storage.Stats)
}
<|endoftext|>


<commit_before>// +build !android

package prettytest

import (
	"flag"
	"regexp"
)

var (
	testToRun = flag.String("pt.run", "", "[prettytest] regular expression that filters tests and examples to run")
)

func filterMethod(name string) bool {
	ok, _ := regexp.MatchString(*testToRun, name)
	return ok
}

func init() {
	flag.Parse()
}
<commit_msg>Remove init for Go 1.13 compatibility<commit_after>// +build !android

package prettytest

import (
	"flag"
	"regexp"
)

var (
	testToRun = flag.String("pt.run", "", "[prettytest] regular expression that filters tests and examples to run")
)

func filterMethod(name string) bool {
	ok, _ := regexp.MatchString(*testToRun, name)
	return ok
}
<|endoftext|>


<commit_before>package dbf

const (
	_URL_GET_VENDOR_INFO    = "vendor/getinfo"
	_URL_GET_VENDOR         = "vendor/get"
	_URL_UPDATE_VENDOR_INFO = "vendor/updateinfo"
	_URL_UPDATE_VENDOR      = "vendor/update"
	_URL_GET_TASK           = "task/get"
	_URL_GET_CRACK_INFO     = "crack/info"
	_URL_GET_CRACK_DEP      = "crack/dep"
	_URL_SEND_RESULT        = "task/result"
)
<commit_msg>No need to get vendor info<commit_after>package dbf

const (
	_URL_GET_VENDOR     = "vendor/get"
	_URL_UPDATE_VENDOR  = "vendor/update"
	_URL_GET_TASK       = "task/get"
	_URL_GET_CRACK_INFO = "crack/info"
	_URL_GET_CRACK_DEP  = "crack/dep"
	_URL_SEND_RESULT    = "task/result"
)
<|endoftext|>


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

package render

import (
	"bytes"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/googlecodelabs/tools/claat/nodes"
)

func TestHTMLEnv(t *testing.T) {
	one := nodes.NewTextNode("one ")
	one.MutateEnv([]string{"one"})
	two := nodes.NewTextNode("two ")
	two.MutateEnv([]string{"two"})
	three := nodes.NewTextNode("three ")
	three.MutateEnv([]string{"one", "three"})

	tests := []struct {
		env    string
		output string
	}{
		{"", "one two three "},
		{"one", "one three "},
		{"two", "two "},
		{"three", "three "},
		{"four", ""},
	}
	for i, test := range tests {
		var ctx Context
		ctx.Env = test.env
		h, err := HTML(ctx, one, two, three)
		if err != nil {
			t.Errorf("%d: %v", i, err)
			continue
		}
		if v := string(h); v != test.output {
			t.Errorf("%d: v = %q; want %q", i, v, test.output)
		}
	}
}

// TODO: test HTML
// TODO: test writeHTML
// TODO: test ReplaceDoubleCurlyBracketsWithEntity
// TODO: test matchEnv
// TODO: test write
// TODO: test writeString
// TODO: test writeFmt
// TODO: test escape
// TODO: test writeEscape
// TODO: test text
// TODO: test image
// TODO: test url
// TODO: test button
// TODO: test code
// TODO: test list
// TODO: test onlyImages
// TODO: test itemsList
// TODO: test grid
// TODO: test infobox
// TODO: test survey
// TODO: test header

func TestYouTube(t *testing.T) {
	tests := []struct {
		name   string
		inNode *nodes.YouTubeNode
		out    string
	}{
		{
			name:   "NonEmpty",
			inNode: nodes.NewYouTubeNode("Mlk888FiI8A"),
			out:    `<iframe class="youtube-video" src="https://www.youtube.com/embed/Mlk888FiI8A?rel=0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`,
		},
		{
			name:   "Empty",
			inNode: nodes.NewYouTubeNode(""),
			out:    `<iframe class="youtube-video" src="https://www.youtube.com/embed/?rel=0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`,
		},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			outBuffer := &bytes.Buffer{}
			hw := &htmlWriter{w: outBuffer}
			hw.youtube(tc.inNode)
			out := outBuffer.String()
			if diff := cmp.Diff(tc.out, out); diff != "" {
				t.Errorf("hw.youtube(%+v) got diff (-want +got):\n%s", tc.inNode, diff)
			}
		})
	}
}

func TestIframe(t *testing.T) {
	tests := []struct {
		name   string
		inNode *nodes.IframeNode
		out    string
	}{
		{
			name:   "SomeText",
			inNode: nodes.NewIframeNode("maps.google.com"),
			out:    `<iframe class="embedded-iframe" src="maps.google.com"></iframe>`,
		},
		{
			name:   "Escape",
			inNode: nodes.NewIframeNode("ma ps.google.com"),
			out:    `<iframe class="embedded-iframe" src="ma ps.google.com"></iframe>`,
		},
		{
			name:   "Empty",
			inNode: nodes.NewIframeNode(""),
			out:    `<iframe class="embedded-iframe" src=""></iframe>`,
		},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			outBuffer := &bytes.Buffer{}
			hw := &htmlWriter{w: outBuffer}
			hw.iframe(tc.inNode)
			out := outBuffer.String()
			if diff := cmp.Diff(tc.out, out); diff != "" {
				t.Errorf("hw.iframe(%+v) got diff (-want +got):\n%s", tc.inNode, diff)
			}
		})
	}
}
<commit_msg>Add tests for HTML header rendering.<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package render

import (
	"bytes"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/googlecodelabs/tools/claat/nodes"
)

func TestHTMLEnv(t *testing.T) {
	one := nodes.NewTextNode("one ")
	one.MutateEnv([]string{"one"})
	two := nodes.NewTextNode("two ")
	two.MutateEnv([]string{"two"})
	three := nodes.NewTextNode("three ")
	three.MutateEnv([]string{"one", "three"})

	tests := []struct {
		env    string
		output string
	}{
		{"", "one two three "},
		{"one", "one three "},
		{"two", "two "},
		{"three", "three "},
		{"four", ""},
	}
	for i, test := range tests {
		var ctx Context
		ctx.Env = test.env
		h, err := HTML(ctx, one, two, three)
		if err != nil {
			t.Errorf("%d: %v", i, err)
			continue
		}
		if v := string(h); v != test.output {
			t.Errorf("%d: v = %q; want %q", i, v, test.output)
		}
	}
}

// TODO: test HTML
// TODO: test writeHTML
// TODO: test ReplaceDoubleCurlyBracketsWithEntity
// TODO: test matchEnv
// TODO: test write
// TODO: test writeString
// TODO: test writeFmt
// TODO: test escape
// TODO: test writeEscape
// TODO: test text
// TODO: test image
// TODO: test url
// TODO: test button
// TODO: test code
// TODO: test list
// TODO: test onlyImages
// TODO: test itemsList
// TODO: test grid
// TODO: test infobox
// TODO: test survey

func TestHeader(t *testing.T) {
	a1 := nodes.NewTextNode("foo")
	a1.Italic = true
	a2 := nodes.NewTextNode("bar")
	a3 := nodes.NewTextNode("baz")
	a3.Code = true

	tests := []struct {
		name   string
		inNode *nodes.HeaderNode
		out    string
	}{
		{
			name:   "SimpleH1",
			inNode: nodes.NewHeaderNode(1, nodes.NewTextNode("foobar")),
			out:    "
foobar
",
		},
		{
			name:   "LevelOutOfRange",
			inNode: nodes.NewHeaderNode(100, nodes.NewTextNode("foobar")),
			out:    "<h100 is-upgraded>foobar</h100>",
		},
		{
			name:   "EmptyContent",
			inNode: nodes.NewHeaderNode(2),
			out:    "

",
		},
		{
			name:   "StyledText",
			inNode: nodes.NewHeaderNode(3, a1, a2, a3),
			out:    "
foobarbaz
",
		},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			outBuffer := &bytes.Buffer{}
			hw := &htmlWriter{w: outBuffer}
			hw.header(tc.inNode)
			out := outBuffer.String()
			if diff := cmp.Diff(tc.out, out); diff != "" {
				t.Errorf("hw.header(%+v) got diff (-want +got):\n%s", tc.inNode, diff)
			}
		})
	}
}

func TestYouTube(t *testing.T) {
	tests := []struct {
		name   string
		inNode *nodes.YouTubeNode
		out    string
	}{
		{
			name:   "NonEmpty",
			inNode: nodes.NewYouTubeNode("Mlk888FiI8A"),
			out:    `<iframe class="youtube-video" src="https://www.youtube.com/embed/Mlk888FiI8A?rel=0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`,
		},
		{
			name:   "Empty",
			inNode: nodes.NewYouTubeNode(""),
			out:    `<iframe class="youtube-video" src="https://www.youtube.com/embed/?rel=0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`,
		},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			outBuffer := &bytes.Buffer{}
			hw := &htmlWriter{w: outBuffer}
			hw.youtube(tc.inNode)
			out := outBuffer.String()
			if diff := cmp.Diff(tc.out, out); diff != "" {
				t.Errorf("hw.youtube(%+v) got diff (-want +got):\n%s", tc.inNode, diff)
			}
		})
	}
}

func TestIframe(t *testing.T) {
	tests := []struct {
		name   string
		inNode *nodes.IframeNode
		out    string
	}{
		{
			name:   "SomeText",
			inNode: nodes.NewIframeNode("maps.google.com"),
			out:    `<iframe class="embedded-iframe" src="maps.google.com"></iframe>`,
		},
		{
			name:   "Escape",
			inNode: nodes.NewIframeNode("ma ps.google.com"),
			out:    `<iframe class="embedded-iframe" src="ma ps.google.com"></iframe>`,
		},
		{
			name:   "Empty",
			inNode: nodes.NewIframeNode(""),
			out:    `<iframe class="embedded-iframe" src=""></iframe>`,
		},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			outBuffer := &bytes.Buffer{}
			hw := &htmlWriter{w: outBuffer}
			hw.iframe(tc.inNode)
			out := outBuffer.String()
			if diff := cmp.Diff(tc.out, out); diff != "" {
				t.Errorf("hw.iframe(%+v) got diff (-want +got):\n%s", tc.inNode, diff)
			}
		})
	}
}
<|endoftext|>


<commit_before>package pipeline

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

	"encoding/json"

	"github.com/AcalephStorage/kontinuous/kube"
	"github.com/Sirupsen/logrus"
)

// CreateJob creates a kubernetes Job for the given build information
func CreateJob(definition *Definition, jobInfo *JobBuildInfo) (j *kube.Job, err error) {

	newJob, _ := build(definition, jobInfo)

	err = deployJob(newJob)
	if err != nil {
		logrus.WithError(err).Errorln("Unable to Create Job")
		return nil, err
	}

	return newJob, nil
}

func GetJobBuildInfo(jobInfo []byte) (payload *JobBuildInfo, err error) {

	if len(jobInfo) == 0 {
		return nil, errors.New("Empty JSON String")
	}

	if err = json.Unmarshal(jobInfo, &payload); err != nil {
		return nil, err
	}

	return payload, nil
}

func build(definition *Definition, jobInfo *JobBuildInfo) (j *kube.Job, err error) {

	namespace := getNamespace(definition)
	name := fmt.Sprintf("%s-%s-%s", jobInfo.PipelineUUID, jobInfo.Build, jobInfo.Stage)
	j = kube.NewJob(name, namespace)

	addJobDetail(j, definition, jobInfo)
	addSpecDetails(j, definition, jobInfo)
	return j, nil

}

func addJobDetail(j *kube.Job, definition *Definition, jobInfo *JobBuildInfo) {

	selectors := map[string]string{
		"pipeline": jobInfo.PipelineUUID,
		"build":    jobInfo.Build,
		"stage":    jobInfo.Stage,
	}

	for key, value := range selectors {
		j.AddSelectorMatchLabel(key, value)
	}
}

func addSpecDetails(j *kube.Job, definitions *Definition, jobInfo *JobBuildInfo) {

	stage := getCurrentStage(definitions, jobInfo)

	source := j.AddPodVolume("kontinuous-source", "/kontinuous/src")
	status := j.AddPodVolume("kontinuous-status", "/kontinuous/status")
	docker := j.AddPodVolume("kontinuous-docker", "/var/run/docker.sock")
	secrets := getSecrets(definitions.Spec.Template.Secrets, getNamespace(definitions))

	agentContainer := createAgentContainer(definitions, jobInfo)
	agentContainer.AddVolumeMountPoint(source, "/kontinuous/src", false)
	agentContainer.AddVolumeMountPoint(status, "/kontinuous/status", false)
	agentContainer.AddVolumeMountPoint(docker, "/var/run/docker.sock", false)
	setContainerEnv(agentContainer, secrets)
	addJobContainer(j, agentContainer)

	switch stage.Type {
	case "docker_build":

		dockerContainer := createDockerContainer(stage, jobInfo, "BUILD")
		dockerContainer.AddVolumeMountPoint(source, "/kontinuous/src", false)
		dockerContainer.AddVolumeMountPoint(status, "/kontinuous/status", false)
		dockerContainer.AddVolumeMountPoint(docker, "/var/run/docker.sock", false)
		setContainerEnv(dockerContainer, secrets)
		addJobContainer(j, dockerContainer)

	case "docker_publish":
		dockerContainer := createDockerContainer(stage, jobInfo, "PUBLISH")
		dockerContainer.AddVolumeMountPoint(source, "/kontinuous/src", false)
		dockerContainer.AddVolumeMountPoint(status, "/kontinuous/status", false)
		dockerContainer.AddVolumeMountPoint(docker, "/var/run/docker.sock", false)
		setContainerEnv(dockerContainer, secrets)
		addJobContainer(j, dockerContainer)

	case "command":
		commandContainer := createCommandContainer(stage, jobInfo)
		commandContainer.AddVolumeMountPoint(source, "/kontinuous/src", false)
		commandContainer.AddVolumeMountPoint(status, "/kontinuous/status", false)
		commandContainer.AddVolumeMountPoint(docker, "/var/run/docker.sock", false)
		setContainerEnv(commandContainer, secrets)
		addJobContainer(j, commandContainer)
	}

}

func getCurrentStage(definitions *Definition, jobInfo *JobBuildInfo) (stage *Stage) {

	index, _ := strconv.Atoi(jobInfo.Stage)

	if currentIndex := index - 1; 0 <= currentIndex && currentIndex < len(definitions.Spec.Template.Stages) {
		return &definitions.Spec.Template.Stages[currentIndex]
	}

	return &Stage{}
}

func createAgentContainer(definitions *Definition, jobInfo *JobBuildInfo) *kube.Container {

	container := createJobContainer("kontinuous-agent", "quay.io/acaleph/kontinuous-agent:latest")
	envVars := map[string]string{
		"REQUIRE_SOURCE_CODE": "TRUE",
		"GIT_COMMIT":          jobInfo.Commit,
		"GIT_USER":            jobInfo.User,
		"GIT_REPO":            jobInfo.Repo,
		"GIT_OWNER":           jobInfo.Owner,
		"PIPELINE_ID":         jobInfo.PipelineUUID,
		"BUILD_ID":            jobInfo.Build,
		"STAGE_ID":            jobInfo.Stage,
		"S3_URL":              os.Getenv("S3_URL"),
		"S3_ACCESS_KEY":       os.Getenv("S3_ACCESS_KEY"),
		"S3_SECRET_KEY":       os.Getenv("S3_SECRET_KEY"),
		"KONTINUOUS_URL":      os.Getenv("KONTINUOUS_URL"),
		"NAMESPACE":           getNamespace(definitions),
		"ARTIFACT_URL":        "",
	}

	setContainerEnv(container, envVars)
	return container
}

func createDockerContainer(stage *Stage, jobInfo *JobBuildInfo, mode string) *kube.Container {
	imageName := fmt.Sprintf("%s-%s", jobInfo.PipelineUUID, jobInfo.Build)
	container := createJobContainer("docker-agent", "quay.io/acaleph/docker-agent:latest")

	envVar := map[string]string{
		"INTERNAL_REGISTRY":   os.Getenv("INTERNAL_REGISTRY"),
		"DOCKERFILE_NAME":     "Dockerfile",
		"DOCKERFILE_PATH":     ".",
		"REQUIRE_CREDENTIALS": "TRUE",
		"IMAGE_NAME":          imageName,
		"MODE":                mode,
		"PIPELINE_ID":         jobInfo.PipelineUUID,
		"BUILD_ID":            jobInfo.Build,
		"STAGE_ID":            jobInfo.Stage,
		"IMAGE_TAG":           jobInfo.Commit,
		"BRANCH":              jobInfo.Branch,
	}

	for stageEnvKey, stageEnvValue := range stage.Params {
		envVar[strings.ToUpper(stageEnvKey)] = stageEnvValue.(string)
	}

	setContainerEnv(container, envVar)
	return container
}

func createCommandContainer(stage *Stage, jobInfo *JobBuildInfo) *kube.Container {

	containerName := fmt.Sprintf("%s-%s", jobInfo.PipelineUUID, jobInfo.Build)
	cmdImage := fmt.Sprintf("%s/%s:%s", os.Getenv("INTERNAL_REGISTRY"), containerName, jobInfo.Commit)
	imageName := "quay.io/acaleph/command-agent:latest"
	container := createJobContainer(containerName, imageName)
	container.Image = imageName
	container.AddEnv("IMAGE", cmdImage)
	container.WorkingDir = fmt.Sprintf("/kontinuous/src/%v/%v/%v", jobInfo.PipelineUUID, jobInfo.Build, stage.Index)

	for paramKey, paramValue := range stage.Params {

		switch strings.ToUpper(paramKey) {
		case "COMMAND":
			commands := paramValue.([]interface{})
			stringCommand := make([]string, len(commands))
			for i, c := range commands {
				stringCommand[i] = c.(string)
			}
			container.AddEnv("COMMAND", strings.Join(stringCommand, " "))
		case "ARGS":
			args := paramValue.([]interface{})
			stringArg := make([]string, len(args))
			for i, a := range args {
				stringArg[i] = a.(string)
			}
			container.SetArgs(stringArg...)
		case "IMAGE":
			container.AddEnv("IMAGE", paramValue.(string))
		case "WORKING_DIR":
			container.WorkingDir = paramValue.(string)
			container.AddEnv("WORKING_DIR", paramValue.(string))
		case "DEPENDENCIES":
			dependencies := paramValue.([]interface{})
			stringDep := make([]string, len(dependencies))
			for i, d := range dependencies {
				stringDep[i] = d.(string)
			}
			container.AddEnv("DEPENDENCIES", strings.Join(stringDep, " "))
		default:
			container.AddEnv(strings.ToUpper(paramKey), paramValue.(string))
		}
	}

	envVars := map[string]string{
		"INTERNAL_REGISTRY": os.Getenv("INTERNAL_REGISTRY"),
		"PIPELINE_ID":       jobInfo.PipelineUUID,
		"BUILD_ID":          jobInfo.Build,
		"STAGE_ID":          jobInfo.Stage,
		"COMMIT":            jobInfo.Commit,
		"BRANCH":            jobInfo.Branch,
	}
	setContainerEnv(container, envVars)

	keySlice := make([]string, 0)
	for _, env := range container.Env {
		keySlice = append(keySlice, env.Name)
	}
	keys := strings.Join(keySlice, " ")
	container.AddEnv("ENV_KEYS", keys)

	return container
}

func deployJob(j *kube.Job) error {
	jobClient, _ := kube.NewClient("https://kubernetes.default")
	return jobClient.CreateJob(j)
}

func setContainerEnv(container *kube.Container, envVars map[string]string) {
	for key, value := range envVars {
		container.AddEnv(key, value)
	}

}

func getSecrets(pipelineSecrets []string, namespace string) map[string]string {
	kubeClient, _ := kube.NewClient("https://kubernetes.default")
	secrets := make(map[string]string)

	for _, secret := range pipelineSecrets {
		secretEnv, err := kubeClient.GetSecret(namespace, secret)
		if err != nil {
			logrus.Printf("Unable to get secret %s", secret)
			continue
		}
		logrus.Printf("Secret retrieved %s", secretEnv)
		for key, value := range secretEnv {
			secrets[key] = value
		}
	}
	return secrets
}

func createJobContainer(name string, image string) *kube.Container {
	container := &kube.Container{
		Name:            name,
		Image:           image,
		ImagePullPolicy: "Always",
	}
	return container
}

func addJobContainer(j *kube.Job, container *kube.Container) {
	j.Spec.Template.Spec.Containers = append(j.Spec.Template.Spec.Containers, container)
}

func getNamespace(definition *Definition) string {
	if definition.Metadata["namespace"] == nil {
		return "default"
	}
	return definition.Metadata["namespace"].(string)
}
<commit_msg>updated command-agent container name<commit_after>package pipeline

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

	"encoding/json"

	"github.com/AcalephStorage/kontinuous/kube"
	"github.com/Sirupsen/logrus"
)

// CreateJob creates a kubernetes Job for the given build information
func CreateJob(definition *Definition, jobInfo *JobBuildInfo) (j *kube.Job, err error) {

	newJob, _ := build(definition, jobInfo)

	err = deployJob(newJob)
	if err != nil {
		logrus.WithError(err).Errorln("Unable to Create Job")
		return nil, err
	}

	return newJob, nil
}

func GetJobBuildInfo(jobInfo []byte) (payload *JobBuildInfo, err error) {

	if len(jobInfo) == 0 {
		return nil, errors.New("Empty JSON String")
	}

	if err = json.Unmarshal(jobInfo, &payload); err != nil {
		return nil, err
	}

	return payload, nil
}

func build(definition *Definition, jobInfo *JobBuildInfo) (j *kube.Job, err error) {

	namespace := getNamespace(definition)
	name := fmt.Sprintf("%s-%s-%s", jobInfo.PipelineUUID, jobInfo.Build, jobInfo.Stage)
	j = kube.NewJob(name, namespace)

	addJobDetail(j, definition, jobInfo)
	addSpecDetails(j, definition, jobInfo)
	return j, nil

}

func addJobDetail(j *kube.Job, definition *Definition, jobInfo *JobBuildInfo) {

	selectors := map[string]string{
		"pipeline": jobInfo.PipelineUUID,
		"build":    jobInfo.Build,
		"stage":    jobInfo.Stage,
	}

	for key, value := range selectors {
		j.AddSelectorMatchLabel(key, value)
	}
}

func addSpecDetails(j *kube.Job, definitions *Definition, jobInfo *JobBuildInfo) {

	stage := getCurrentStage(definitions, jobInfo)

	source := j.AddPodVolume("kontinuous-source", "/kontinuous/src")
	status := j.AddPodVolume("kontinuous-status", "/kontinuous/status")
	docker := j.AddPodVolume("kontinuous-docker", "/var/run/docker.sock")
	secrets := getSecrets(definitions.Spec.Template.Secrets, getNamespace(definitions))

	agentContainer := createAgentContainer(definitions, jobInfo)
	agentContainer.AddVolumeMountPoint(source, "/kontinuous/src", false)
	agentContainer.AddVolumeMountPoint(status, "/kontinuous/status", false)
	agentContainer.AddVolumeMountPoint(docker, "/var/run/docker.sock", false)
	setContainerEnv(agentContainer, secrets)
	addJobContainer(j, agentContainer)

	switch stage.Type {
	case "docker_build":

		dockerContainer := createDockerContainer(stage, jobInfo, "BUILD")
		dockerContainer.AddVolumeMountPoint(source, "/kontinuous/src", false)
		dockerContainer.AddVolumeMountPoint(status, "/kontinuous/status", false)
		dockerContainer.AddVolumeMountPoint(docker, "/var/run/docker.sock", false)
		setContainerEnv(dockerContainer, secrets)
		addJobContainer(j, dockerContainer)

	case "docker_publish":
		dockerContainer := createDockerContainer(stage, jobInfo, "PUBLISH")
		dockerContainer.AddVolumeMountPoint(source, "/kontinuous/src", false)
		dockerContainer.AddVolumeMountPoint(status, "/kontinuous/status", false)
		dockerContainer.AddVolumeMountPoint(docker, "/var/run/docker.sock", false)
		setContainerEnv(dockerContainer, secrets)
		addJobContainer(j, dockerContainer)

	case "command":
		commandContainer := createCommandContainer(stage, jobInfo)
		commandContainer.AddVolumeMountPoint(source, "/kontinuous/src", false)
		commandContainer.AddVolumeMountPoint(status, "/kontinuous/status", false)
		commandContainer.AddVolumeMountPoint(docker, "/var/run/docker.sock", false)
		setContainerEnv(commandContainer, secrets)
		addJobContainer(j, commandContainer)
	}

}

func getCurrentStage(definitions *Definition, jobInfo *JobBuildInfo) (stage *Stage) {

	index, _ := strconv.Atoi(jobInfo.Stage)

	if currentIndex := index - 1; 0 <= currentIndex && currentIndex < len(definitions.Spec.Template.Stages) {
		return &definitions.Spec.Template.Stages[currentIndex]
	}

	return &Stage{}
}

func createAgentContainer(definitions *Definition, jobInfo *JobBuildInfo) *kube.Container {

	container := createJobContainer("kontinuous-agent", "quay.io/acaleph/kontinuous-agent:latest")
	envVars := map[string]string{
		"REQUIRE_SOURCE_CODE": "TRUE",
		"GIT_COMMIT":          jobInfo.Commit,
		"GIT_USER":            jobInfo.User,
		"GIT_REPO":            jobInfo.Repo,
		"GIT_OWNER":           jobInfo.Owner,
		"PIPELINE_ID":         jobInfo.PipelineUUID,
		"BUILD_ID":            jobInfo.Build,
		"STAGE_ID":            jobInfo.Stage,
		"S3_URL":              os.Getenv("S3_URL"),
		"S3_ACCESS_KEY":       os.Getenv("S3_ACCESS_KEY"),
		"S3_SECRET_KEY":       os.Getenv("S3_SECRET_KEY"),
		"KONTINUOUS_URL":      os.Getenv("KONTINUOUS_URL"),
		"NAMESPACE":           getNamespace(definitions),
		"ARTIFACT_URL":        "",
	}

	setContainerEnv(container, envVars)
	return container
}

func createDockerContainer(stage *Stage, jobInfo *JobBuildInfo, mode string) *kube.Container {
	imageName := fmt.Sprintf("%s-%s", jobInfo.PipelineUUID, jobInfo.Build)
	container := createJobContainer("docker-agent", "quay.io/acaleph/docker-agent:latest")

	envVar := map[string]string{
		"INTERNAL_REGISTRY":   os.Getenv("INTERNAL_REGISTRY"),
		"DOCKERFILE_NAME":     "Dockerfile",
		"DOCKERFILE_PATH":     ".",
		"REQUIRE_CREDENTIALS": "TRUE",
		"IMAGE_NAME":          imageName,
		"MODE":                mode,
		"PIPELINE_ID":         jobInfo.PipelineUUID,
		"BUILD_ID":            jobInfo.Build,
		"STAGE_ID":            jobInfo.Stage,
		"IMAGE_TAG":           jobInfo.Commit,
		"BRANCH":              jobInfo.Branch,
	}

	for stageEnvKey, stageEnvValue := range stage.Params {
		envVar[strings.ToUpper(stageEnvKey)] = stageEnvValue.(string)
	}

	setContainerEnv(container, envVar)
	return container
}

func createCommandContainer(stage *Stage, jobInfo *JobBuildInfo) *kube.Container {

	containerName := "command-agent"
	cmdImage := fmt.Sprintf("%s/%s:%s", os.Getenv("INTERNAL_REGISTRY"), containerName, jobInfo.Commit)
	imageName := "quay.io/acaleph/command-agent:latest"
	container := createJobContainer(containerName, imageName)
	container.Image = imageName
	container.AddEnv("IMAGE", cmdImage)
	container.WorkingDir = fmt.Sprintf("/kontinuous/src/%s/%s/%d", jobInfo.PipelineUUID, jobInfo.Build, stage.Index)

	for paramKey, paramValue := range stage.Params {

		switch strings.ToUpper(paramKey) {
		case "COMMAND":
			commands := paramValue.([]interface{})
			stringCommand := make([]string, len(commands))
			for i, c := range commands {
				stringCommand[i] = c.(string)
			}
			container.AddEnv("COMMAND", strings.Join(stringCommand, " "))
		case "ARGS":
			args := paramValue.([]interface{})
			stringArg := make([]string, len(args))
			for i, a := range args {
				stringArg[i] = a.(string)
			}
			container.SetArgs(stringArg...)
		case "IMAGE":
			container.AddEnv("IMAGE", paramValue.(string))
		case "WORKING_DIR":
			container.WorkingDir = paramValue.(string)
			container.AddEnv("WORKING_DIR", paramValue.(string))
		case "DEPENDENCIES":
			dependencies := paramValue.([]interface{})
			stringDep := make([]string, len(dependencies))
			for i, d := range dependencies {
				stringDep[i] = d.(string)
			}
			container.AddEnv("DEPENDENCIES", strings.Join(stringDep, " "))
		default:
			container.AddEnv(strings.ToUpper(paramKey), paramValue.(string))
		}
	}

	envVars := map[string]string{
		"INTERNAL_REGISTRY": os.Getenv("INTERNAL_REGISTRY"),
		"PIPELINE_ID":       jobInfo.PipelineUUID,
		"BUILD_ID":          jobInfo.Build,
		"STAGE_ID":          jobInfo.Stage,
		"COMMIT":            jobInfo.Commit,
		"BRANCH":            jobInfo.Branch,
	}
	setContainerEnv(container, envVars)

	keySlice := make([]string, 0)
	for _, env := range container.Env {
		keySlice = append(keySlice, env.Name)
	}
	keys := strings.Join(keySlice, " ")
	container.AddEnv("ENV_KEYS", keys)

	return container
}

func deployJob(j *kube.Job) error {
	jobClient, _ := kube.NewClient("https://kubernetes.default")
	return jobClient.CreateJob(j)
}

func setContainerEnv(container *kube.Container, envVars map[string]string) {
	for key, value := range envVars {
		container.AddEnv(key, value)
	}

}

func getSecrets(pipelineSecrets []string, namespace string) map[string]string {
	kubeClient, _ := kube.NewClient("https://kubernetes.default")
	secrets := make(map[string]string)

	for _, secret := range pipelineSecrets {
		secretEnv, err := kubeClient.GetSecret(namespace, secret)
		if err != nil {
			logrus.Printf("Unable to get secret %s", secret)
			continue
		}
		logrus.Printf("Secret retrieved %s", secretEnv)
		for key, value := range secretEnv {
			secrets[key] = value
		}
	}
	return secrets
}

func createJobContainer(name string, image string) *kube.Container {
	container := &kube.Container{
		Name:            name,
		Image:           image,
		ImagePullPolicy: "Always",
	}
	return container
}

func addJobContainer(j *kube.Job, container *kube.Container) {
	j.Spec.Template.Spec.Containers = append(j.Spec.Template.Spec.Containers, container)
}

func getNamespace(definition *Definition) string {
	if definition.Metadata["namespace"] == nil {
		return "default"
	}
	return definition.Metadata["namespace"].(string)
}
<|endoftext|>


<commit_before>package layout

import (
	"reflect"
	"testing"

	"github.com/elves/elvish/cli/clitypes"
	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/styled"
)

var bb = ui.NewBufferBuilder

var renderTests = []struct {
	name     string
	renderer clitypes.Renderer
	width    int
	height   int
	wantBuf  *ui.BufferBuilder
}{
	{
		"Label",
		Label{styled.Plain("label")},
		10, 24,
		ui.NewBufferBuilder(10).WritePlain("label"),
	},
}

func TestRender(t *testing.T) {
	for _, test := range renderTests {
		t.Run(test.name, func(t *testing.T) {
			buf := test.renderer.Render(test.width, test.height)
			wantBuf := test.wantBuf.Buffer()
			if !reflect.DeepEqual(buf, wantBuf) {
				t.Errorf("got buf %v, want %v", buf, wantBuf)
			}
		})
	}
}
<commit_msg>cli/layout: Add more unit tests.<commit_after>package layout

import (
	"reflect"
	"testing"

	"github.com/elves/elvish/cli/clitypes"
	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/styled"
)

var bb = ui.NewBufferBuilder

var renderTests = []struct {
	name     string
	renderer clitypes.Renderer
	width    int
	height   int
	wantBuf  *ui.BufferBuilder
}{
	{
		"Label showing all",
		Label{styled.Plain("label")},
		10, 24,
		ui.NewBufferBuilder(10).WritePlain("label"),
	},
	{
		"Label cropping",
		Label{styled.Plain("label")},
		4, 1,
		ui.NewBufferBuilder(4).WritePlain("labe"),
	},
	{
		"CroppedLines showing all",
		CroppedLines{Lines: []styled.Text{
			styled.Plain("line 1"),
			styled.Plain("line 2"),
		}},
		10, 24,
		ui.NewBufferBuilder(10).WritePlain("line 1").
			Newline().WritePlain("line 2"),
	},
	{
		"CroppedLines cropping horizontally",
		CroppedLines{Lines: []styled.Text{
			styled.Plain("line 1"),
			styled.Plain("line 2"),
		}},
		4, 24,
		ui.NewBufferBuilder(4).WritePlain("line").
			Newline().WritePlain("line"),
	},
	{
		"CroppedLines cropping vertically",
		CroppedLines{Lines: []styled.Text{
			styled.Plain("line 1"),
			styled.Plain("line 2"),
			styled.Plain("line 3"),
		}},
		10, 2,
		ui.NewBufferBuilder(10).WritePlain("line 1").
			Newline().WritePlain("line 2"),
	},
}

func TestRender(t *testing.T) {
	for _, test := range renderTests {
		t.Run(test.name, func(t *testing.T) {
			buf := test.renderer.Render(test.width, test.height)
			wantBuf := test.wantBuf.Buffer()
			if !reflect.DeepEqual(buf, wantBuf) {
				t.Errorf("got buf %v, want %v", buf, wantBuf)
			}
		})
	}
}
<|endoftext|>