<commit_before>package plugin_test

import (
	"io/ioutil"
	"net/rpc"
	"os"
	"path/filepath"

	"github.com/cloudfoundry/cli/cf/configuration/config_helpers"
	"github.com/cloudfoundry/cli/cf/configuration/plugin_config"
	cliRpc "github.com/cloudfoundry/cli/plugin/rpc"
	testcmd "github.com/cloudfoundry/cli/testhelpers/commands"
	testreq "github.com/cloudfoundry/cli/testhelpers/requirements"
	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"

	. "github.com/cloudfoundry/cli/cf/commands/plugin"
	"github.com/cloudfoundry/cli/fileutils"
	. "github.com/cloudfoundry/cli/testhelpers/matchers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Uninstall", func() {
	var (
		ui                  *testterm.FakeUI
		requirementsFactory *testreq.FakeReqFactory
		fakePluginRepoDir   string
		pluginDir           string
		pluginConfig        *plugin_config.PluginConfig
	)

	BeforeEach(func() {
		ui = &testterm.FakeUI{}
		requirementsFactory = &testreq.FakeReqFactory{}

		var err error
		fakePluginRepoDir, err = ioutil.TempDir(os.TempDir(), "plugins")
		Expect(err).ToNot(HaveOccurred())

		fixtureDir := filepath.Join("..", "..", "..", "fixtures", "plugins")

		pluginDir = filepath.Join(fakePluginRepoDir, ".cf", "plugins")
		err = os.MkdirAll(pluginDir, 0700)
		Expect(err).NotTo(HaveOccurred())

		fileutils.CopyFile(filepath.Join(pluginDir, "test_1.exe"), filepath.Join(fixtureDir, "test_1.exe"))
		fileutils.CopyFile(filepath.Join(pluginDir, "test_2.exe"), filepath.Join(fixtureDir, "test_2.exe"))

		config_helpers.PluginRepoDir = func() string {
			return fakePluginRepoDir
		}

		pluginConfig = plugin_config.NewPluginConfig(func(err error) { Expect(err).ToNot(HaveOccurred()) })
		pluginConfig.SetPlugin("test_1.exe", plugin_config.PluginMetadata{Location: filepath.Join(pluginDir, "test_1.exe")})
		pluginConfig.SetPlugin("test_2.exe", plugin_config.PluginMetadata{Location: filepath.Join(pluginDir, "test_2.exe")})

	})

	AfterEach(func() {
		os.Remove(fakePluginRepoDir)
	})

	runCommand := func(args ...string) bool {
		//reset rpc registration, each service can only be registered once
		rpc.DefaultServer = rpc.NewServer()
		rpcService, _ := cliRpc.NewRpcService(nil, nil, nil)
		cmd := NewPluginUninstall(ui, pluginConfig, rpcService)
		return testcmd.RunCommand(cmd, args, requirementsFactory)
	}

	Describe("requirements", func() {
		It("fails with usage when not provided a path to the plugin executable", func() {
			Expect(runCommand()).ToNot(HavePassedRequirements())
		})
	})

	Describe("failures", func() {
		It("if plugin name does not exist", func() {
			runCommand("garbage")

			Expect(ui.Outputs).To(ContainSubstrings(
				[]string{"Uninstalling plugin garbage..."},
				[]string{"FAILED"},
				[]string{"Plugin name", "garbage", "does not exist"},
			))
		})
	})

	Describe("success", func() {

		Context("notifying plugin of uninstalling", func() {
			var path2file string

			BeforeEach(func() {
				path2file = filepath.Join(os.TempDir(), "uninstall-test-file-for-test_1.exe")

				_, err := os.Create(path2file)
				Ω(err).ToNot(HaveOccurred())
			})

			AfterEach(func() {
				os.Remove(path2file)
			})

			It("notifies the plugin upon uninstalling", func() {
				_, err := os.Stat(path2file)
				Ω(err).ToNot(HaveOccurred())

				runCommand("test_1.exe")

				_, err = os.Stat(path2file)
				Ω(err).To(HaveOccurred())
				Ω(os.IsNotExist(err)).To(BeTrue())
			})
		})

		It("removes the binary from the <FAKE_HOME_DIR>/.cf/plugins dir", func() {
			_, err := os.Stat(filepath.Join(pluginDir, "test_1.exe"))
			Expect(err).ToNot(HaveOccurred())

			runCommand("test_1.exe")

			_, err = os.Stat(filepath.Join(pluginDir, "test_1.exe"))
			Expect(err).To(HaveOccurred())
			Expect(os.IsNotExist(err)).To(BeTrue())
		})

		It("removes the entry from the config.json", func() {
			plugins := pluginConfig.Plugins()
			Expect(plugins).To(HaveKey("test_1.exe"))

			runCommand("test_1.exe")

			plugins = pluginConfig.Plugins()
			Expect(plugins).NotTo(HaveKey("test_1.exe"))
		})

		It("prints success text", func() {
			runCommand("test_1.exe")

			Expect(ui.Outputs).To(ContainSubstrings(
				[]string{"Uninstalling plugin test_1.exe..."},
				[]string{"OK"},
				[]string{"Plugin", "test_1.exe", "successfully uninstalled."},
			))
		})

	})

})
<commit_msg>closing a file in test<commit_after>package plugin_test

import (
	"io/ioutil"
	"net/rpc"
	"os"
	"path/filepath"

	"github.com/cloudfoundry/cli/cf/configuration/config_helpers"
	"github.com/cloudfoundry/cli/cf/configuration/plugin_config"
	cliRpc "github.com/cloudfoundry/cli/plugin/rpc"
	testcmd "github.com/cloudfoundry/cli/testhelpers/commands"
	testreq "github.com/cloudfoundry/cli/testhelpers/requirements"
	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"

	. "github.com/cloudfoundry/cli/cf/commands/plugin"
	"github.com/cloudfoundry/cli/fileutils"
	. "github.com/cloudfoundry/cli/testhelpers/matchers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Uninstall", func() {
	var (
		ui                  *testterm.FakeUI
		requirementsFactory *testreq.FakeReqFactory
		fakePluginRepoDir   string
		pluginDir           string
		pluginConfig        *plugin_config.PluginConfig
	)

	BeforeEach(func() {
		ui = &testterm.FakeUI{}
		requirementsFactory = &testreq.FakeReqFactory{}

		var err error
		fakePluginRepoDir, err = ioutil.TempDir(os.TempDir(), "plugins")
		Expect(err).ToNot(HaveOccurred())

		fixtureDir := filepath.Join("..", "..", "..", "fixtures", "plugins")

		pluginDir = filepath.Join(fakePluginRepoDir, ".cf", "plugins")
		err = os.MkdirAll(pluginDir, 0700)
		Expect(err).NotTo(HaveOccurred())

		fileutils.CopyFile(filepath.Join(pluginDir, "test_1.exe"), filepath.Join(fixtureDir, "test_1.exe"))
		fileutils.CopyFile(filepath.Join(pluginDir, "test_2.exe"), filepath.Join(fixtureDir, "test_2.exe"))

		config_helpers.PluginRepoDir = func() string {
			return fakePluginRepoDir
		}

		pluginConfig = plugin_config.NewPluginConfig(func(err error) { Expect(err).ToNot(HaveOccurred()) })
		pluginConfig.SetPlugin("test_1.exe", plugin_config.PluginMetadata{Location: filepath.Join(pluginDir, "test_1.exe")})
		pluginConfig.SetPlugin("test_2.exe", plugin_config.PluginMetadata{Location: filepath.Join(pluginDir, "test_2.exe")})

	})

	AfterEach(func() {
		os.Remove(fakePluginRepoDir)
	})

	runCommand := func(args ...string) bool {
		//reset rpc registration, each service can only be registered once
		rpc.DefaultServer = rpc.NewServer()
		rpcService, _ := cliRpc.NewRpcService(nil, nil, nil)
		cmd := NewPluginUninstall(ui, pluginConfig, rpcService)
		return testcmd.RunCommand(cmd, args, requirementsFactory)
	}

	Describe("requirements", func() {
		It("fails with usage when not provided a path to the plugin executable", func() {
			Expect(runCommand()).ToNot(HavePassedRequirements())
		})
	})

	Describe("failures", func() {
		It("if plugin name does not exist", func() {
			runCommand("garbage")

			Expect(ui.Outputs).To(ContainSubstrings(
				[]string{"Uninstalling plugin garbage..."},
				[]string{"FAILED"},
				[]string{"Plugin name", "garbage", "does not exist"},
			))
		})
	})

	Describe("success", func() {

		Context("notifying plugin of uninstalling", func() {
			var path2file string

			BeforeEach(func() {
				path2file = filepath.Join(os.TempDir(), "uninstall-test-file-for-test_1.exe")

				f, err := os.Create(path2file)
				Ω(err).ToNot(HaveOccurred())
				defer f.Close()
			})

			AfterEach(func() {
				os.Remove(path2file)
			})

			It("notifies the plugin upon uninstalling", func() {
				_, err := os.Stat(path2file)
				Ω(err).ToNot(HaveOccurred())

				runCommand("test_1.exe")

				_, err = os.Stat(path2file)
				Ω(err).To(HaveOccurred())
				Ω(os.IsNotExist(err)).To(BeTrue())
			})
		})

		It("removes the binary from the <FAKE_HOME_DIR>/.cf/plugins dir", func() {
			_, err := os.Stat(filepath.Join(pluginDir, "test_1.exe"))
			Expect(err).ToNot(HaveOccurred())

			runCommand("test_1.exe")

			_, err = os.Stat(filepath.Join(pluginDir, "test_1.exe"))
			Expect(err).To(HaveOccurred())
			Expect(os.IsNotExist(err)).To(BeTrue())
		})

		It("removes the entry from the config.json", func() {
			plugins := pluginConfig.Plugins()
			Expect(plugins).To(HaveKey("test_1.exe"))

			runCommand("test_1.exe")

			plugins = pluginConfig.Plugins()
			Expect(plugins).NotTo(HaveKey("test_1.exe"))
		})

		It("prints success text", func() {
			runCommand("test_1.exe")

			Expect(ui.Outputs).To(ContainSubstrings(
				[]string{"Uninstalling plugin test_1.exe..."},
				[]string{"OK"},
				[]string{"Plugin", "test_1.exe", "successfully uninstalled."},
			))
		})

	})

})
<|endoftext|>


<commit_before>/*
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 awsup

import (
	"bytes"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/sts"
	"k8s.io/kops/upup/pkg/fi"
)

type AWSVerifierOptions struct {
	// NodesRoles are the IAM roles that worker nodes are permitted to have.
	NodesRoles []string `json:"nodesRoles"`
}

type awsVerifier struct {
	accountId string
	opt       AWSVerifierOptions

	ec2    *ec2.EC2
	sts    *sts.STS
	client http.Client
}

var _ fi.Verifier = &awsVerifier{}

func NewAWSVerifier(opt *AWSVerifierOptions) (fi.Verifier, error) {
	config := aws.NewConfig().WithCredentialsChainVerboseErrors(true)
	sess, err := session.NewSession(config)
	if err != nil {
		return nil, err
	}

	stsClient := sts.New(sess)
	identity, err := stsClient.GetCallerIdentity(&sts.GetCallerIdentityInput{})
	if err != nil {
		return nil, err
	}

	metadata := ec2metadata.New(sess, config)
	region, err := metadata.Region()
	if err != nil {
		return nil, fmt.Errorf("error querying ec2 metadata service (for region): %v", err)
	}

	ec2Client := ec2.New(sess, config.WithRegion(region))

	return &awsVerifier{
		accountId: aws.StringValue(identity.Account),
		opt:       *opt,
		ec2:       ec2Client,
		sts:       stsClient,
		client: http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
				DialContext: (&net.Dialer{
					Timeout: 30 * time.Second,
				}).DialContext,
				ForceAttemptHTTP2:     true,
				DisableKeepAlives:     true,
				MaxIdleConnsPerHost:   -1,
				TLSHandshakeTimeout:   10 * time.Second,
				ExpectContinueTimeout: 1 * time.Second,
			},
			CheckRedirect: func(req *http.Request, via []*http.Request) error {
				return http.ErrUseLastResponse
			},
		},
	}, nil
}

type GetCallerIdentityResponse struct {
	XMLName                 xml.Name                  `xml:"GetCallerIdentityResponse"`
	GetCallerIdentityResult []GetCallerIdentityResult `xml:"GetCallerIdentityResult"`
	ResponseMetadata        []ResponseMetadata        `xml:"ResponseMetadata"`
}

type GetCallerIdentityResult struct {
	Arn     string `xml:"Arn"`
	UserId  string `xml:"UserId"`
	Account string `xml:"Account"`
}

type ResponseMetadata struct {
	RequestId string `xml:"RequestId"`
}

func (a awsVerifier) VerifyToken(token string, body []byte) (*fi.VerifyResult, error) {
	if !strings.HasPrefix(token, AWSAuthenticationTokenPrefix) {
		return nil, fmt.Errorf("incorrect authorization type")
	}
	token = strings.TrimPrefix(token, AWSAuthenticationTokenPrefix)

	// We rely on the client and server using the same version of the same STS library.
	stsRequest, _ := a.sts.GetCallerIdentityRequest(nil)
	err := stsRequest.Sign()
	if err != nil {
		return nil, fmt.Errorf("creating identity request: %v", err)
	}

	stsRequest.HTTPRequest.Header = nil
	tokenBytes, err := base64.StdEncoding.DecodeString(token)
	if err != nil {
		return nil, fmt.Errorf("decoding authorization token: %v", err)
	}
	err = json.Unmarshal(tokenBytes, &stsRequest.HTTPRequest.Header)
	if err != nil {
		return nil, fmt.Errorf("unmarshalling authorization token: %v", err)
	}

	// Verify the token has signed the body content.
	sha := sha256.Sum256(body)
	if stsRequest.HTTPRequest.Header.Get("X-Kops-Request-SHA") != base64.RawStdEncoding.EncodeToString(sha[:]) {
		return nil, fmt.Errorf("incorrect SHA")
	}

	requestBytes, _ := ioutil.ReadAll(stsRequest.Body)
	_, _ = stsRequest.Body.Seek(0, io.SeekStart)
	if stsRequest.HTTPRequest.Header.Get("Content-Length") != strconv.Itoa(len(requestBytes)) {
		return nil, fmt.Errorf("incorrect content-length")
	}

	response, err := a.client.Do(stsRequest.HTTPRequest)
	if err != nil {
		return nil, fmt.Errorf("sending STS request: %v", err)
	}
	if response != nil {
		defer response.Body.Close()
	}

	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, fmt.Errorf("reading STS response: %v", err)
	}
	if response.StatusCode != 200 {
		return nil, fmt.Errorf("received status code %d from STS: %s", response.StatusCode, string(responseBody))
	}

	result := GetCallerIdentityResponse{}
	err = xml.NewDecoder(bytes.NewReader(responseBody)).Decode(&result)
	if err != nil {
		return nil, fmt.Errorf("decoding STS response: %v", err)
	}

	if result.GetCallerIdentityResult[0].Account != a.accountId {
		return nil, fmt.Errorf("incorrect account %s", result.GetCallerIdentityResult[0].Account)
	}

	arn := result.GetCallerIdentityResult[0].Arn
	parts := strings.Split(arn, ":")
	if len(parts) != 6 {
		return nil, fmt.Errorf("arn %q contains unexpected number of colons", arn)
	}
	if parts[0] != "arn" {
		return nil, fmt.Errorf("arn %q doesn't start with \"arn:\"", arn)
	}
	// parts[1] is partition
	if parts[2] != "iam" && parts[2] != "sts" {
		return nil, fmt.Errorf("arn %q has unrecognized service", arn)
	}
	// parts[3] is region
	// parts[4] is account
	resource := strings.Split(parts[5], "/")
	if resource[0] != "assumed-role" {
		return nil, fmt.Errorf("arn %q has unrecognized type", arn)
	}
	if len(resource) < 3 {
		return nil, fmt.Errorf("arn %q contains too few slashes", arn)
	}
	found := false
	for _, role := range a.opt.NodesRoles {
		if resource[1] == role {
			found = true
			break
		}
	}
	if !found {
		return nil, fmt.Errorf("arn %q does not contain acceptable node role", arn)
	}

	instanceID := resource[2]
	instances, err := a.ec2.DescribeInstances(&ec2.DescribeInstancesInput{
		InstanceIds: aws.StringSlice([]string{instanceID}),
	})
	if err != nil {
		return nil, fmt.Errorf("describing instance for arn %q", arn)
	}

	if len(instances.Reservations) <= 0 || len(instances.Reservations[0].Instances) <= 0 {
		return nil, fmt.Errorf("missing instance id: %s", instanceID)
	}
	if len(instances.Reservations[0].Instances) > 1 {
		return nil, fmt.Errorf("found multiple instances with instance id: %s", instanceID)
	}

	return &fi.VerifyResult{
		NodeName: aws.StringValue(instances.Reservations[0].Instances[0].PrivateDnsName),
	}, nil
}
<commit_msg>Verify the ARN's partition<commit_after>/*
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 awsup

import (
	"bytes"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/sts"
	"k8s.io/kops/upup/pkg/fi"
)

type AWSVerifierOptions struct {
	// NodesRoles are the IAM roles that worker nodes are permitted to have.
	NodesRoles []string `json:"nodesRoles"`
}

type awsVerifier struct {
	accountId string
	partition string
	opt       AWSVerifierOptions

	ec2    *ec2.EC2
	sts    *sts.STS
	client http.Client
}

var _ fi.Verifier = &awsVerifier{}

func NewAWSVerifier(opt *AWSVerifierOptions) (fi.Verifier, error) {
	config := aws.NewConfig().WithCredentialsChainVerboseErrors(true)
	sess, err := session.NewSession(config)
	if err != nil {
		return nil, err
	}

	stsClient := sts.New(sess)
	identity, err := stsClient.GetCallerIdentity(&sts.GetCallerIdentityInput{})
	if err != nil {
		return nil, err
	}

	partition := strings.Split(aws.StringValue(identity.Arn), ":")[1]

	metadata := ec2metadata.New(sess, config)
	region, err := metadata.Region()
	if err != nil {
		return nil, fmt.Errorf("error querying ec2 metadata service (for region): %v", err)
	}

	ec2Client := ec2.New(sess, config.WithRegion(region))

	return &awsVerifier{
		accountId: aws.StringValue(identity.Account),
		partition: partition,
		opt:       *opt,
		ec2:       ec2Client,
		sts:       stsClient,
		client: http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
				DialContext: (&net.Dialer{
					Timeout: 30 * time.Second,
				}).DialContext,
				ForceAttemptHTTP2:     true,
				DisableKeepAlives:     true,
				MaxIdleConnsPerHost:   -1,
				TLSHandshakeTimeout:   10 * time.Second,
				ExpectContinueTimeout: 1 * time.Second,
			},
			CheckRedirect: func(req *http.Request, via []*http.Request) error {
				return http.ErrUseLastResponse
			},
		},
	}, nil
}

type GetCallerIdentityResponse struct {
	XMLName                 xml.Name                  `xml:"GetCallerIdentityResponse"`
	GetCallerIdentityResult []GetCallerIdentityResult `xml:"GetCallerIdentityResult"`
	ResponseMetadata        []ResponseMetadata        `xml:"ResponseMetadata"`
}

type GetCallerIdentityResult struct {
	Arn     string `xml:"Arn"`
	UserId  string `xml:"UserId"`
	Account string `xml:"Account"`
}

type ResponseMetadata struct {
	RequestId string `xml:"RequestId"`
}

func (a awsVerifier) VerifyToken(token string, body []byte) (*fi.VerifyResult, error) {
	if !strings.HasPrefix(token, AWSAuthenticationTokenPrefix) {
		return nil, fmt.Errorf("incorrect authorization type")
	}
	token = strings.TrimPrefix(token, AWSAuthenticationTokenPrefix)

	// We rely on the client and server using the same version of the same STS library.
	stsRequest, _ := a.sts.GetCallerIdentityRequest(nil)
	err := stsRequest.Sign()
	if err != nil {
		return nil, fmt.Errorf("creating identity request: %v", err)
	}

	stsRequest.HTTPRequest.Header = nil
	tokenBytes, err := base64.StdEncoding.DecodeString(token)
	if err != nil {
		return nil, fmt.Errorf("decoding authorization token: %v", err)
	}
	err = json.Unmarshal(tokenBytes, &stsRequest.HTTPRequest.Header)
	if err != nil {
		return nil, fmt.Errorf("unmarshalling authorization token: %v", err)
	}

	// Verify the token has signed the body content.
	sha := sha256.Sum256(body)
	if stsRequest.HTTPRequest.Header.Get("X-Kops-Request-SHA") != base64.RawStdEncoding.EncodeToString(sha[:]) {
		return nil, fmt.Errorf("incorrect SHA")
	}

	requestBytes, _ := ioutil.ReadAll(stsRequest.Body)
	_, _ = stsRequest.Body.Seek(0, io.SeekStart)
	if stsRequest.HTTPRequest.Header.Get("Content-Length") != strconv.Itoa(len(requestBytes)) {
		return nil, fmt.Errorf("incorrect content-length")
	}

	response, err := a.client.Do(stsRequest.HTTPRequest)
	if err != nil {
		return nil, fmt.Errorf("sending STS request: %v", err)
	}
	if response != nil {
		defer response.Body.Close()
	}

	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, fmt.Errorf("reading STS response: %v", err)
	}
	if response.StatusCode != 200 {
		return nil, fmt.Errorf("received status code %d from STS: %s", response.StatusCode, string(responseBody))
	}

	result := GetCallerIdentityResponse{}
	err = xml.NewDecoder(bytes.NewReader(responseBody)).Decode(&result)
	if err != nil {
		return nil, fmt.Errorf("decoding STS response: %v", err)
	}

	if result.GetCallerIdentityResult[0].Account != a.accountId {
		return nil, fmt.Errorf("incorrect account %s", result.GetCallerIdentityResult[0].Account)
	}

	arn := result.GetCallerIdentityResult[0].Arn
	parts := strings.Split(arn, ":")
	if len(parts) != 6 {
		return nil, fmt.Errorf("arn %q contains unexpected number of colons", arn)
	}
	if parts[0] != "arn" {
		return nil, fmt.Errorf("arn %q doesn't start with \"arn:\"", arn)
	}
	if parts[1] != a.partition {
		return nil, fmt.Errorf("arn %q not in partion %q", arn, a.partition)
	}
	if parts[2] != "iam" && parts[2] != "sts" {
		return nil, fmt.Errorf("arn %q has unrecognized service", arn)
	}
	// parts[3] is region
	// parts[4] is account
	resource := strings.Split(parts[5], "/")
	if resource[0] != "assumed-role" {
		return nil, fmt.Errorf("arn %q has unrecognized type", arn)
	}
	if len(resource) < 3 {
		return nil, fmt.Errorf("arn %q contains too few slashes", arn)
	}
	found := false
	for _, role := range a.opt.NodesRoles {
		if resource[1] == role {
			found = true
			break
		}
	}
	if !found {
		return nil, fmt.Errorf("arn %q does not contain acceptable node role", arn)
	}

	instanceID := resource[2]
	instances, err := a.ec2.DescribeInstances(&ec2.DescribeInstancesInput{
		InstanceIds: aws.StringSlice([]string{instanceID}),
	})
	if err != nil {
		return nil, fmt.Errorf("describing instance for arn %q", arn)
	}

	if len(instances.Reservations) <= 0 || len(instances.Reservations[0].Instances) <= 0 {
		return nil, fmt.Errorf("missing instance id: %s", instanceID)
	}
	if len(instances.Reservations[0].Instances) > 1 {
		return nil, fmt.Errorf("found multiple instances with instance id: %s", instanceID)
	}

	return &fi.VerifyResult{
		NodeName: aws.StringValue(instances.Reservations[0].Instances[0].PrivateDnsName),
	}, nil
}
<|endoftext|>


<commit_before>package sudoku

import (
	"container/list"
	"fmt"
	"strconv"
)

type forcingChainsTechnique struct {
	*basicSolveTechnique
}

func (self *forcingChainsTechnique) HumanLikelihood() float64 {
	//TODO: figure out what the baseDifficulty should be, this might be higher than
	//it's actually in practice

	//Note that this number has to be pretty high because it's competing against
	//HiddenSIZEGROUP, which has the k exponential in its favor.
	return self.difficultyHelper(20000.0)
}

func (self *forcingChainsTechnique) Description(step *SolveStep) string {
	return fmt.Sprintf("cell %s only has two options, %s, and if you put either one in and see the chain of implications it leads to, both ones end up with %s in cell %s, so we can just fill that number in", step.PointerCells.Description(), step.PointerNums.Description(), step.TargetNums.Description(), step.TargetCells.Description())
}

func (self *forcingChainsTechnique) Find(grid *Grid, results chan *SolveStep, done chan bool) {
	//TODO: test that this will find multiple if they exist.

	/*
	 * Conceptually this techinque chooses a cell with two possibilities
	 * and explores forward along two branches, seeing what would happen
	 * if it followed the simple implication chains forward to see if any
	 * cells end up set to the same number on both branches, meaning
	 * that no matter what, the cell will end up that value so you can set it
	 * that way now. In some ways it's like a very easy form of guessing.
	 *
	 * This techinque will do a BFS forward from the chosen cell, and won't
	 * explore more than _MAX_IMPLICATION_STEPS steps out from that. It will
	 * stop exploring if it finds one of two types of contradictions:
	 * 1) It notes that down this branch a single cell has had two different numbers
	 * implicated into it, which implies that somewhere earlier we ran into some inconsistency
	 * or
	 * 2) As soon as we note an inconsistency (a cell with no legal values).
	 *
	 * It is important to note that for every sudoku with one solution (that is, all
	 * legal puzzles), one of the two branches MUST lead to an inconsistency somewhere
	 * it's just a matter of how forward you have to go before you find it. That means
	 * that this technique is sensitive to the order in which you explore the frontiers
	 * of implications and when you choose to bail.
	 *
	 */

	getter := grid.queue().DefaultGetter()

	//TODO: investigate bumping this back up when #100 lands.
	_MAX_IMPLICATION_STEPS := 5

	for {

		//Check if it's time to stop.
		select {
		case <-done:
			return
		default:
		}

		candidate := getter.GetSmallerThan(3)

		if candidate == nil {
			break
		}

		candidateCell := candidate.(*Cell)

		if len(candidateCell.Possibilities()) != 2 {
			//We found one with 1 possibility, which isn't interesting for us--nakedSingle should do that one.
			continue
		}

		firstPossibilityNum := candidateCell.Possibilities()[0]
		secondPossibilityNum := candidateCell.Possibilities()[1]

		firstGrid := grid.Copy()
		secondGrid := grid.Copy()

		//Check that the neighbor isn't just already having a single possibility, because then this technique is overkill.

		firstAccumulator := chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(firstGrid),
			firstPossibilityNum)

		secondAccumulator := chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(secondGrid),
			secondPossibilityNum)

		//Cells that we've already vended and shouldn't vend again if we find another
		//TODO: figure out a better way to not vend duplicates. this method feels dirty.
		vendedCells := make(map[cellRef]bool)
		//don't vend the candidateCell; obviously both of the two branches will overlap on that one
		//in generation0.
		vendedCells[candidateCell.ref()] = true

		//See if either branch, at some generation, has the same cell forced to the same number in either generation.

		//TODO: visit the pairs of generations in such a way that the sum of the two generation counts
		//goes up linearly, since we're going to skip any that together are too long.. ... but it's
		//probably not a big deal since we'll skip early in the loop anyway.
		for firstGeneration := 0; firstGeneration < len(firstAccumulator); firstGeneration++ {
			for secondGeneration := 0; secondGeneration < len(secondAccumulator); secondGeneration++ {
				firstAffectedCells := firstAccumulator[firstGeneration]
				secondAffectedCells := secondAccumulator[secondGeneration]

				//We calculated up to _MAX_IMPLICATION_STEPS down each branch,
				//but we shouldn't return steps that require more than _MAX_IMPLICATION_STEPS
				//down either branch, total.
				if firstGeneration+secondGeneration > _MAX_IMPLICATION_STEPS+1 {
					continue
				}

				for key, val := range firstAffectedCells {
					//Skip the candidateCell, because that's not a meaningful overlap--we set that one as a way of branching!

					if _, ok := vendedCells[key]; ok {
						//This is a cell we've already vended
						continue
					}

					if num, ok := secondAffectedCells[key]; ok {
						//Found cell overlap! ... is the forced number the same?
						if val == num {
							//Yup, seems like we've found a cell that is forced to the same value on either branch.
							step := &SolveStep{self,
								CellSlice{key.Cell(grid)},
								IntSlice{val},
								CellSlice{candidateCell},
								candidateCell.Possibilities(),
							}

							if step.IsUseful(grid) {
								vendedCells[key] = true
								select {
								case results <- step:
								case <-done:
									return
								}
							}
						}
					}
				}
			}
		}

		//TODO: figure out why the tests are coming back with different answers, even when only looking at the key cell
		//that should work from the example.
		//TODO: figure out a way to only compute a generation if required on each branch (don't compute all the way to _MAX_IMPLICATIONS to start)

		//TODO: ideally steps with a higher generation + generation score
		//would be scored as higher diffiuclty maybe include a
		//difficultyMultiplier in SolveStep that we can fill in? Hmmm, but
		//ideally it would factor  in at humanLikelihood level. Having a
		//million different ForcingChainLength techniques would be a
		//nightmare, peformance wise... unless there was a way to pass the
		//work done in one technique to another.

	}
}

type chainSearcherGenerationDetails map[cellRef]int

func (c chainSearcherGenerationDetails) String() string {
	result := "Begin map (length " + strconv.Itoa(len(c)) + ")\n"
	for cell, num := range c {
		result += "\t" + cell.String() + " : " + strconv.Itoa(num) + "\n"
	}
	result += "End map\n"
	return result
}

type chainSearcherAccumulator []chainSearcherGenerationDetails

func (c chainSearcherAccumulator) String() string {
	result := "Accumulator[\n"
	for _, rec := range c {
		result += fmt.Sprintf("%s\n", rec)
	}
	result += "]\n"
	return result
}

func (c chainSearcherAccumulator) addGeneration() chainSearcherAccumulator {
	newGeneration := make(chainSearcherGenerationDetails)
	result := append(c, newGeneration)
	if len(result) > 1 {
		oldGeneration := result[len(result)-2]
		//Accumulate forward old generation
		for key, val := range oldGeneration {
			newGeneration[key] = val
		}
	}
	return result
}

func chainSearcher(maxGeneration int, cell *Cell, numToApply int) chainSearcherAccumulator {

	//Chainsearcher implements a BFS over implications forward given the starting point.
	//It collects its results in the provided chainSearcherAccumulator.

	//the first time we cross over into a new generation, we should do a one-time copy of the old generation
	//into the new.
	//At any write, if we notice that we'd be overwriting to a different value, we can bail out (how would
	//we mark that we bailed early), since we've run into an inconsistency down this branch and following
	//it further is not useful.

	type modificationToMake struct {
		generation int
		cell       *Cell
		numToApply int
	}

	var result chainSearcherAccumulator

	workSteps := list.New()

	//Add the first workstep.
	workSteps.PushBack(modificationToMake{
		0,
		cell,
		numToApply,
	})

	var step modificationToMake

	e := workSteps.Front()

	for e != nil {

		workSteps.Remove(e)

		switch t := e.Value.(type) {
		case modificationToMake:
			step = t
		default:
			panic("Found unexpected type in workSteps list")
		}

		if step.generation > maxGeneration {
			break
		}

		for len(result) < step.generation+1 {
			result = result.addGeneration()
		}

		generationDetails := result[step.generation]

		cellsToVisit := step.cell.Neighbors().FilterByPossible(step.numToApply).FilterByNumPossibilities(2)

		step.cell.SetNumber(step.numToApply)

		if cell.grid.Invalid() {
			//Filling that cell make the grid invalid! We found a contradiction, no need to process
			//this branch more.

			//However, this last generation--the one we found the inconsistency in--needs to be
			//thrown out.

			return result[:len(result)-1]
		}

		if currentVal, ok := generationDetails[step.cell.ref()]; ok {
			if currentVal != step.numToApply {
				//Found a contradiction! We can bail from processing any more because this branch leads inexorably
				//to a contradiction.

				//However, this last generation--the one we found the inconsistency in--needs to be thrown out.

				return result[:len(result)-1]
			}
		}

		generationDetails[step.cell.ref()] = step.numToApply

		for _, cellToVisit := range cellsToVisit {
			possibilities := cellToVisit.Possibilities()

			if len(possibilities) != 1 {
				panic("Expected the cell to have one possibility")
			}

			forcedNum := possibilities[0]

			//Each branch modifies the grid, so create a new copy
			newGrid := cellToVisit.grid.Copy()
			cellToVisit = cellToVisit.InGrid(newGrid)

			workSteps.PushBack(modificationToMake{
				step.generation + 1,
				cellToVisit,
				forcedNum,
			})

		}

		e = workSteps.Front()

	}

	return result

}
<commit_msg>Made it so accumulationGenerationDetails is not just a map, but a struct with a map inside.<commit_after>package sudoku

import (
	"container/list"
	"fmt"
	"strconv"
)

type forcingChainsTechnique struct {
	*basicSolveTechnique
}

func (self *forcingChainsTechnique) HumanLikelihood() float64 {
	//TODO: figure out what the baseDifficulty should be, this might be higher than
	//it's actually in practice

	//Note that this number has to be pretty high because it's competing against
	//HiddenSIZEGROUP, which has the k exponential in its favor.
	return self.difficultyHelper(20000.0)
}

func (self *forcingChainsTechnique) Description(step *SolveStep) string {
	return fmt.Sprintf("cell %s only has two options, %s, and if you put either one in and see the chain of implications it leads to, both ones end up with %s in cell %s, so we can just fill that number in", step.PointerCells.Description(), step.PointerNums.Description(), step.TargetNums.Description(), step.TargetCells.Description())
}

func (self *forcingChainsTechnique) Find(grid *Grid, results chan *SolveStep, done chan bool) {
	//TODO: test that this will find multiple if they exist.

	/*
	 * Conceptually this techinque chooses a cell with two possibilities
	 * and explores forward along two branches, seeing what would happen
	 * if it followed the simple implication chains forward to see if any
	 * cells end up set to the same number on both branches, meaning
	 * that no matter what, the cell will end up that value so you can set it
	 * that way now. In some ways it's like a very easy form of guessing.
	 *
	 * This techinque will do a BFS forward from the chosen cell, and won't
	 * explore more than _MAX_IMPLICATION_STEPS steps out from that. It will
	 * stop exploring if it finds one of two types of contradictions:
	 * 1) It notes that down this branch a single cell has had two different numbers
	 * implicated into it, which implies that somewhere earlier we ran into some inconsistency
	 * or
	 * 2) As soon as we note an inconsistency (a cell with no legal values).
	 *
	 * It is important to note that for every sudoku with one solution (that is, all
	 * legal puzzles), one of the two branches MUST lead to an inconsistency somewhere
	 * it's just a matter of how forward you have to go before you find it. That means
	 * that this technique is sensitive to the order in which you explore the frontiers
	 * of implications and when you choose to bail.
	 *
	 */

	getter := grid.queue().DefaultGetter()

	//TODO: investigate bumping this back up when #100 lands.
	_MAX_IMPLICATION_STEPS := 5

	for {

		//Check if it's time to stop.
		select {
		case <-done:
			return
		default:
		}

		candidate := getter.GetSmallerThan(3)

		if candidate == nil {
			break
		}

		candidateCell := candidate.(*Cell)

		if len(candidateCell.Possibilities()) != 2 {
			//We found one with 1 possibility, which isn't interesting for us--nakedSingle should do that one.
			continue
		}

		firstPossibilityNum := candidateCell.Possibilities()[0]
		secondPossibilityNum := candidateCell.Possibilities()[1]

		firstGrid := grid.Copy()
		secondGrid := grid.Copy()

		//Check that the neighbor isn't just already having a single possibility, because then this technique is overkill.

		firstAccumulator := chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(firstGrid),
			firstPossibilityNum)

		secondAccumulator := chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(secondGrid),
			secondPossibilityNum)

		//Cells that we've already vended and shouldn't vend again if we find another
		//TODO: figure out a better way to not vend duplicates. this method feels dirty.
		vendedCells := make(map[cellRef]bool)
		//don't vend the candidateCell; obviously both of the two branches will overlap on that one
		//in generation0.
		vendedCells[candidateCell.ref()] = true

		//See if either branch, at some generation, has the same cell forced to the same number in either generation.

		//TODO: visit the pairs of generations in such a way that the sum of the two generation counts
		//goes up linearly, since we're going to skip any that together are too long.. ... but it's
		//probably not a big deal since we'll skip early in the loop anyway.
		for firstGeneration := 0; firstGeneration < len(firstAccumulator); firstGeneration++ {
			for secondGeneration := 0; secondGeneration < len(secondAccumulator); secondGeneration++ {
				firstAffectedCells := firstAccumulator[firstGeneration].numbers
				secondAffectedCells := secondAccumulator[secondGeneration].numbers

				//We calculated up to _MAX_IMPLICATION_STEPS down each branch,
				//but we shouldn't return steps that require more than _MAX_IMPLICATION_STEPS
				//down either branch, total.
				if firstGeneration+secondGeneration > _MAX_IMPLICATION_STEPS+1 {
					continue
				}

				for key, val := range firstAffectedCells {
					//Skip the candidateCell, because that's not a meaningful overlap--we set that one as a way of branching!

					if _, ok := vendedCells[key]; ok {
						//This is a cell we've already vended
						continue
					}

					if num, ok := secondAffectedCells[key]; ok {
						//Found cell overlap! ... is the forced number the same?
						if val == num {
							//Yup, seems like we've found a cell that is forced to the same value on either branch.
							step := &SolveStep{self,
								CellSlice{key.Cell(grid)},
								IntSlice{val},
								CellSlice{candidateCell},
								candidateCell.Possibilities(),
							}

							if step.IsUseful(grid) {
								vendedCells[key] = true
								select {
								case results <- step:
								case <-done:
									return
								}
							}
						}
					}
				}
			}
		}

		//TODO: figure out why the tests are coming back with different answers, even when only looking at the key cell
		//that should work from the example.
		//TODO: figure out a way to only compute a generation if required on each branch (don't compute all the way to _MAX_IMPLICATIONS to start)

		//TODO: ideally steps with a higher generation + generation score
		//would be scored as higher diffiuclty maybe include a
		//difficultyMultiplier in SolveStep that we can fill in? Hmmm, but
		//ideally it would factor  in at humanLikelihood level. Having a
		//million different ForcingChainLength techniques would be a
		//nightmare, peformance wise... unless there was a way to pass the
		//work done in one technique to another.

	}
}

type chainSearcherGenerationDetails struct {
	numbers map[cellRef]int
}

func (c chainSearcherGenerationDetails) String() string {
	result := "Begin map (length " + strconv.Itoa(len(c.numbers)) + ")\n"
	for cell, num := range c.numbers {
		result += "\t" + cell.String() + " : " + strconv.Itoa(num) + "\n"
	}
	result += "End map\n"
	return result
}

type chainSearcherAccumulator []chainSearcherGenerationDetails

func (c chainSearcherAccumulator) String() string {
	result := "Accumulator[\n"
	for _, rec := range c {
		result += fmt.Sprintf("%s\n", rec)
	}
	result += "]\n"
	return result
}

func (c chainSearcherAccumulator) addGeneration() chainSearcherAccumulator {
	newGeneration := chainSearcherGenerationDetails{make(map[cellRef]int)}
	result := append(c, newGeneration)
	if len(result) > 1 {
		oldGeneration := result[len(result)-2]
		//Accumulate forward old generation
		for key, val := range oldGeneration.numbers {
			newGeneration.numbers[key] = val
		}
	}
	return result
}

func chainSearcher(maxGeneration int, cell *Cell, numToApply int) chainSearcherAccumulator {

	//Chainsearcher implements a BFS over implications forward given the starting point.
	//It collects its results in the provided chainSearcherAccumulator.

	//the first time we cross over into a new generation, we should do a one-time copy of the old generation
	//into the new.
	//At any write, if we notice that we'd be overwriting to a different value, we can bail out (how would
	//we mark that we bailed early), since we've run into an inconsistency down this branch and following
	//it further is not useful.

	type modificationToMake struct {
		generation int
		cell       *Cell
		numToApply int
	}

	var result chainSearcherAccumulator

	workSteps := list.New()

	//Add the first workstep.
	workSteps.PushBack(modificationToMake{
		0,
		cell,
		numToApply,
	})

	var step modificationToMake

	e := workSteps.Front()

	for e != nil {

		workSteps.Remove(e)

		switch t := e.Value.(type) {
		case modificationToMake:
			step = t
		default:
			panic("Found unexpected type in workSteps list")
		}

		if step.generation > maxGeneration {
			break
		}

		for len(result) < step.generation+1 {
			result = result.addGeneration()
		}

		generationDetails := result[step.generation]

		cellsToVisit := step.cell.Neighbors().FilterByPossible(step.numToApply).FilterByNumPossibilities(2)

		step.cell.SetNumber(step.numToApply)

		if cell.grid.Invalid() {
			//Filling that cell make the grid invalid! We found a contradiction, no need to process
			//this branch more.

			//However, this last generation--the one we found the inconsistency in--needs to be
			//thrown out.

			return result[:len(result)-1]
		}

		if currentVal, ok := generationDetails.numbers[step.cell.ref()]; ok {
			if currentVal != step.numToApply {
				//Found a contradiction! We can bail from processing any more because this branch leads inexorably
				//to a contradiction.

				//However, this last generation--the one we found the inconsistency in--needs to be thrown out.

				return result[:len(result)-1]
			}
		}

		generationDetails.numbers[step.cell.ref()] = step.numToApply

		for _, cellToVisit := range cellsToVisit {
			possibilities := cellToVisit.Possibilities()

			if len(possibilities) != 1 {
				panic("Expected the cell to have one possibility")
			}

			forcedNum := possibilities[0]

			//Each branch modifies the grid, so create a new copy
			newGrid := cellToVisit.grid.Copy()
			cellToVisit = cellToVisit.InGrid(newGrid)

			workSteps.PushBack(modificationToMake{
				step.generation + 1,
				cellToVisit,
				forcedNum,
			})

		}

		e = workSteps.Front()

	}

	return result

}
<|endoftext|>


<commit_before>// Copyright Project Harbor 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 handler

import (
	"context"
	"fmt"
	"github.com/goharbor/harbor/src/lib"
	"github.com/goharbor/harbor/src/lib/errors"
	"strings"

	"github.com/go-openapi/runtime/middleware"
	"github.com/goharbor/harbor/src/common/rbac"
	"github.com/goharbor/harbor/src/controller/registry"
	"github.com/goharbor/harbor/src/lib/q"
	"github.com/goharbor/harbor/src/pkg/reg/model"
	"github.com/goharbor/harbor/src/server/v2.0/models"
	operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/registry"
)

func newRegistryAPI() *registryAPI {
	return &registryAPI{
		ctl: registry.Ctl,
	}
}

type registryAPI struct {
	BaseAPI
	ctl registry.Controller
}

func (r *registryAPI) CreateRegistry(ctx context.Context, params operation.CreateRegistryParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionCreate, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}
	registry := &model.Registry{
		Name:        params.Registry.Name,
		Description: params.Registry.Description,
		Type:        params.Registry.Type,
		URL:         params.Registry.URL,
		Insecure:    params.Registry.Insecure,
	}
	if params.Registry.Credential != nil {
		registry.Credential = &model.Credential{
			Type:         params.Registry.Credential.Type,
			AccessKey:    params.Registry.Credential.AccessKey,
			AccessSecret: params.Registry.Credential.AccessSecret,
		}
	}

	id, err := r.ctl.Create(ctx, registry)
	if err != nil {
		return r.SendError(ctx, err)
	}
	location := fmt.Sprintf("%s/%d", strings.TrimSuffix(params.HTTPRequest.URL.Path, "/"), id)
	return operation.NewCreateRegistryCreated().WithLocation(location)
}

func (r *registryAPI) GetRegistry(ctx context.Context, params operation.GetRegistryParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}

	registry, err := r.ctl.Get(ctx, params.ID)
	if err != nil {
		return r.SendError(ctx, err)
	}
	return operation.NewGetRegistryOK().WithPayload(convertRegistry(registry))
}

func (r *registryAPI) ListRegistries(ctx context.Context, params operation.ListRegistriesParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}

	query, err := r.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize)
	if err != nil {
		return r.SendError(ctx, err)
	}
	// keep backward compatibility for the "name" query
	if params.Name != nil {
		query.Keywords["Name"] = q.NewFuzzyMatchValue(*params.Name)
	}

	total, err := r.ctl.Count(ctx, query)
	if err != nil {
		return r.SendError(ctx, err)
	}
	registries, err := r.ctl.List(ctx, query)
	if err != nil {
		return r.SendError(ctx, err)
	}
	var regs []*models.Registry
	for _, registry := range registries {
		regs = append(regs, convertRegistry(registry))
	}
	return operation.NewListRegistriesOK().WithXTotalCount(total).
		WithLink(r.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
		WithPayload(regs)
}

func (r *registryAPI) DeleteRegistry(ctx context.Context, params operation.DeleteRegistryParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionDelete, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}
	if err := r.ctl.Delete(ctx, params.ID); err != nil {
		return r.SendError(ctx, err)
	}
	return operation.NewDeleteRegistryOK()
}

func (r *registryAPI) UpdateRegistry(ctx context.Context, params operation.UpdateRegistryParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionUpdate, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}
	registry, err := r.ctl.Get(ctx, params.ID)
	if err != nil {
		return r.SendError(ctx, err)
	}
	if params.Registry != nil {
		if params.Registry.Name != nil {
			registry.Name = *params.Registry.Name
		}
		if params.Registry.Description != nil {
			registry.Description = *params.Registry.Description
		}
		if params.Registry.URL != nil {
			registry.URL = *params.Registry.URL
		}
		if params.Registry.Insecure != nil {
			registry.Insecure = *params.Registry.Insecure
		}
		if registry.Credential == nil {
			registry.Credential = &model.Credential{}
		}
		if params.Registry.CredentialType != nil {
			registry.Credential.Type = *params.Registry.CredentialType
		}
		if params.Registry.AccessKey != nil {
			registry.Credential.AccessKey = *params.Registry.AccessKey
		}
		if params.Registry.AccessSecret != nil {
			registry.Credential.AccessSecret = *params.Registry.AccessSecret
		}
	}
	if err := r.ctl.Update(ctx, registry); err != nil {
		return r.SendError(ctx, err)
	}
	return operation.NewUpdateRegistryOK()
}

func (r *registryAPI) GetRegistryInfo(ctx context.Context, params operation.GetRegistryInfoParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}

	info, err := r.ctl.GetInfo(ctx, params.ID)
	if err != nil {
		return r.SendError(ctx, err)
	}

	in := &models.RegistryInfo{
		Description: info.Description,
		Type:        string(info.Type),
	}
	for _, filter := range info.SupportedResourceFilters {
		in.SupportedResourceFilters = append(in.SupportedResourceFilters, &models.FilterStyle{
			Style:  filter.Style,
			Type:   string(filter.Type),
			Values: filter.Values,
		})
	}
	for _, trigger := range info.SupportedTriggers {
		in.SupportedTriggers = append(in.SupportedTriggers, string(trigger))
	}
	return operation.NewGetRegistryInfoOK().WithPayload(in)
}

func (r *registryAPI) ListRegistryProviderTypes(ctx context.Context, params operation.ListRegistryProviderTypesParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceReplicationAdapter); err != nil {
		return r.SendError(ctx, err)
	}

	types, err := r.ctl.ListRegistryProviderTypes(ctx)
	if err != nil {
		return r.SendError(ctx, err)
	}

	return operation.NewListRegistryProviderTypesOK().WithPayload(types)
}

func (r *registryAPI) PingRegistry(ctx context.Context, params operation.PingRegistryParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}

	registry := &model.Registry{}
	var err error
	if params.Registry != nil {
		if params.Registry.ID != nil {
			registry, err = r.ctl.Get(ctx, *params.Registry.ID)
			if err != nil {
				return r.SendError(ctx, err)
			}
		}
		if params.Registry.Type != nil {
			registry.Type = *params.Registry.Type
		}
		if params.Registry.URL != nil {
			url, err := lib.ValidateHTTPURL(registry.URL)
			if err != nil {
				return r.SendError(ctx, err)
			}
			registry.URL = url
		}
		if params.Registry.CredentialType != nil {
			if registry.Credential == nil {
				registry.Credential = &model.Credential{}
			}
			registry.Credential.Type = *params.Registry.CredentialType
		}
		if params.Registry.AccessKey != nil {
			if registry.Credential == nil {
				registry.Credential = &model.Credential{}
			}
			registry.Credential.AccessKey = *params.Registry.AccessKey
		}
		if params.Registry.AccessSecret != nil {
			if registry.Credential == nil {
				registry.Credential = &model.Credential{}
			}
			registry.Credential.AccessSecret = *params.Registry.AccessSecret
		}
	}

	if len(registry.Type) == 0 || len(registry.URL) == 0 {
		return r.SendError(ctx, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("type or url cannot be empty"))
	}

	healthy, err := r.ctl.IsHealthy(ctx, registry)
	if err != nil {
		return r.SendError(ctx, err)
	}

	if !healthy {
		return r.SendError(ctx, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("the registry is unhealthy"))
	}

	return operation.NewPingRegistryOK()
}

func (r *registryAPI) ListRegistryProviderInfos(ctx context.Context, params operation.ListRegistryProviderInfosParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceReplicationAdapter); err != nil {
		return r.SendError(ctx, err)
	}

	infos, err := r.ctl.ListRegistryProviderInfos(ctx)
	if err != nil {
		return r.SendError(ctx, err)
	}

	result := map[string]models.RegistryProviderInfo{}
	for key, info := range infos {
		item := models.RegistryProviderInfo{}
		if info.CredentialPattern != nil {
			item.CredentialPattern = &models.RegistryProviderCredentialPattern{
				AccessKeyData:    info.CredentialPattern.AccessKeyData,
				AccessKeyType:    info.CredentialPattern.AccessKeyType,
				AccessSecretData: info.CredentialPattern.AccessSecretData,
				AccessSecretType: info.CredentialPattern.AccessSecretType,
			}
		}
		if info.EndpointPattern != nil {
			item.EndpointPattern = &models.RegistryProviderEndpointPattern{
				EndpointType: info.EndpointPattern.EndpointType,
			}
			for _, endpoint := range info.EndpointPattern.Endpoints {
				item.EndpointPattern.Endpoints = append(item.EndpointPattern.Endpoints, &models.RegistryEndpoint{
					Key:   endpoint.Key,
					Value: endpoint.Value,
				})
			}
		}
		result[key] = item
	}

	return operation.NewListRegistryProviderInfosOK().WithPayload(result)
}
<commit_msg>Fix bug of ping registry (#14625)<commit_after>// Copyright Project Harbor 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 handler

import (
	"context"
	"fmt"
	"github.com/goharbor/harbor/src/lib"
	"github.com/goharbor/harbor/src/lib/errors"
	"strings"

	"github.com/go-openapi/runtime/middleware"
	"github.com/goharbor/harbor/src/common/rbac"
	"github.com/goharbor/harbor/src/controller/registry"
	"github.com/goharbor/harbor/src/lib/q"
	"github.com/goharbor/harbor/src/pkg/reg/model"
	"github.com/goharbor/harbor/src/server/v2.0/models"
	operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/registry"
)

func newRegistryAPI() *registryAPI {
	return &registryAPI{
		ctl: registry.Ctl,
	}
}

type registryAPI struct {
	BaseAPI
	ctl registry.Controller
}

func (r *registryAPI) CreateRegistry(ctx context.Context, params operation.CreateRegistryParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionCreate, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}
	registry := &model.Registry{
		Name:        params.Registry.Name,
		Description: params.Registry.Description,
		Type:        params.Registry.Type,
		URL:         params.Registry.URL,
		Insecure:    params.Registry.Insecure,
	}
	if params.Registry.Credential != nil {
		registry.Credential = &model.Credential{
			Type:         params.Registry.Credential.Type,
			AccessKey:    params.Registry.Credential.AccessKey,
			AccessSecret: params.Registry.Credential.AccessSecret,
		}
	}

	id, err := r.ctl.Create(ctx, registry)
	if err != nil {
		return r.SendError(ctx, err)
	}
	location := fmt.Sprintf("%s/%d", strings.TrimSuffix(params.HTTPRequest.URL.Path, "/"), id)
	return operation.NewCreateRegistryCreated().WithLocation(location)
}

func (r *registryAPI) GetRegistry(ctx context.Context, params operation.GetRegistryParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}

	registry, err := r.ctl.Get(ctx, params.ID)
	if err != nil {
		return r.SendError(ctx, err)
	}
	return operation.NewGetRegistryOK().WithPayload(convertRegistry(registry))
}

func (r *registryAPI) ListRegistries(ctx context.Context, params operation.ListRegistriesParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}

	query, err := r.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize)
	if err != nil {
		return r.SendError(ctx, err)
	}
	// keep backward compatibility for the "name" query
	if params.Name != nil {
		query.Keywords["Name"] = q.NewFuzzyMatchValue(*params.Name)
	}

	total, err := r.ctl.Count(ctx, query)
	if err != nil {
		return r.SendError(ctx, err)
	}
	registries, err := r.ctl.List(ctx, query)
	if err != nil {
		return r.SendError(ctx, err)
	}
	var regs []*models.Registry
	for _, registry := range registries {
		regs = append(regs, convertRegistry(registry))
	}
	return operation.NewListRegistriesOK().WithXTotalCount(total).
		WithLink(r.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
		WithPayload(regs)
}

func (r *registryAPI) DeleteRegistry(ctx context.Context, params operation.DeleteRegistryParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionDelete, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}
	if err := r.ctl.Delete(ctx, params.ID); err != nil {
		return r.SendError(ctx, err)
	}
	return operation.NewDeleteRegistryOK()
}

func (r *registryAPI) UpdateRegistry(ctx context.Context, params operation.UpdateRegistryParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionUpdate, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}
	registry, err := r.ctl.Get(ctx, params.ID)
	if err != nil {
		return r.SendError(ctx, err)
	}
	if params.Registry != nil {
		if params.Registry.Name != nil {
			registry.Name = *params.Registry.Name
		}
		if params.Registry.Description != nil {
			registry.Description = *params.Registry.Description
		}
		if params.Registry.URL != nil {
			registry.URL = *params.Registry.URL
		}
		if params.Registry.Insecure != nil {
			registry.Insecure = *params.Registry.Insecure
		}
		if registry.Credential == nil {
			registry.Credential = &model.Credential{}
		}
		if params.Registry.CredentialType != nil {
			registry.Credential.Type = *params.Registry.CredentialType
		}
		if params.Registry.AccessKey != nil {
			registry.Credential.AccessKey = *params.Registry.AccessKey
		}
		if params.Registry.AccessSecret != nil {
			registry.Credential.AccessSecret = *params.Registry.AccessSecret
		}
	}
	if err := r.ctl.Update(ctx, registry); err != nil {
		return r.SendError(ctx, err)
	}
	return operation.NewUpdateRegistryOK()
}

func (r *registryAPI) GetRegistryInfo(ctx context.Context, params operation.GetRegistryInfoParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}

	info, err := r.ctl.GetInfo(ctx, params.ID)
	if err != nil {
		return r.SendError(ctx, err)
	}

	in := &models.RegistryInfo{
		Description: info.Description,
		Type:        string(info.Type),
	}
	for _, filter := range info.SupportedResourceFilters {
		in.SupportedResourceFilters = append(in.SupportedResourceFilters, &models.FilterStyle{
			Style:  filter.Style,
			Type:   string(filter.Type),
			Values: filter.Values,
		})
	}
	for _, trigger := range info.SupportedTriggers {
		in.SupportedTriggers = append(in.SupportedTriggers, string(trigger))
	}
	return operation.NewGetRegistryInfoOK().WithPayload(in)
}

func (r *registryAPI) ListRegistryProviderTypes(ctx context.Context, params operation.ListRegistryProviderTypesParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceReplicationAdapter); err != nil {
		return r.SendError(ctx, err)
	}

	types, err := r.ctl.ListRegistryProviderTypes(ctx)
	if err != nil {
		return r.SendError(ctx, err)
	}

	return operation.NewListRegistryProviderTypesOK().WithPayload(types)
}

func (r *registryAPI) PingRegistry(ctx context.Context, params operation.PingRegistryParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceRegistry); err != nil {
		return r.SendError(ctx, err)
	}

	registry := &model.Registry{}
	var err error
	if params.Registry != nil {
		if params.Registry.ID != nil {
			registry, err = r.ctl.Get(ctx, *params.Registry.ID)
			if err != nil {
				return r.SendError(ctx, err)
			}
		}
		if params.Registry.Type != nil {
			registry.Type = *params.Registry.Type
		}
		if params.Registry.URL != nil {
			url, err := lib.ValidateHTTPURL(*params.Registry.URL)
			if err != nil {
				return r.SendError(ctx, err)
			}
			registry.URL = url
		}
		if params.Registry.CredentialType != nil {
			if registry.Credential == nil {
				registry.Credential = &model.Credential{}
			}
			registry.Credential.Type = *params.Registry.CredentialType
		}
		if params.Registry.AccessKey != nil {
			if registry.Credential == nil {
				registry.Credential = &model.Credential{}
			}
			registry.Credential.AccessKey = *params.Registry.AccessKey
		}
		if params.Registry.AccessSecret != nil {
			if registry.Credential == nil {
				registry.Credential = &model.Credential{}
			}
			registry.Credential.AccessSecret = *params.Registry.AccessSecret
		}
	}

	if len(registry.Type) == 0 || len(registry.URL) == 0 {
		return r.SendError(ctx, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("type or url cannot be empty"))
	}

	healthy, err := r.ctl.IsHealthy(ctx, registry)
	if err != nil {
		return r.SendError(ctx, err)
	}

	if !healthy {
		return r.SendError(ctx, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("the registry is unhealthy"))
	}

	return operation.NewPingRegistryOK()
}

func (r *registryAPI) ListRegistryProviderInfos(ctx context.Context, params operation.ListRegistryProviderInfosParams) middleware.Responder {
	if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceReplicationAdapter); err != nil {
		return r.SendError(ctx, err)
	}

	infos, err := r.ctl.ListRegistryProviderInfos(ctx)
	if err != nil {
		return r.SendError(ctx, err)
	}

	result := map[string]models.RegistryProviderInfo{}
	for key, info := range infos {
		item := models.RegistryProviderInfo{}
		if info.CredentialPattern != nil {
			item.CredentialPattern = &models.RegistryProviderCredentialPattern{
				AccessKeyData:    info.CredentialPattern.AccessKeyData,
				AccessKeyType:    info.CredentialPattern.AccessKeyType,
				AccessSecretData: info.CredentialPattern.AccessSecretData,
				AccessSecretType: info.CredentialPattern.AccessSecretType,
			}
		}
		if info.EndpointPattern != nil {
			item.EndpointPattern = &models.RegistryProviderEndpointPattern{
				EndpointType: info.EndpointPattern.EndpointType,
			}
			for _, endpoint := range info.EndpointPattern.Endpoints {
				item.EndpointPattern.Endpoints = append(item.EndpointPattern.Endpoints, &models.RegistryEndpoint{
					Key:   endpoint.Key,
					Value: endpoint.Value,
				})
			}
		}
		result[key] = item
	}

	return operation.NewListRegistryProviderInfosOK().WithPayload(result)
}
<|endoftext|>


<commit_before>package conio

import "bytes"
import "fmt"

import "github.com/mattn/go-runewidth"

func getCharWidth(n rune) int {
	return runewidth.RuneWidth(n)
	// if n > 0xFF {
	//	return 2;
	//}else{
	//	return 1;
	//}
}

func PutRep(ch rune, n int) {
	for i := 0; i < n; i++ {
		fmt.Printf("%c", ch)
	}
}

type ReadLineBuffer struct {
	Buffer    []rune
	Length    int
	Cursor    int
	Unicode   rune
	Keycode   uint16
	ViewStart int
	ViewWidth int
}

func (this *ReadLineBuffer) Insert(pos int, c []rune) bool {
	n := len(c)
	for this.Length+n >= len(this.Buffer) {
		tmp := make([]rune, len(this.Buffer)*2)
		copy(tmp, this.Buffer)
		this.Buffer = tmp
	}
	for i := this.Length; i >= pos; i-- {
		this.Buffer[i+n] = this.Buffer[i]
	}
	for i := 0; i < n; i++ {
		this.Buffer[pos+i] = c[i]
	}
	this.Length += n
	return true
}

func (this *ReadLineBuffer) InsertString(pos int, s string) int {
	list := make([]rune, 0)
	for _, r := range s {
		list = append(list, r)
	}
	if this.Insert(pos, list) {
		return len(list)
	} else {
		return -1
	}
}

func (this *ReadLineBuffer) Delete(pos int, n int) int {
	if this.Length < pos+n {
		return 0
	}
	delw := 0
	for i := pos; i < pos+n; i++ {
		delw += getCharWidth(this.Buffer[i])
	}
	for i := pos; i < this.Length-n; i++ {
		this.Buffer[i] = this.Buffer[i+n]
	}
	this.Length -= n
	return delw
}

func (this *ReadLineBuffer) ReplaceAndRepaint(pos int, str string) {
	n := this.Cursor - pos
	this.Delete(pos, n)
	this.InsertString(pos, str)
	if pos < this.ViewStart {
		PutRep('\b', this.GetWidthBetween(this.ViewStart, this.Cursor))
	} else {
		PutRep('\b', this.GetWidthBetween(pos, this.Cursor))
	}
	this.Cursor = pos
	for _, ch := range str {
		if this.Cursor >= this.ViewStart {
			PutRep(ch, 1)
		}
		this.Cursor++
	}
	this.Repaint(this.Cursor, 0)
}

func (this *ReadLineBuffer) GetWidthBetween(from int, to int) int {
	width := 0
	for i := from; i < to; i++ {
		width += getCharWidth(this.Buffer[i])
	}
	return width
}

func (this *ReadLineBuffer) Repaint(pos int, del int) {
	bs := 0
	vp := this.GetWidthBetween(this.ViewStart, pos)

	for i := pos; i < this.Length; i++ {
		w1 := getCharWidth(this.Buffer[i])
		vp += w1
		if vp >= this.ViewWidth {
			break
		}
		PutRep(this.Buffer[i], 1)
		bs += w1
	}
	PutRep(' ', del)
	PutRep('\b', bs+del)
}

func (this *ReadLineBuffer) RepaintAll(header string) {
	for _, ch := range header {
		PutRep(ch, 1)
	}
	for i := this.ViewStart; i < this.Cursor; i++ {
		PutRep(this.Buffer[i], 1)
	}
	this.Repaint(this.Cursor, 0)
}

func (this ReadLineBuffer) String() string {
	var result bytes.Buffer
	for i := 0; i < this.Length; i++ {
		result.WriteRune(this.Buffer[i])
	}
	return result.String()
}

func (this *ReadLineBuffer) CurrentWordTop() (wordTop int) {
	wordTop = -1
	isQuoted := false
	for i := 0; i < this.Cursor; i++ {
		if this.Buffer[i] == '"' {
			isQuoted = !isQuoted
		}
		if this.Buffer[i] == ' ' && !isQuoted {
			wordTop = -1
		} else if wordTop < 0 {
			wordTop = i
		}
	}
	if wordTop < 0 {
		return this.Cursor
	} else {
		return wordTop
	}
}

func (this *ReadLineBuffer) CurrentWord() (string, int) {
	var buffer bytes.Buffer
	start := this.CurrentWordTop()
	for i := start; i < this.Cursor; i++ {
		if this.Buffer[i] != '"' {
			buffer.WriteRune(this.Buffer[i])
		}
	}
	return buffer.String(), start
}

type KeyFuncResult int

const (
	CONTINUE KeyFuncResult = iota
	ENTER    KeyFuncResult = iota
	ABORT    KeyFuncResult = iota
)

func KeyFuncPass(this *ReadLineBuffer) KeyFuncResult {
	return CONTINUE
}

func KeyFuncEnter(this *ReadLineBuffer) KeyFuncResult { // Ctrl-M
	return ENTER
}

func KeyFuncHead(this *ReadLineBuffer) KeyFuncResult { // Ctrl-A
	PutRep('\b', this.GetWidthBetween(this.ViewStart, this.Cursor))
	this.Cursor = 0
	this.ViewStart = 0
	this.Repaint(0, 1)
	return CONTINUE
}

func KeyFuncBackword(this *ReadLineBuffer) KeyFuncResult { // Ctrl-B
	if this.Cursor <= 0 {
		return CONTINUE
	}
	this.Cursor--
	if this.Cursor < this.ViewStart {
		this.ViewStart--
		this.Repaint(this.Cursor, 1)
	} else {
		PutRep('\b', getCharWidth(this.Buffer[this.Cursor]))
	}
	return CONTINUE
}

func KeyFuncTail(this *ReadLineBuffer) KeyFuncResult { // Ctrl-E
	allength := this.GetWidthBetween(this.ViewStart, this.Length)
	if allength < this.ViewWidth {
		for ; this.Cursor < this.Length; this.Cursor++ {
			PutRep(this.Buffer[this.Cursor], 1)
		}
	} else {
		PutRep('\a', 1)
		PutRep('\b', this.GetWidthBetween(this.ViewStart, this.Cursor))
		this.ViewStart = this.Length - 1
		w := getCharWidth(this.Buffer[this.ViewStart])
		for {
			if this.ViewStart <= 0 {
				break
			}
			w_ := w + getCharWidth(this.Buffer[this.ViewStart-1])
			if w_ >= this.ViewWidth {
				break
			}
			w = w_
			this.ViewStart--
		}
		for this.Cursor = this.ViewStart; this.Cursor < this.Length; this.Cursor++ {
			PutRep(this.Buffer[this.Cursor], 1)
		}
	}
	return CONTINUE
}

func KeyFuncForward(this *ReadLineBuffer) KeyFuncResult { // Ctrl-F
	if this.Cursor >= this.Length {
		return CONTINUE
	}
	w := this.GetWidthBetween(this.ViewStart, this.Cursor+1)
	if w < this.ViewWidth {
		// No Scroll
		PutRep(this.Buffer[this.Cursor], 1)
	} else {
		// Right Scroll
		PutRep('\b', this.GetWidthBetween(this.ViewStart, this.Cursor))
		if getCharWidth(this.Buffer[this.Cursor]) > getCharWidth(this.Buffer[this.ViewStart]) {
			this.ViewStart++
		}
		this.ViewStart++
		for i := this.ViewStart; i <= this.Cursor; i++ {
			PutRep(this.Buffer[i], 1)
		}
		PutRep(' ', 1)
		PutRep('\b', 1)
	}
	this.Cursor++
	return CONTINUE
}

func KeyFuncBackSpace(this *ReadLineBuffer) KeyFuncResult { // Backspace
	if this.Cursor > 0 {
		this.Cursor--
		delw := this.Delete(this.Cursor, 1)
		if this.Cursor >= this.ViewStart {
			PutRep('\b', delw)
		} else {
			this.ViewStart = this.Cursor
		}
		this.Repaint(this.Cursor, delw)
	}
	return CONTINUE
}

func KeyFuncDelete(this *ReadLineBuffer) KeyFuncResult { // Del
	delw := this.Delete(this.Cursor, 1)
	this.Repaint(this.Cursor, delw)
	return CONTINUE
}

func KeyFuncDeleteOrAbort(this *ReadLineBuffer) KeyFuncResult { // Ctrl-D
	if this.Length > 0 {
		return KeyFuncDelete(this)
	} else {
		return ABORT
	}
}

func KeyFuncInsertSelf(this *ReadLineBuffer) KeyFuncResult {
	ch := this.Unicode
	if ch < 0x20 || !this.Insert(this.Cursor, []rune{ch}) {
		return CONTINUE
	}
	w := this.GetWidthBetween(this.ViewStart, this.Cursor)
	w1 := getCharWidth(ch)
	if w+w1 >= this.ViewWidth {
		// scroll left
		PutRep('\b', w)
		if getCharWidth(this.Buffer[this.ViewStart]) < w1 {
			this.ViewStart++
		}
		this.ViewStart++
		for i := this.ViewStart; i <= this.Cursor; i++ {
			PutRep(this.Buffer[i], 1)
		}
		PutRep(' ', 1)
		PutRep('\b', 1)
	} else {
		this.Repaint(this.Cursor, -w1)
	}
	this.Cursor++
	return CONTINUE
}

func KeyFuncInsertReport(this *ReadLineBuffer) KeyFuncResult {
	L := this.InsertString(this.Cursor, fmt.Sprintf("[%X]", this.Unicode))
	if L >= 0 {
		this.Repaint(this.Cursor, -L)
		this.Cursor += L
	}
	return CONTINUE
}

var KeyMap = map[rune]func(*ReadLineBuffer) KeyFuncResult{
	'\r':   KeyFuncEnter,
	'\x01': KeyFuncHead,
	'\x02': KeyFuncBackword,
	'\x05': KeyFuncTail,
	'\x06': KeyFuncForward,
	'\b':   KeyFuncBackSpace,
	'\x04': KeyFuncDeleteOrAbort,
	'\x7F': KeyFuncDelete,
}

const (
	K_LEFT  = 0x25
	K_RIGHT = 0x27
	K_DEL   = 0x2E
	K_HOME  = 0x24
	K_END   = 0x23
	K_CTRL  = 0x11
	K_SHIFT = 0x10
)

var ZeroMap = map[uint16]func(*ReadLineBuffer) KeyFuncResult{
	K_LEFT:  KeyFuncBackword,
	K_RIGHT: KeyFuncForward,
	K_DEL:   KeyFuncDelete,
	K_HOME:  KeyFuncHead,
	K_END:   KeyFuncTail,
	K_CTRL:  KeyFuncPass,
	K_SHIFT: KeyFuncPass,
}

func ReadLine() (string, KeyFuncResult) {
	var this ReadLineBuffer
	this.Buffer = make([]rune, 20)
	this.Length = 0
	this.Cursor = 0
	this.ViewStart = 0
	this.ViewWidth = 60
	for {
		this.Unicode, this.Keycode = GetKey()
		var f func(*ReadLineBuffer) KeyFuncResult
		var ok bool
		if this.Unicode != 0 {
			f, ok = KeyMap[this.Unicode]
			if !ok {
				//f = KeyFuncInsertReport
				f = KeyFuncInsertSelf
			}
		} else {
			f, ok = ZeroMap[this.Keycode]
			if !ok {
				f = KeyFuncPass
			}
		}
		rc := f(&this)
		if rc != CONTINUE {
			fmt.Print("\n")
			return this.String(), rc
		}
	}
}
<commit_msg>use bufio to up speed.<commit_after>package conio

import "bufio"
import "bytes"
import "fmt"
import "os"
import "unicode"

import "github.com/mattn/go-runewidth"

func getCharWidth(n rune) int {
	return runewidth.RuneWidth(n)
	// if n > 0xFF {
	//	return 2;
	//}else{
	//	return 1;
	//}
}

var stdOut *bufio.Writer = bufio.NewWriter(os.Stdout)

func PutRep(ch rune, n int) {
	for i := 0; i < n; i++ {
		stdOut.WriteRune(ch)
	}
}

type ReadLineBuffer struct {
	Buffer    []rune
	Length    int
	Cursor    int
	Unicode   rune
	Keycode   uint16
	ViewStart int
	ViewWidth int
}

func (this *ReadLineBuffer) Insert(pos int, c []rune) bool {
	n := len(c)
	for this.Length+n >= len(this.Buffer) {
		tmp := make([]rune, len(this.Buffer)*2)
		copy(tmp, this.Buffer)
		this.Buffer = tmp
	}
	for i := this.Length; i >= pos; i-- {
		this.Buffer[i+n] = this.Buffer[i]
	}
	for i := 0; i < n; i++ {
		this.Buffer[pos+i] = c[i]
	}
	this.Length += n
	return true
}

func (this *ReadLineBuffer) InsertString(pos int, s string) int {
	list := make([]rune, 0)
	for _, r := range s {
		list = append(list, r)
	}
	if this.Insert(pos, list) {
		return len(list)
	} else {
		return -1
	}
}

func (this *ReadLineBuffer) Delete(pos int, n int) int {
	if this.Length < pos+n {
		return 0
	}
	delw := 0
	for i := pos; i < pos+n; i++ {
		delw += getCharWidth(this.Buffer[i])
	}
	for i := pos; i < this.Length-n; i++ {
		this.Buffer[i] = this.Buffer[i+n]
	}
	this.Length -= n
	return delw
}

func (this *ReadLineBuffer) ReplaceAndRepaint(pos int, str string) {
	n := this.Cursor - pos
	this.Delete(pos, n)
	this.InsertString(pos, str)
	if pos < this.ViewStart {
		PutRep('\b', this.GetWidthBetween(this.ViewStart, this.Cursor))
	} else {
		PutRep('\b', this.GetWidthBetween(pos, this.Cursor))
	}
	this.Cursor = pos
	for _, ch := range str {
		if this.Cursor >= this.ViewStart {
			PutRep(ch, 1)
		}
		this.Cursor++
	}
	this.Repaint(this.Cursor, 0)
}

func (this *ReadLineBuffer) GetWidthBetween(from int, to int) int {
	width := 0
	for i := from; i < to; i++ {
		width += getCharWidth(this.Buffer[i])
	}
	return width
}

func (this *ReadLineBuffer) Repaint(pos int, del int) {
	bs := 0
	vp := this.GetWidthBetween(this.ViewStart, pos)

	for i := pos; i < this.Length; i++ {
		w1 := getCharWidth(this.Buffer[i])
		vp += w1
		if vp >= this.ViewWidth {
			break
		}
		PutRep(this.Buffer[i], 1)
		bs += w1
	}
	PutRep(' ', del)
	PutRep('\b', bs+del)
}

func (this *ReadLineBuffer) RepaintAll(header string) {
	for _, ch := range header {
		PutRep(ch, 1)
	}
	for i := this.ViewStart; i < this.Cursor; i++ {
		PutRep(this.Buffer[i], 1)
	}
	this.Repaint(this.Cursor, 0)
}

func (this ReadLineBuffer) String() string {
	var result bytes.Buffer
	for i := 0; i < this.Length; i++ {
		result.WriteRune(this.Buffer[i])
	}
	return result.String()
}

func (this *ReadLineBuffer) CurrentWordTop() (wordTop int) {
	wordTop = -1
	isQuoted := false
	for i := 0; i < this.Cursor; i++ {
		if this.Buffer[i] == '"' {
			isQuoted = !isQuoted
		}
		if unicode.IsSpace(this.Buffer[i]) && !isQuoted {
			wordTop = -1
		} else if wordTop < 0 {
			wordTop = i
		}
	}
	if wordTop < 0 {
		return this.Cursor
	} else {
		return wordTop
	}
}

func (this *ReadLineBuffer) CurrentWord() (string, int) {
	var buffer bytes.Buffer
	start := this.CurrentWordTop()
	for i := start; i < this.Cursor; i++ {
		if this.Buffer[i] != '"' {
			buffer.WriteRune(this.Buffer[i])
		}
	}
	return buffer.String(), start
}

type KeyFuncResult int

const (
	CONTINUE KeyFuncResult = iota
	ENTER    KeyFuncResult = iota
	ABORT    KeyFuncResult = iota
)

func KeyFuncPass(this *ReadLineBuffer) KeyFuncResult {
	return CONTINUE
}

func KeyFuncEnter(this *ReadLineBuffer) KeyFuncResult { // Ctrl-M
	return ENTER
}

func KeyFuncHead(this *ReadLineBuffer) KeyFuncResult { // Ctrl-A
	PutRep('\b', this.GetWidthBetween(this.ViewStart, this.Cursor))
	this.Cursor = 0
	this.ViewStart = 0
	this.Repaint(0, 1)
	return CONTINUE
}

func KeyFuncBackword(this *ReadLineBuffer) KeyFuncResult { // Ctrl-B
	if this.Cursor <= 0 {
		return CONTINUE
	}
	this.Cursor--
	if this.Cursor < this.ViewStart {
		this.ViewStart--
		this.Repaint(this.Cursor, 1)
	} else {
		PutRep('\b', getCharWidth(this.Buffer[this.Cursor]))
	}
	return CONTINUE
}

func KeyFuncTail(this *ReadLineBuffer) KeyFuncResult { // Ctrl-E
	allength := this.GetWidthBetween(this.ViewStart, this.Length)
	if allength < this.ViewWidth {
		for ; this.Cursor < this.Length; this.Cursor++ {
			PutRep(this.Buffer[this.Cursor], 1)
		}
	} else {
		PutRep('\a', 1)
		PutRep('\b', this.GetWidthBetween(this.ViewStart, this.Cursor))
		this.ViewStart = this.Length - 1
		w := getCharWidth(this.Buffer[this.ViewStart])
		for {
			if this.ViewStart <= 0 {
				break
			}
			w_ := w + getCharWidth(this.Buffer[this.ViewStart-1])
			if w_ >= this.ViewWidth {
				break
			}
			w = w_
			this.ViewStart--
		}
		for this.Cursor = this.ViewStart; this.Cursor < this.Length; this.Cursor++ {
			PutRep(this.Buffer[this.Cursor], 1)
		}
	}
	return CONTINUE
}

func KeyFuncForward(this *ReadLineBuffer) KeyFuncResult { // Ctrl-F
	if this.Cursor >= this.Length {
		return CONTINUE
	}
	w := this.GetWidthBetween(this.ViewStart, this.Cursor+1)
	if w < this.ViewWidth {
		// No Scroll
		PutRep(this.Buffer[this.Cursor], 1)
	} else {
		// Right Scroll
		PutRep('\b', this.GetWidthBetween(this.ViewStart, this.Cursor))
		if getCharWidth(this.Buffer[this.Cursor]) > getCharWidth(this.Buffer[this.ViewStart]) {
			this.ViewStart++
		}
		this.ViewStart++
		for i := this.ViewStart; i <= this.Cursor; i++ {
			PutRep(this.Buffer[i], 1)
		}
		PutRep(' ', 1)
		PutRep('\b', 1)
	}
	this.Cursor++
	return CONTINUE
}

func KeyFuncBackSpace(this *ReadLineBuffer) KeyFuncResult { // Backspace
	if this.Cursor > 0 {
		this.Cursor--
		delw := this.Delete(this.Cursor, 1)
		if this.Cursor >= this.ViewStart {
			PutRep('\b', delw)
		} else {
			this.ViewStart = this.Cursor
		}
		this.Repaint(this.Cursor, delw)
	}
	return CONTINUE
}

func KeyFuncDelete(this *ReadLineBuffer) KeyFuncResult { // Del
	delw := this.Delete(this.Cursor, 1)
	this.Repaint(this.Cursor, delw)
	return CONTINUE
}

func KeyFuncDeleteOrAbort(this *ReadLineBuffer) KeyFuncResult { // Ctrl-D
	if this.Length > 0 {
		return KeyFuncDelete(this)
	} else {
		return ABORT
	}
}

func KeyFuncInsertSelf(this *ReadLineBuffer) KeyFuncResult {
	ch := this.Unicode
	if ch < 0x20 || !this.Insert(this.Cursor, []rune{ch}) {
		return CONTINUE
	}
	w := this.GetWidthBetween(this.ViewStart, this.Cursor)
	w1 := getCharWidth(ch)
	if w+w1 >= this.ViewWidth {
		// scroll left
		PutRep('\b', w)
		if getCharWidth(this.Buffer[this.ViewStart]) < w1 {
			this.ViewStart++
		}
		this.ViewStart++
		for i := this.ViewStart; i <= this.Cursor; i++ {
			PutRep(this.Buffer[i], 1)
		}
		PutRep(' ', 1)
		PutRep('\b', 1)
	} else {
		this.Repaint(this.Cursor, -w1)
	}
	this.Cursor++
	return CONTINUE
}

func KeyFuncInsertReport(this *ReadLineBuffer) KeyFuncResult {
	L := this.InsertString(this.Cursor, fmt.Sprintf("[%X]", this.Unicode))
	if L >= 0 {
		this.Repaint(this.Cursor, -L)
		this.Cursor += L
	}
	return CONTINUE
}

var KeyMap = map[rune]func(*ReadLineBuffer) KeyFuncResult{
	'\r':   KeyFuncEnter,
	'\x01': KeyFuncHead,
	'\x02': KeyFuncBackword,
	'\x05': KeyFuncTail,
	'\x06': KeyFuncForward,
	'\b':   KeyFuncBackSpace,
	'\x04': KeyFuncDeleteOrAbort,
	'\x7F': KeyFuncDelete,
}

const (
	K_LEFT  = 0x25
	K_RIGHT = 0x27
	K_DEL   = 0x2E
	K_HOME  = 0x24
	K_END   = 0x23
	K_CTRL  = 0x11
	K_SHIFT = 0x10
)

var ZeroMap = map[uint16]func(*ReadLineBuffer) KeyFuncResult{
	K_LEFT:  KeyFuncBackword,
	K_RIGHT: KeyFuncForward,
	K_DEL:   KeyFuncDelete,
	K_HOME:  KeyFuncHead,
	K_END:   KeyFuncTail,
	K_CTRL:  KeyFuncPass,
	K_SHIFT: KeyFuncPass,
}

func ReadLine() (string, KeyFuncResult) {
	var this ReadLineBuffer
	this.Buffer = make([]rune, 20)
	this.Length = 0
	this.Cursor = 0
	this.ViewStart = 0
	this.ViewWidth = 60
	for {
		stdOut.Flush()
		this.Unicode, this.Keycode = GetKey()
		var f func(*ReadLineBuffer) KeyFuncResult
		var ok bool
		if this.Unicode != 0 {
			f, ok = KeyMap[this.Unicode]
			if !ok {
				//f = KeyFuncInsertReport
				f = KeyFuncInsertSelf
			}
		} else {
			f, ok = ZeroMap[this.Keycode]
			if !ok {
				f = KeyFuncPass
			}
		}
		rc := f(&this)
		if rc != CONTINUE {
			stdOut.WriteRune('\n')
			stdOut.Flush()
			return this.String(), rc
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 sip authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.

package sip

import (
	"bufio"
	"bytes"
	"log"
	"net"
)

// StartTCPClient prepares TCP connection and returns inbound/outbound channels
func StartTCPClient(lAddr string, rAddr string) (chan []byte, chan []byte, *net.TCPConn) {
	//var wg sync.WaitGroup
	//wg.Add(2)
	//Resolve local address
	localTcpAddr, err := net.ResolveTCPAddr("tcp4", lAddr)
	CheckConnError(err)

	//Resolve remote address
	remoteTcpAddr, err := net.ResolveTCPAddr("tcp4", "127.0.0.1:5060")
	CheckConnError(err)

	//Establish connection to remote address
	conn, err := net.DialTCP("tcp", localTcpAddr, remoteTcpAddr)
	CheckConnError(err)

	// Outbound channel uses connection to send messages
	outbound := make(chan []byte)
	// Inbound channel passes received message to handleIncomingPacket function
	inbound := make(chan []byte)

	// Goroutine for receiving messages and passing them to handleIncomingPacket function
	go recvTCP(conn, inbound)
	// Goroutine for sending messages
	go sendTCP(conn, outbound)

	return inbound, outbound, conn

}

func sendTCP(connection *net.TCPConn, outbound chan []byte) {
	for message := range outbound {
		_, err := connection.Write(message)
		if err != nil {
			log.Println("Error on write: ", err)
			continue
		}
	}
}

func recvTCP(connection *net.TCPConn, inbound chan []byte) {
	for {
		scanner := bufio.NewScanner(connection)
		onSipDelimiter := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
			delim := []byte{'\r', '\n', '\r', '\n'}
			if atEOF && len(data) == 0 {
				return 0, nil, nil
			}
			if i := bytes.Index(data, delim); i > 0 {
				return i + len(delim), data[0:i], nil
			}
			if atEOF {
				return len(data), data, nil
			}
			return 0, nil, nil
		}
		scanner.Split(onSipDelimiter)
		for scanner.Scan() {
			inbound <- scanner.Bytes()
		}
	}
}
<commit_msg>removing hard coding in tcp params<commit_after>// Copyright 2015 sip authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.

package sip

import (
	"bufio"
	"bytes"
	"log"
	"net"
)

// StartTCPClient prepares TCP connection and returns inbound/outbound channels
func StartTCPClient(lAddr string, rAddr string) (chan []byte, chan []byte, *net.TCPConn) {
	//var wg sync.WaitGroup
	//wg.Add(2)
	//Resolve local address
	localTcpAddr, err := net.ResolveTCPAddr("tcp4", lAddr)
	CheckConnError(err)

	//Resolve remote address
	remoteTcpAddr, err := net.ResolveTCPAddr("tcp4", rAddr)
	CheckConnError(err)

	//Establish connection to remote address
	conn, err := net.DialTCP("tcp", localTcpAddr, remoteTcpAddr)
	CheckConnError(err)

	// Outbound channel uses connection to send messages
	outbound := make(chan []byte)
	// Inbound channel passes received message to handleIncomingPacket function
	inbound := make(chan []byte)

	// Goroutine for receiving messages and passing them to handleIncomingPacket function
	go recvTCP(conn, inbound)
	// Goroutine for sending messages
	go sendTCP(conn, outbound)

	return inbound, outbound, conn

}

func sendTCP(connection *net.TCPConn, outbound chan []byte) {
	for message := range outbound {
		_, err := connection.Write(message)
		if err != nil {
			log.Println("Error on write: ", err)
			continue
		}
	}
}

func recvTCP(connection *net.TCPConn, inbound chan []byte) {
	for {
		scanner := bufio.NewScanner(connection)
		onSipDelimiter := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
			delim := []byte{'\r', '\n', '\r', '\n'}
			if atEOF && len(data) == 0 {
				return 0, nil, nil
			}
			if i := bytes.Index(data, delim); i > 0 {
				return i + len(delim), data[0:i], nil
			}
			if atEOF {
				return len(data), data, nil
			}
			return 0, nil, nil
		}
		scanner.Split(onSipDelimiter)
		for scanner.Scan() {
			inbound <- scanner.Bytes()
		}
	}
}
<|endoftext|>