<commit_before>/*
Copyright 2011 Google Inc.

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

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

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

package client

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sync"

	"camlistore.org/pkg/auth"
)

type Client struct {
	server   string // URL prefix before "/camli/"
	authMode auth.AuthMode

	httpClient *http.Client

	statsMutex sync.Mutex
	stats      Stats

	log *log.Logger // not nil
}

type Stats struct {
	// The number of uploads that were requested, but perhaps
	// not actually performed if the server already had the items.
	UploadRequests ByCountAndBytes

	// The uploads which were actually sent to the blobserver
	// due to the server not having the blobs
	Uploads ByCountAndBytes
}

func (s *Stats) String() string {
	return "[uploadRequests=" + s.UploadRequests.String() + " uploads=" + s.Uploads.String() + "]"
}

type ByCountAndBytes struct {
	Blobs int
	Bytes int64
}

func (bb *ByCountAndBytes) String() string {
	return fmt.Sprintf("[blobs=%d bytes=%d]", bb.Blobs, bb.Bytes)
}

func New(server string) *Client {
	return &Client{
		server:     server,
		httpClient: http.DefaultClient,
	}
}

func (c *Client) SetHTTPClient(client *http.Client) {
	c.httpClient = client
}

func NewOrFail() *Client {
	log := log.New(os.Stderr, "", log.Ldate|log.Ltime)
	c := &Client{
		server:     blobServerOrDie(),
		httpClient: http.DefaultClient,
		log:        log,
	}
	err := c.SetupAuth()
	if err != nil {
		log.Fatal(err)
	}
	return c
}

func (c *Client) SetLogger(logger *log.Logger) {
	if logger == nil {
		c.log = log.New(ioutil.Discard, "", 0)
	} else {
		c.log = logger
	}
}

func (c *Client) Stats() Stats {
	c.statsMutex.Lock()
	defer c.statsMutex.Unlock()
	return c.stats // copy
}

func (c *Client) newRequest(method, url string) *http.Request {
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		panic(err.Error())
	}
	c.authMode.AddAuthHeader(req)
	return req
}
<commit_msg>client: document SetHTTPClient<commit_after>/*
Copyright 2011 Google Inc.

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

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

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

package client

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sync"

	"camlistore.org/pkg/auth"
)

type Client struct {
	server   string // URL prefix before "/camli/"
	authMode auth.AuthMode

	httpClient *http.Client

	statsMutex sync.Mutex
	stats      Stats

	log *log.Logger // not nil
}

type Stats struct {
	// The number of uploads that were requested, but perhaps
	// not actually performed if the server already had the items.
	UploadRequests ByCountAndBytes

	// The uploads which were actually sent to the blobserver
	// due to the server not having the blobs
	Uploads ByCountAndBytes
}

func (s *Stats) String() string {
	return "[uploadRequests=" + s.UploadRequests.String() + " uploads=" + s.Uploads.String() + "]"
}

type ByCountAndBytes struct {
	Blobs int
	Bytes int64
}

func (bb *ByCountAndBytes) String() string {
	return fmt.Sprintf("[blobs=%d bytes=%d]", bb.Blobs, bb.Bytes)
}

func New(server string) *Client {
	return &Client{
		server:     server,
		httpClient: http.DefaultClient,
	}
}

// SetHTTPClient sets the Camlistore client's HTTP client.
// If nil, the default HTTP client is used.
func (c *Client) SetHTTPClient(client *http.Client) {
	if client == nil {
		client = http.DefaultClient
	}
	c.httpClient = client
}

func NewOrFail() *Client {
	log := log.New(os.Stderr, "", log.Ldate|log.Ltime)
	c := &Client{
		server:     blobServerOrDie(),
		httpClient: http.DefaultClient,
		log:        log,
	}
	err := c.SetupAuth()
	if err != nil {
		log.Fatal(err)
	}
	return c
}

func (c *Client) SetLogger(logger *log.Logger) {
	if logger == nil {
		c.log = log.New(ioutil.Discard, "", 0)
	} else {
		c.log = logger
	}
}

func (c *Client) Stats() Stats {
	c.statsMutex.Lock()
	defer c.statsMutex.Unlock()
	return c.stats // copy
}

func (c *Client) newRequest(method, url string) *http.Request {
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		panic(err.Error())
	}
	c.authMode.AddAuthHeader(req)
	return req
}
<|endoftext|>


<commit_before>/*
Copyright 2016 caicloud authors. 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 docker

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"

	docker_client "github.com/fsouza/go-dockerclient"
	log "github.com/golang/glog"
)

const (
	defaultEndpoint = "unix:///var/run/docker.sock"
)

// DockerManager represents the manager of Docker, it packages the Docker client to easily use it.
// The Docker client can be direclty used for some functions not provided by this manager.
type DockerManager struct {
	// Client represets the Docker client.
	Client *docker_client.Client
}

func NewDockerManager(endpoint string) (*DockerManager, error) {
	if len(strings.TrimSpace(endpoint)) == 0 {
		endpoint = defaultEndpoint
	}

	client, err := docker_client.NewClient(endpoint)
	if err != nil {
		return nil, fmt.Errorf("new Docker client with error %s", err.Error())
	}

	if _, err := client.Version(); err != nil {
		return nil, fmt.Errorf("connect Docker server with error %s", err.Error())
	}

	return &DockerManager{
		Client: client,
	}, nil
}

func (dm *DockerManager) StartContainer(options docker_client.CreateContainerOptions) (string, error) {
	// Create the container
	container, err := dm.Client.CreateContainer(options)
	if err != nil {
		return "", fmt.Errorf("create container with error %s", err.Error())
	}

	// Run the container
	err = dm.Client.StartContainer(container.ID, options.HostConfig)
	if err != nil {
		return "", fmt.Errorf("start container with error %s", err.Error())
	}

	return container.ID, nil
}

// ExecOptions specify parameters to the ExecInContainer function.
type ExecOptions struct {
	Cmd       []string
	Container string
	User      string

	// InputStream  io.Reader
	OutputStream io.Writer
	ErrorStream  io.Writer
}

func (dm *DockerManager) ExecInContainer(options ExecOptions) error {
	// Create the exec instance in the running container.
	// In order to return after the command finishes, the options must attach the stdout and stderr,
	// and set their writer stream.
	ceo := docker_client.CreateExecOptions{
		AttachStdout: true,
		AttachStderr: true,
		Cmd:          options.Cmd,
		Container:    options.Container,
	}
	exec, err := dm.Client.CreateExec(ceo)
	if err != nil {
		return fmt.Errorf("create exec instance in container %s with error %s", ceo.Container, err.Error())
	}

	// Start the exec instance
	seo := docker_client.StartExecOptions{
		ErrorStream:  options.ErrorStream,
		OutputStream: options.OutputStream,
	}
	err = dm.Client.StartExec(exec.ID, seo)
	if err != nil {
		return fmt.Errorf("start command %s in container %s with error %s", ceo.Cmd, ceo.Container, err.Error())
	}

	// Check the exit code of the exec instance
	execInspect, err := dm.Client.InspectExec(exec.ID)
	if err != nil {
		return fmt.Errorf("inspect command %s in container %s with error %s", ceo.Cmd, ceo.Container, err.Error())
	}

	if execInspect.ExitCode != 0 {
		return fmt.Errorf("command %s failed in container %s", ceo.Cmd, ceo.Container)
	}

	return nil
}

// CopyFromContainerOptions specify parameters download resources from a container.
type CopyFromContainerOptions struct {
	Container     string
	HostPath      string
	ContainerPath string
}

func (dm *DockerManager) CopyFromContainer(options CopyFromContainerOptions) error {
	var buf bytes.Buffer
	dfco := docker_client.DownloadFromContainerOptions{
		Path:         options.ContainerPath,
		OutputStream: &buf,
	}
	err := dm.Client.DownloadFromContainer(options.Container, dfco)

	if err != nil {
		err = fmt.Errorf("copy %s from container %s with error %s", options.ContainerPath, options.Container, err.Error())
		log.Error(err)
		return err
	}

	reader := bytes.NewReader(buf.Bytes())
	tarReader := tar.NewReader(reader)

	for {
		var fileHead *tar.Header
		fileHead, err = tarReader.Next()
		if err == io.EOF {
			log.Info("docker copy file finished")
			break
		} else if err != nil {
			log.Error(err)
			return err
		}

		if fileHead.FileInfo().IsDir() {
			os.Mkdir(options.HostPath+fileHead.Name, os.FileMode(fileHead.Mode))
		} else {
			var fileOutPut *os.File
			fileOutPut, err = os.OpenFile(options.HostPath+fileHead.Name,
				os.O_CREATE|os.O_WRONLY, os.FileMode(fileHead.Mode))
			if err != nil {
				log.Error(err)
				return err
			}
			defer fileOutPut.Close()

			if _, err := io.Copy(fileOutPut, tarReader); err != nil {
				log.Error(err)
				return err
			}
		}
	}
	return nil
}
<commit_msg>feat(#319): add registry auth info into Docker manager (#337)<commit_after>/*
Copyright 2016 caicloud authors. 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 docker

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"

	docker_client "github.com/fsouza/go-dockerclient"
	log "github.com/golang/glog"
)

const (
	defaultEndpoint = "unix:///var/run/docker.sock"
)

// DockerManager represents the manager of Docker, it packages the Docker client to easily use it.
// The Docker client can be direclty used for some functions not provided by this manager.
type DockerManager struct {
	// Client represets the Docker client.
	Client     *docker_client.Client
	EndPoint   string
	AuthConfig *docker_client.AuthConfiguration
}

func NewDockerManager(endpoint, registryServer, registryUsername, registryPassword string) (*DockerManager, error) {
	if len(strings.TrimSpace(endpoint)) == 0 {
		endpoint = defaultEndpoint
	}

	client, err := docker_client.NewClient(endpoint)
	if err != nil {
		return nil, fmt.Errorf("new Docker client with error %s", err.Error())
	}

	if _, err := client.Version(); err != nil {
		return nil, fmt.Errorf("connect Docker server with error %s", err.Error())
	}

	return &DockerManager{
		Client:   client,
		EndPoint: endpoint,
		AuthConfig: &docker_client.AuthConfiguration{
			ServerAddress: registryServer,
			Username:      registryUsername,
			Password:      registryPassword,
		},
	}, nil
}

func (dm *DockerManager) StartContainer(options docker_client.CreateContainerOptions) (string, error) {
	// Create the container
	container, err := dm.Client.CreateContainer(options)
	if err != nil {
		return "", fmt.Errorf("create container with error %s", err.Error())
	}

	// Run the container
	err = dm.Client.StartContainer(container.ID, options.HostConfig)
	if err != nil {
		return "", fmt.Errorf("start container with error %s", err.Error())
	}

	return container.ID, nil
}

// ExecOptions specify parameters to the ExecInContainer function.
type ExecOptions struct {
	AttachStdin  bool
	AttachStdout bool
	AttachStderr bool
	Cmd          []string
	Container    string
	User         string

	// InputStream  io.Reader
	OutputStream io.Writer
	ErrorStream  io.Writer
}

func (dm *DockerManager) ExecInContainer(options ExecOptions) error {
	// Create the exec instance in the running container.
	// In order to return after the command finishes, the options must attach the stdout and stderr,
	// and set their writer stream.
	ceo := docker_client.CreateExecOptions{
		AttachStdout: true,
		AttachStderr: true,
		Cmd:          options.Cmd,
		Container:    options.Container,
	}
	exec, err := dm.Client.CreateExec(ceo)
	if err != nil {
		return fmt.Errorf("create exec instance in container %s with error %s", ceo.Container, err.Error())
	}

	// Start the exec instance
	seo := docker_client.StartExecOptions{
		ErrorStream:  options.ErrorStream,
		OutputStream: options.OutputStream,
	}
	err = dm.Client.StartExec(exec.ID, seo)
	if err != nil {
		return fmt.Errorf("start command %s in container %s with error %s", ceo.Cmd, ceo.Container, err.Error())
	}

	// Check the exit code of the exec instance
	execInspect, err := dm.Client.InspectExec(exec.ID)
	if err != nil {
		return fmt.Errorf("inspect command %s in container %s with error %s", ceo.Cmd, ceo.Container, err.Error())
	}

	if execInspect.ExitCode != 0 {
		return fmt.Errorf("command %s failed in container %s", ceo.Cmd, ceo.Container)
	}

	return nil
}

// CopyFromContainerOptions specify parameters download resources from a container.
type CopyFromContainerOptions struct {
	Container     string
	HostPath      string
	ContainerPath string
}

func (dm *DockerManager) CopyFromContainer(options CopyFromContainerOptions) error {
	var buf bytes.Buffer
	dfco := docker_client.DownloadFromContainerOptions{
		Path:         options.ContainerPath,
		OutputStream: &buf,
	}
	err := dm.Client.DownloadFromContainer(options.Container, dfco)

	if err != nil {
		err = fmt.Errorf("copy %s from container %s with error %s", options.ContainerPath, options.Container, err.Error())
		log.Error(err)
		return err
	}

	reader := bytes.NewReader(buf.Bytes())
	tarReader := tar.NewReader(reader)

	for {
		var fileHead *tar.Header
		fileHead, err = tarReader.Next()
		if err == io.EOF {
			log.Info("docker copy file finished")
			break
		} else if err != nil {
			log.Error(err)
			return err
		}

		if fileHead.FileInfo().IsDir() {
			os.Mkdir(options.HostPath+fileHead.Name, os.FileMode(fileHead.Mode))
		} else {
			var fileOutPut *os.File
			fileOutPut, err = os.OpenFile(options.HostPath+fileHead.Name,
				os.O_CREATE|os.O_WRONLY, os.FileMode(fileHead.Mode))
			if err != nil {
				log.Error(err)
				return err
			}
			defer fileOutPut.Close()

			if _, err := io.Copy(fileOutPut, tarReader); err != nil {
				log.Error(err)
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 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 ingress

import (
	"os/exec"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/healthz"

	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/auth"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/authreq"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/authtls"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/ipwhitelist"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/proxy"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/ratelimit"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/rewrite"
)

// IController ...
type IController interface {
	healthz.HealthzChecker

	Start() *exec.Cmd
	Stop() *exec.Cmd
	Restart() *exec.Cmd

	Test(file string) *exec.Cmd

	OnUpdate(*api.ConfigMap, Configuration) error
}

// Configuration describes
type Configuration struct {
	Upstreams    []*Upstream
	Servers      []*Server
	TCPUpstreams []*Location
	UDPUpstreams []*Location
}

// Upstream describes an upstream server (endpoint)
type Upstream struct {
	// Name represents an unique api.Service name formatted
	// as <namespace>-<name>-<port>
	Name string
	// Backends
	Backends []UpstreamServer
	// Secure indicates if the communication with the en
	Secure bool
}

// UpstreamByNameServers sorts upstreams by name
type UpstreamByNameServers []*Upstream

func (c UpstreamByNameServers) Len() int      { return len(c) }
func (c UpstreamByNameServers) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c UpstreamByNameServers) Less(i, j int) bool {
	return c[i].Name < c[j].Name
}

// UpstreamServer describes a server in an upstream
type UpstreamServer struct {
	// Address IP address of the endpoint
	Address string
	Port    string
	// MaxFails returns the maximum number of check failures
	// allowed before this should be considered dow.
	// Setting 0 indicates that the check is performed by a Kubernetes probe
	MaxFails    int
	FailTimeout int
}

// UpstreamServerByAddrPort sorts upstream servers by address and port
type UpstreamServerByAddrPort []UpstreamServer

func (c UpstreamServerByAddrPort) Len() int      { return len(c) }
func (c UpstreamServerByAddrPort) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c UpstreamServerByAddrPort) Less(i, j int) bool {
	iName := c[i].Address
	jName := c[j].Address
	if iName != jName {
		return iName < jName
	}

	iU := c[i].Port
	jU := c[j].Port
	return iU < jU
}

// Server describes a virtual server
type Server struct {
	Name              string
	Locations         []*Location
	SSL               bool
	SSLCertificate    string
	SSLCertificateKey string
	SSLPemChecksum    string
}

// ServerByName sorts server by name
type ServerByName []*Server

func (c ServerByName) Len() int      { return len(c) }
func (c ServerByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c ServerByName) Less(i, j int) bool {
	return c[i].Name < c[j].Name
}

// Location describes a server location
type Location struct {
	Path            string
	IsDefBackend    bool
	Upstream        Upstream
	BasicDigestAuth auth.BasicDigest
	RateLimit       ratelimit.RateLimit
	Redirect        rewrite.Redirect
	SecureUpstream  bool
	Whitelist       ipwhitelist.SourceRange
	EnableCORS      bool
	ExternalAuth    authreq.External
	Proxy           proxy.Configuration
	CertificateAuth authtls.SSLCert
}

// LocationByPath sorts location by path
// Location / is the last one
type LocationByPath []*Location

func (c LocationByPath) Len() int      { return len(c) }
func (c LocationByPath) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c LocationByPath) Less(i, j int) bool {
	return c[i].Path > c[j].Path
}

// SSLCert describes a SSL certificate to be used in a server
type SSLCert struct {
	CertFileName string
	KeyFileName  string
	CAFileName   string

	// PemFileName contains the path to the file with the certificate and key concatenated
	PemFileName string
	// PemSHA contains the sha1 of the pem file.
	// This is used to detect changes in the secret that contains the certificates
	PemSHA string
	// CN contains all the common names defined in the SSL certificate
	CN []string
}
<commit_msg>Change Stop signature<commit_after>/*
Copyright 2015 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 ingress

import (
	"os/exec"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/healthz"

	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/auth"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/authreq"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/authtls"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/ipwhitelist"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/proxy"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/ratelimit"
	"github.com/aledbf/ingress-controller/pkg/ingress/annotations/rewrite"
)

// IController ...
type IController interface {
	healthz.HealthzChecker

	Start() *exec.Cmd
	Stop() error
	Restart() *exec.Cmd

	Test(file string) *exec.Cmd

	OnUpdate(*api.ConfigMap, Configuration) error
}

// Configuration describes
type Configuration struct {
	Upstreams    []*Upstream
	Servers      []*Server
	TCPUpstreams []*Location
	UDPUpstreams []*Location
}

// Upstream describes an upstream server (endpoint)
type Upstream struct {
	// Name represents an unique api.Service name formatted
	// as <namespace>-<name>-<port>
	Name string
	// Backends
	Backends []UpstreamServer
	// Secure indicates if the communication with the en
	Secure bool
}

// UpstreamByNameServers sorts upstreams by name
type UpstreamByNameServers []*Upstream

func (c UpstreamByNameServers) Len() int      { return len(c) }
func (c UpstreamByNameServers) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c UpstreamByNameServers) Less(i, j int) bool {
	return c[i].Name < c[j].Name
}

// UpstreamServer describes a server in an upstream
type UpstreamServer struct {
	// Address IP address of the endpoint
	Address string
	Port    string
	// MaxFails returns the maximum number of check failures
	// allowed before this should be considered dow.
	// Setting 0 indicates that the check is performed by a Kubernetes probe
	MaxFails    int
	FailTimeout int
}

// UpstreamServerByAddrPort sorts upstream servers by address and port
type UpstreamServerByAddrPort []UpstreamServer

func (c UpstreamServerByAddrPort) Len() int      { return len(c) }
func (c UpstreamServerByAddrPort) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c UpstreamServerByAddrPort) Less(i, j int) bool {
	iName := c[i].Address
	jName := c[j].Address
	if iName != jName {
		return iName < jName
	}

	iU := c[i].Port
	jU := c[j].Port
	return iU < jU
}

// Server describes a virtual server
type Server struct {
	Name              string
	Locations         []*Location
	SSL               bool
	SSLCertificate    string
	SSLCertificateKey string
	SSLPemChecksum    string
}

// ServerByName sorts server by name
type ServerByName []*Server

func (c ServerByName) Len() int      { return len(c) }
func (c ServerByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c ServerByName) Less(i, j int) bool {
	return c[i].Name < c[j].Name
}

// Location describes a server location
type Location struct {
	Path            string
	IsDefBackend    bool
	Upstream        Upstream
	BasicDigestAuth auth.BasicDigest
	RateLimit       ratelimit.RateLimit
	Redirect        rewrite.Redirect
	SecureUpstream  bool
	Whitelist       ipwhitelist.SourceRange
	EnableCORS      bool
	ExternalAuth    authreq.External
	Proxy           proxy.Configuration
	CertificateAuth authtls.SSLCert
}

// LocationByPath sorts location by path
// Location / is the last one
type LocationByPath []*Location

func (c LocationByPath) Len() int      { return len(c) }
func (c LocationByPath) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c LocationByPath) Less(i, j int) bool {
	return c[i].Path > c[j].Path
}

// SSLCert describes a SSL certificate to be used in a server
type SSLCert struct {
	CertFileName string
	KeyFileName  string
	CAFileName   string

	// PemFileName contains the path to the file with the certificate and key concatenated
	PemFileName string
	// PemSHA contains the sha1 of the pem file.
	// This is used to detect changes in the secret that contains the certificates
	PemSHA string
	// CN contains all the common names defined in the SSL certificate
	CN []string
}
<|endoftext|>


<commit_before>// Copyright 2015 CNI 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 ip

import (
	"crypto/rand"
	"errors"
	"fmt"
	"net"
	"os"

	"github.com/containernetworking/plugins/pkg/ns"
	"github.com/containernetworking/plugins/pkg/utils/hwaddr"
	"github.com/safchain/ethtool"
	"github.com/vishvananda/netlink"
)

var (
	ErrLinkNotFound = errors.New("link not found")
)

func makeVethPair(name, peer string, mtu int) (netlink.Link, error) {
	veth := &netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{
			Name:  name,
			Flags: net.FlagUp,
			MTU:   mtu,
		},
		PeerName: peer,
	}
	if err := netlink.LinkAdd(veth); err != nil {
		return nil, err
	}
	// Re-fetch the link to get its creation-time parameters, e.g. index and mac
	veth2, err := netlink.LinkByName(name)
	if err != nil {
		netlink.LinkDel(veth) // try and clean up the link if possible.
		return nil, err
	}

	return veth2, nil
}

func peerExists(name string) bool {
	if _, err := netlink.LinkByName(name); err != nil {
		return false
	}
	return true
}

func makeVeth(name, vethPeerName string, mtu int) (peerName string, veth netlink.Link, err error) {
	for i := 0; i < 10; i++ {
		if vethPeerName != "" {
			peerName = vethPeerName
		} else {
			peerName, err = RandomVethName()
			if err != nil {
				return
			}
		}

		veth, err = makeVethPair(name, peerName, mtu)
		switch {
		case err == nil:
			return

		case os.IsExist(err):
			if peerExists(peerName) {
				continue
			}
			err = fmt.Errorf("container veth name provided (%v) already exists", name)
			return

		default:
			err = fmt.Errorf("failed to make veth pair: %v", err)
			return
		}
	}

	// should really never be hit
	err = fmt.Errorf("failed to find a unique veth name")
	return
}

// RandomVethName returns string "veth" with random prefix (hashed from entropy)
func RandomVethName() (string, error) {
	entropy := make([]byte, 4)
	_, err := rand.Reader.Read(entropy)
	if err != nil {
		return "", fmt.Errorf("failed to generate random veth name: %v", err)
	}

	// NetworkManager (recent versions) will ignore veth devices that start with "veth"
	return fmt.Sprintf("veth%x", entropy), nil
}

func RenameLink(curName, newName string) error {
	link, err := netlink.LinkByName(curName)
	if err == nil {
		err = netlink.LinkSetName(link, newName)
	}
	return err
}

func ifaceFromNetlinkLink(l netlink.Link) net.Interface {
	a := l.Attrs()
	return net.Interface{
		Index:        a.Index,
		MTU:          a.MTU,
		Name:         a.Name,
		HardwareAddr: a.HardwareAddr,
		Flags:        a.Flags,
	}
}

// SetupVeth sets up a pair of virtual ethernet devices.
// Call SetupVeth from inside the container netns.  It will create both veth
// devices and move the host-side veth into the provided hostNS namespace.
// hostVethName: If hostVethName is not specified, the host-side veth name will use a random string.
// On success, SetupVeth returns (hostVeth, containerVeth, nil)
func SetupVeth(contVethName, hostVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) {
	hostVethName, contVeth, err := makeVeth(contVethName, hostVethName, mtu)
	if err != nil {
		return net.Interface{}, net.Interface{}, err
	}

	if err = netlink.LinkSetUp(contVeth); err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to set %q up: %v", contVethName, err)
	}

	hostVeth, err := netlink.LinkByName(hostVethName)
	if err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
	}

	if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to move veth to host netns: %v", err)
	}

	err = hostNS.Do(func(_ ns.NetNS) error {
		hostVeth, err = netlink.LinkByName(hostVethName)
		if err != nil {
			return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err)
		}

		if err = netlink.LinkSetUp(hostVeth); err != nil {
			return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
		}
		return nil
	})
	if err != nil {
		return net.Interface{}, net.Interface{}, err
	}
	return ifaceFromNetlinkLink(hostVeth), ifaceFromNetlinkLink(contVeth), nil
}

// DelLinkByName removes an interface link.
func DelLinkByName(ifName string) error {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		if err.Error() == "Link not found" {
			return ErrLinkNotFound
		}
		return fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	if err = netlink.LinkDel(iface); err != nil {
		return fmt.Errorf("failed to delete %q: %v", ifName, err)
	}

	return nil
}

// DelLinkByNameAddr remove an interface and returns its addresses
func DelLinkByNameAddr(ifName string) ([]*net.IPNet, error) {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		if err != nil && err.Error() == "Link not found" {
			return nil, ErrLinkNotFound
		}
		return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	addrs, err := netlink.AddrList(iface, netlink.FAMILY_ALL)
	if err != nil {
		return nil, fmt.Errorf("failed to get IP addresses for %q: %v", ifName, err)
	}

	if err = netlink.LinkDel(iface); err != nil {
		return nil, fmt.Errorf("failed to delete %q: %v", ifName, err)
	}

	out := []*net.IPNet{}
	for _, addr := range addrs {
		if addr.IP.IsGlobalUnicast() {
			out = append(out, addr.IPNet)
		}
	}

	return out, nil
}

func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		return fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	switch {
	case ip4 == nil && ip6 == nil:
		return fmt.Errorf("neither ip4 or ip6 specified")

	case ip4 != nil:
		{
			hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix)
			if err != nil {
				return fmt.Errorf("failed to generate hardware addr: %v", err)
			}
			if err = netlink.LinkSetHardwareAddr(iface, hwAddr); err != nil {
				return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err)
			}
		}
	case ip6 != nil:
		// TODO: IPv6
	}

	return nil
}

// GetVethPeerIfindex returns the veth link object, the peer ifindex of the
// veth, or an error. This peer ifindex will only be valid in the peer's
// network namespace.
func GetVethPeerIfindex(ifName string) (netlink.Link, int, error) {
	link, err := netlink.LinkByName(ifName)
	if err != nil {
		return nil, -1, fmt.Errorf("could not look up %q: %v", ifName, err)
	}
	if _, ok := link.(*netlink.Veth); !ok {
		return nil, -1, fmt.Errorf("interface %q was not a veth interface", ifName)
	}

	// veth supports IFLA_LINK (what vishvananda/netlink calls ParentIndex)
	// on 4.1 and higher kernels
	peerIndex := link.Attrs().ParentIndex
	if peerIndex <= 0 {
		// Fall back to ethtool for 4.0 and earlier kernels
		e, err := ethtool.NewEthtool()
		if err != nil {
			return nil, -1, fmt.Errorf("failed to initialize ethtool: %v", err)
		}
		defer e.Close()

		stats, err := e.Stats(link.Attrs().Name)
		if err != nil {
			return nil, -1, fmt.Errorf("failed to request ethtool stats: %v", err)
		}
		n, ok := stats["peer_ifindex"]
		if !ok {
			return nil, -1, fmt.Errorf("failed to find 'peer_ifindex' in ethtool stats")
		}
		if n > 32767 || n == 0 {
			return nil, -1, fmt.Errorf("invalid 'peer_ifindex' %d", n)
		}
		peerIndex = int(n)
	}

	return link, peerIndex, nil
}
<commit_msg>skip makeVeth retry if a vethPeerName is set.<commit_after>// Copyright 2015 CNI 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 ip

import (
	"crypto/rand"
	"errors"
	"fmt"
	"net"
	"os"

	"github.com/containernetworking/plugins/pkg/ns"
	"github.com/containernetworking/plugins/pkg/utils/hwaddr"
	"github.com/safchain/ethtool"
	"github.com/vishvananda/netlink"
)

var (
	ErrLinkNotFound = errors.New("link not found")
)

func makeVethPair(name, peer string, mtu int) (netlink.Link, error) {
	veth := &netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{
			Name:  name,
			Flags: net.FlagUp,
			MTU:   mtu,
		},
		PeerName: peer,
	}
	if err := netlink.LinkAdd(veth); err != nil {
		return nil, err
	}
	// Re-fetch the link to get its creation-time parameters, e.g. index and mac
	veth2, err := netlink.LinkByName(name)
	if err != nil {
		netlink.LinkDel(veth) // try and clean up the link if possible.
		return nil, err
	}

	return veth2, nil
}

func peerExists(name string) bool {
	if _, err := netlink.LinkByName(name); err != nil {
		return false
	}
	return true
}

func makeVeth(name, vethPeerName string, mtu int) (peerName string, veth netlink.Link, err error) {
	for i := 0; i < 10; i++ {
		if vethPeerName != "" {
			peerName = vethPeerName
		} else {
			peerName, err = RandomVethName()
			if err != nil {
				return
			}
		}

		veth, err = makeVethPair(name, peerName, mtu)
		switch {
		case err == nil:
			return

		case os.IsExist(err):
			if peerExists(peerName) && vethPeerName == "" {
				continue
			}
			err = fmt.Errorf("container veth name provided (%v) already exists", name)
			return

		default:
			err = fmt.Errorf("failed to make veth pair: %v", err)
			return
		}
	}

	// should really never be hit
	err = fmt.Errorf("failed to find a unique veth name")
	return
}

// RandomVethName returns string "veth" with random prefix (hashed from entropy)
func RandomVethName() (string, error) {
	entropy := make([]byte, 4)
	_, err := rand.Reader.Read(entropy)
	if err != nil {
		return "", fmt.Errorf("failed to generate random veth name: %v", err)
	}

	// NetworkManager (recent versions) will ignore veth devices that start with "veth"
	return fmt.Sprintf("veth%x", entropy), nil
}

func RenameLink(curName, newName string) error {
	link, err := netlink.LinkByName(curName)
	if err == nil {
		err = netlink.LinkSetName(link, newName)
	}
	return err
}

func ifaceFromNetlinkLink(l netlink.Link) net.Interface {
	a := l.Attrs()
	return net.Interface{
		Index:        a.Index,
		MTU:          a.MTU,
		Name:         a.Name,
		HardwareAddr: a.HardwareAddr,
		Flags:        a.Flags,
	}
}

// SetupVeth sets up a pair of virtual ethernet devices.
// Call SetupVeth from inside the container netns.  It will create both veth
// devices and move the host-side veth into the provided hostNS namespace.
// hostVethName: If hostVethName is not specified, the host-side veth name will use a random string.
// On success, SetupVeth returns (hostVeth, containerVeth, nil)
func SetupVeth(contVethName, hostVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) {
	hostVethName, contVeth, err := makeVeth(contVethName, hostVethName, mtu)
	if err != nil {
		return net.Interface{}, net.Interface{}, err
	}

	if err = netlink.LinkSetUp(contVeth); err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to set %q up: %v", contVethName, err)
	}

	hostVeth, err := netlink.LinkByName(hostVethName)
	if err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
	}

	if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to move veth to host netns: %v", err)
	}

	err = hostNS.Do(func(_ ns.NetNS) error {
		hostVeth, err = netlink.LinkByName(hostVethName)
		if err != nil {
			return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err)
		}

		if err = netlink.LinkSetUp(hostVeth); err != nil {
			return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
		}
		return nil
	})
	if err != nil {
		return net.Interface{}, net.Interface{}, err
	}
	return ifaceFromNetlinkLink(hostVeth), ifaceFromNetlinkLink(contVeth), nil
}

// DelLinkByName removes an interface link.
func DelLinkByName(ifName string) error {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		if err.Error() == "Link not found" {
			return ErrLinkNotFound
		}
		return fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	if err = netlink.LinkDel(iface); err != nil {
		return fmt.Errorf("failed to delete %q: %v", ifName, err)
	}

	return nil
}

// DelLinkByNameAddr remove an interface and returns its addresses
func DelLinkByNameAddr(ifName string) ([]*net.IPNet, error) {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		if err != nil && err.Error() == "Link not found" {
			return nil, ErrLinkNotFound
		}
		return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	addrs, err := netlink.AddrList(iface, netlink.FAMILY_ALL)
	if err != nil {
		return nil, fmt.Errorf("failed to get IP addresses for %q: %v", ifName, err)
	}

	if err = netlink.LinkDel(iface); err != nil {
		return nil, fmt.Errorf("failed to delete %q: %v", ifName, err)
	}

	out := []*net.IPNet{}
	for _, addr := range addrs {
		if addr.IP.IsGlobalUnicast() {
			out = append(out, addr.IPNet)
		}
	}

	return out, nil
}

func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		return fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	switch {
	case ip4 == nil && ip6 == nil:
		return fmt.Errorf("neither ip4 or ip6 specified")

	case ip4 != nil:
		{
			hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix)
			if err != nil {
				return fmt.Errorf("failed to generate hardware addr: %v", err)
			}
			if err = netlink.LinkSetHardwareAddr(iface, hwAddr); err != nil {
				return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err)
			}
		}
	case ip6 != nil:
		// TODO: IPv6
	}

	return nil
}

// GetVethPeerIfindex returns the veth link object, the peer ifindex of the
// veth, or an error. This peer ifindex will only be valid in the peer's
// network namespace.
func GetVethPeerIfindex(ifName string) (netlink.Link, int, error) {
	link, err := netlink.LinkByName(ifName)
	if err != nil {
		return nil, -1, fmt.Errorf("could not look up %q: %v", ifName, err)
	}
	if _, ok := link.(*netlink.Veth); !ok {
		return nil, -1, fmt.Errorf("interface %q was not a veth interface", ifName)
	}

	// veth supports IFLA_LINK (what vishvananda/netlink calls ParentIndex)
	// on 4.1 and higher kernels
	peerIndex := link.Attrs().ParentIndex
	if peerIndex <= 0 {
		// Fall back to ethtool for 4.0 and earlier kernels
		e, err := ethtool.NewEthtool()
		if err != nil {
			return nil, -1, fmt.Errorf("failed to initialize ethtool: %v", err)
		}
		defer e.Close()

		stats, err := e.Stats(link.Attrs().Name)
		if err != nil {
			return nil, -1, fmt.Errorf("failed to request ethtool stats: %v", err)
		}
		n, ok := stats["peer_ifindex"]
		if !ok {
			return nil, -1, fmt.Errorf("failed to find 'peer_ifindex' in ethtool stats")
		}
		if n > 32767 || n == 0 {
			return nil, -1, fmt.Errorf("invalid 'peer_ifindex' %d", n)
		}
		peerIndex = int(n)
	}

	return link, peerIndex, nil
}
<|endoftext|>


<commit_before>package chuper

import (
	"net/url"

	"github.com/PuerkitoBio/fetchbot"
)

type Queue struct {
	*fetchbot.Queue
}

type Enqueuer interface {
	Enqueue(string, string, string, int, int) error

	EnqueueWithBasicAuth(string, string, string, int, int, string, string) error
}

func (q *Queue) Enqueue(method, URL, sourceURL string, depth, retries int) error {
	u, err := url.Parse(URL)
	if err != nil {
		return err
	}
	s, err := url.Parse(sourceURL)
	if err != nil {
		return err
	}

	cmd := &Cmd{&fetchbot.Cmd{U: u, M: method}, s, depth, retries}
	if err = q.Send(cmd); err != nil {
		return err
	}
	return nil
}

func (q *Queue) EnqueueWithBasicAuth(method string, URL string, sourceURL string, depth, retries int, user string, password string) error {
	if user == "" && password == "" {
		return q.Enqueue(method, URL, sourceURL, depth, retries)
	}

	u, err := url.Parse(URL)
	if err != nil {
		return err
	}
	s, err := url.Parse(sourceURL)
	if err != nil {
		return err
	}

	cmd := &CmdBasicAuth{&fetchbot.Cmd{U: u, M: method}, s, depth, retries, user, password}
	if err = q.Send(cmd); err != nil {
		return err
	}
	return nil
}
<commit_msg>Init commands more verbose.<commit_after>package chuper

import (
	"net/url"

	"github.com/PuerkitoBio/fetchbot"
)

type Queue struct {
	*fetchbot.Queue
}

type Enqueuer interface {
	Enqueue(string, string, string, int, int) error

	EnqueueWithBasicAuth(string, string, string, int, int, string, string) error
}

func (q *Queue) Enqueue(method, URL, sourceURL string, depth, retries int) error {
	u, err := url.Parse(URL)
	if err != nil {
		return err
	}
	s, err := url.Parse(sourceURL)
	if err != nil {
		return err
	}

	cmd := &Cmd{
		Cmd: &fetchbot.Cmd{U: u, M: method},
		S:   s,
		D:   depth,
		R:   retries,
	}

	if err = q.Send(cmd); err != nil {
		return err
	}
	return nil
}

func (q *Queue) EnqueueWithBasicAuth(method string, URL string, sourceURL string, depth, retries int, user string, password string) error {
	if user == "" && password == "" {
		return q.Enqueue(method, URL, sourceURL, depth, retries)
	}

	u, err := url.Parse(URL)
	if err != nil {
		return err
	}
	s, err := url.Parse(sourceURL)
	if err != nil {
		return err
	}

	cmd := &CmdBasicAuth{
		Cmd:  &fetchbot.Cmd{U: u, M: method},
		S:    s,
		D:    depth,
		R:    retries,
		user: user,
		pass: password,
	}

	if err = q.Send(cmd); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors 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 options

import (
	"errors"
	"sort"
	"strings"

	"k8s.io/apimachinery/pkg/fields"

	"k8s.io/klog/v2"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var errLabelsAllowListFormat = errors.New("invalid format, metric=[label1,label2,labeln...],metricN=[]")

// MetricSet represents a collection which has a unique set of metrics.
type MetricSet map[string]struct{}

func (ms *MetricSet) String() string {
	s := *ms
	ss := s.asSlice()
	sort.Strings(ss)
	return strings.Join(ss, ",")
}

// Set converts a comma-separated string of metrics into a slice and appends it to the MetricSet.
func (ms *MetricSet) Set(value string) error {
	s := *ms
	metrics := strings.Split(value, ",")
	for _, metric := range metrics {
		metric = strings.TrimSpace(metric)
		if len(metric) != 0 {
			s[metric] = struct{}{}
		}
	}
	return nil
}

// asSlice returns the MetricSet in the form of plain string slice.
func (ms MetricSet) asSlice() []string {
	metrics := make([]string, 0, len(ms))
	for metric := range ms {
		metrics = append(metrics, metric)
	}
	return metrics
}

// Type returns a descriptive string about the MetricSet type.
func (ms *MetricSet) Type() string {
	return "string"
}

// ResourceSet represents a collection which has a unique set of resources.
type ResourceSet map[string]struct{}

func (r *ResourceSet) String() string {
	s := *r
	ss := s.AsSlice()
	sort.Strings(ss)
	return strings.Join(ss, ",")
}

// Set converts a comma-separated string of resources into a slice and appends it to the ResourceSet.
func (r *ResourceSet) Set(value string) error {
	s := *r
	cols := strings.Split(value, ",")
	for _, col := range cols {
		col = strings.TrimSpace(col)
		if len(col) != 0 {
			s[col] = struct{}{}
		}
	}
	return nil
}

// AsSlice returns the Resource in the form of a plain string slice.
func (r ResourceSet) AsSlice() []string {
	cols := make([]string, 0, len(r))
	for col := range r {
		cols = append(cols, col)
	}
	return cols
}

// Type returns a descriptive string about the ResourceSet type.
func (r *ResourceSet) Type() string {
	return "string"
}

// NamespaceList represents a list of namespaces to query from.
type NamespaceList []string

func (n *NamespaceList) String() string {
	return strings.Join(*n, ",")
}

// IsAllNamespaces checks if the Namespace selector is that of `NamespaceAll` which is used for
// selecting or filtering across all namespaces.
func (n *NamespaceList) IsAllNamespaces() bool {
	return len(*n) == 1 && (*n)[0] == metav1.NamespaceAll
}

// Set converts a comma-separated string of namespaces into a slice and appends it to the NamespaceList
func (n *NamespaceList) Set(value string) error {
	splitNamespaces := strings.Split(value, ",")
	for _, ns := range splitNamespaces {
		ns = strings.TrimSpace(ns)
		if len(ns) != 0 {
			*n = append(*n, ns)
		}
	}
	return nil
}

// GetNamespaces is a helper function to get namespaces from opts.Namespaces
func (n *NamespaceList) GetNamespaces() NamespaceList {
	ns := *n
	if len(*n) == 0 {
		klog.InfoS("Using all namespaces")
		ns = DefaultNamespaces
	} else {
		if n.IsAllNamespaces() {
			klog.InfoS("Using all namespace")
		} else {
			klog.InfoS("Using namespaces", "nameSpaces", ns)
		}
	}
	return ns
}

// GetExcludeNSFieldSelector will return excluded namespace field selector
// if nsDenylist = {case1,case2}, the result will be "metadata.namespace!=case1,metadata.namespace!=case2".
func (n *NamespaceList) GetExcludeNSFieldSelector(nsDenylist []string) string {
	if len(nsDenylist) == 0 {
		return ""
	}

	namespaceExcludeSelectors := make([]fields.Selector, len(nsDenylist))
	for i, ns := range nsDenylist {
		selector := fields.OneTermNotEqualSelector("metadata.namespace", ns)
		namespaceExcludeSelectors[i] = selector
	}
	return fields.AndSelectors(namespaceExcludeSelectors...).String()
}

// Type returns a descriptive string about the NamespaceList type.
func (n *NamespaceList) Type() string {
	return "string"
}

// LabelWildcard allowlists any label
const LabelWildcard = "*"

// LabelsAllowList represents a list of allowed labels for metrics.
type LabelsAllowList map[string][]string

// Set converts a comma-separated string of resources and their allowed Kubernetes labels and appends to the LabelsAllowList.
// Value is in the following format:
// resource=[k8s-label-name,another-k8s-label],another-resource[k8s-label]
// Example: pods=[app.kubernetes.io/component,app],resource=[blah]
func (l *LabelsAllowList) Set(value string) error {
	// Taken from text/scanner EOF constant.
	const EOF = -1
	var (
		m            = make(map[string][]string, len(*l))
		previous     rune
		next         rune
		firstWordPos int
		name         string
	)
	firstWordPos = 0

	for i, v := range value {
		if i+1 == len(value) {
			next = EOF
		} else {
			next = []rune(value)[i+1]
		}
		if i-1 >= 0 {
			previous = []rune(value)[i-1]
		} else {
			previous = v
		}

		switch v {
		case '=':
			if previous == ',' || next != '[' {
				return errLabelsAllowListFormat
			}
			name = strings.TrimSpace(string(([]rune(value)[firstWordPos:i])))
			m[name] = []string{}
			firstWordPos = i + 1
		case '[':
			if previous != '=' {
				return errLabelsAllowListFormat
			}
			firstWordPos = i + 1
		case ']':
			// if after metric group, has char not comma or end.
			if next != EOF && next != ',' {
				return errLabelsAllowListFormat
			}
			if previous != '[' {
				m[name] = append(m[name], strings.TrimSpace(string(([]rune(value)[firstWordPos:i]))))
			}
			firstWordPos = i + 1
		case ',':
			// if starts or ends with comma
			if previous == v || next == EOF || next == ']' {
				return errLabelsAllowListFormat
			}
			if previous != ']' {
				m[name] = append(m[name], strings.TrimSpace(string(([]rune(value)[firstWordPos:i]))))
			}
			firstWordPos = i + 1
		}
	}
	*l = m
	return nil
}

// asSlice returns the LabelsAllowList in the form of plain string slice.
func (l LabelsAllowList) asSlice() []string {
	metrics := make([]string, 0, len(l))
	for metric := range l {
		metrics = append(metrics, metric)
	}
	return metrics
}

func (l *LabelsAllowList) String() string {
	s := *l
	ss := s.asSlice()
	sort.Strings(ss)
	return strings.Join(ss, ",")
}

// Type returns a descriptive string about the LabelsAllowList type.
func (l *LabelsAllowList) Type() string {
	return "string"
}
<commit_msg>Apply suggestions from code review<commit_after>/*
Copyright 2018 The Kubernetes Authors 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 options

import (
	"errors"
	"sort"
	"strings"

	"k8s.io/apimachinery/pkg/fields"

	"k8s.io/klog/v2"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var errLabelsAllowListFormat = errors.New("invalid format, metric=[label1,label2,labeln...],metricN=[]")

// MetricSet represents a collection which has a unique set of metrics.
type MetricSet map[string]struct{}

func (ms *MetricSet) String() string {
	s := *ms
	ss := s.asSlice()
	sort.Strings(ss)
	return strings.Join(ss, ",")
}

// Set converts a comma-separated string of metrics into a slice and appends it to the MetricSet.
func (ms *MetricSet) Set(value string) error {
	s := *ms
	metrics := strings.Split(value, ",")
	for _, metric := range metrics {
		metric = strings.TrimSpace(metric)
		if len(metric) != 0 {
			s[metric] = struct{}{}
		}
	}
	return nil
}

// asSlice returns the MetricSet in the form of plain string slice.
func (ms MetricSet) asSlice() []string {
	metrics := make([]string, 0, len(ms))
	for metric := range ms {
		metrics = append(metrics, metric)
	}
	return metrics
}

// Type returns a descriptive string about the MetricSet type.
func (ms *MetricSet) Type() string {
	return "string"
}

// ResourceSet represents a collection which has a unique set of resources.
type ResourceSet map[string]struct{}

func (r *ResourceSet) String() string {
	s := *r
	ss := s.AsSlice()
	sort.Strings(ss)
	return strings.Join(ss, ",")
}

// Set converts a comma-separated string of resources into a slice and appends it to the ResourceSet.
func (r *ResourceSet) Set(value string) error {
	s := *r
	cols := strings.Split(value, ",")
	for _, col := range cols {
		col = strings.TrimSpace(col)
		if len(col) != 0 {
			s[col] = struct{}{}
		}
	}
	return nil
}

// AsSlice returns the Resource in the form of a plain string slice.
func (r ResourceSet) AsSlice() []string {
	cols := make([]string, 0, len(r))
	for col := range r {
		cols = append(cols, col)
	}
	return cols
}

// Type returns a descriptive string about the ResourceSet type.
func (r *ResourceSet) Type() string {
	return "string"
}

// NamespaceList represents a list of namespaces to query from.
type NamespaceList []string

func (n *NamespaceList) String() string {
	return strings.Join(*n, ",")
}

// IsAllNamespaces checks if the Namespace selector is that of `NamespaceAll` which is used for
// selecting or filtering across all namespaces.
func (n *NamespaceList) IsAllNamespaces() bool {
	return len(*n) == 1 && (*n)[0] == metav1.NamespaceAll
}

// Set converts a comma-separated string of namespaces into a slice and appends it to the NamespaceList
func (n *NamespaceList) Set(value string) error {
	splitNamespaces := strings.Split(value, ",")
	for _, ns := range splitNamespaces {
		ns = strings.TrimSpace(ns)
		if len(ns) != 0 {
			*n = append(*n, ns)
		}
	}
	return nil
}

// GetNamespaces is a helper function to get namespaces from opts.Namespaces
func (n *NamespaceList) GetNamespaces() NamespaceList {
	ns := *n
	if len(*n) == 0 {
		klog.InfoS("Using all namespaces")
		ns = DefaultNamespaces
	} else {
		if n.IsAllNamespaces() {
			klog.InfoS("Using all namespaces")
		} else {
			klog.InfoS("Using namespaces", "nameSpaces", ns)
		}
	}
	return ns
}

// GetExcludeNSFieldSelector will return excluded namespace field selector
// if nsDenylist = {case1,case2}, the result will be "metadata.namespace!=case1,metadata.namespace!=case2".
func (n *NamespaceList) GetExcludeNSFieldSelector(nsDenylist []string) string {
	if len(nsDenylist) == 0 {
		return ""
	}

	namespaceExcludeSelectors := make([]fields.Selector, len(nsDenylist))
	for i, ns := range nsDenylist {
		selector := fields.OneTermNotEqualSelector("metadata.namespace", ns)
		namespaceExcludeSelectors[i] = selector
	}
	return fields.AndSelectors(namespaceExcludeSelectors...).String()
}

// Type returns a descriptive string about the NamespaceList type.
func (n *NamespaceList) Type() string {
	return "string"
}

// LabelWildcard allowlists any label
const LabelWildcard = "*"

// LabelsAllowList represents a list of allowed labels for metrics.
type LabelsAllowList map[string][]string

// Set converts a comma-separated string of resources and their allowed Kubernetes labels and appends to the LabelsAllowList.
// Value is in the following format:
// resource=[k8s-label-name,another-k8s-label],another-resource[k8s-label]
// Example: pods=[app.kubernetes.io/component,app],resource=[blah]
func (l *LabelsAllowList) Set(value string) error {
	// Taken from text/scanner EOF constant.
	const EOF = -1
	var (
		m            = make(map[string][]string, len(*l))
		previous     rune
		next         rune
		firstWordPos int
		name         string
	)
	firstWordPos = 0

	for i, v := range value {
		if i+1 == len(value) {
			next = EOF
		} else {
			next = []rune(value)[i+1]
		}
		if i-1 >= 0 {
			previous = []rune(value)[i-1]
		} else {
			previous = v
		}

		switch v {
		case '=':
			if previous == ',' || next != '[' {
				return errLabelsAllowListFormat
			}
			name = strings.TrimSpace(string(([]rune(value)[firstWordPos:i])))
			m[name] = []string{}
			firstWordPos = i + 1
		case '[':
			if previous != '=' {
				return errLabelsAllowListFormat
			}
			firstWordPos = i + 1
		case ']':
			// if after metric group, has char not comma or end.
			if next != EOF && next != ',' {
				return errLabelsAllowListFormat
			}
			if previous != '[' {
				m[name] = append(m[name], strings.TrimSpace(string(([]rune(value)[firstWordPos:i]))))
			}
			firstWordPos = i + 1
		case ',':
			// if starts or ends with comma
			if previous == v || next == EOF || next == ']' {
				return errLabelsAllowListFormat
			}
			if previous != ']' {
				m[name] = append(m[name], strings.TrimSpace(string(([]rune(value)[firstWordPos:i]))))
			}
			firstWordPos = i + 1
		}
	}
	*l = m
	return nil
}

// asSlice returns the LabelsAllowList in the form of plain string slice.
func (l LabelsAllowList) asSlice() []string {
	metrics := make([]string, 0, len(l))
	for metric := range l {
		metrics = append(metrics, metric)
	}
	return metrics
}

func (l *LabelsAllowList) String() string {
	s := *l
	ss := s.asSlice()
	sort.Strings(ss)
	return strings.Join(ss, ",")
}

// Type returns a descriptive string about the LabelsAllowList type.
func (l *LabelsAllowList) Type() string {
	return "string"
}
<|endoftext|>


<commit_before>package slinga

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/mattn/go-zglob"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"sort"
)

/*
	This file declares all the necessary structures for Slinga
*/

// LabelOperations defines the set of label manipulations (e.g. set/remove)
type LabelOperations map[string]map[string]string

// Criteria defines a structure with criteria accept/reject syntax
type Criteria struct {
	Accept []string
	Reject []string
}

// Allocation defines within a Context for a given service
type Allocation struct {
	Name     string
	Criteria *Criteria
	Labels   *LabelOperations

	// Evaluated field (when parameters in name are substituted with real values)
	NameResolved string
}

// Context for a given service
type Context struct {
	Name        string
	Service     string
	Criteria    *Criteria
	Labels      *LabelOperations
	Allocations []*Allocation
}

// Code with type and parameters, used to instantiate/update/delete component instances
type Code struct {
	Type     string
	Metadata map[string]string
	Params   interface{}
}

// ServiceComponent defines component within a service
type ServiceComponent struct {
	Name         string
	Service      string
	Code         *Code
	Discovery    interface{}
	Dependencies []string
	Labels       *LabelOperations
}

// Service defines individual service
type Service struct {
	Name       string
	Labels     *LabelOperations
	Components []*ServiceComponent

	// Lazily evaluated field (all components topologically sorted). Use via getter
	componentsOrdered []*ServiceComponent

	// Lazily evaluated field (not serialized). Use via getter
	componentsMap map[string]*ServiceComponent
}

// Cluster defines individual K8s cluster and way to access it
type Cluster struct {
	Name     string
	Type     string
	Metadata struct {
		KubeContext     string
		TillerNamespace string
		Namespace       string

		// store local proxy address when connection established
		tillerHost string

		// store kube external address
		kubeExternalAddress string
	}
}

// Policy is a global policy object with services and contexts
type Policy struct {
	Services map[string]*Service
	Contexts map[string][]*Context
	Clusters map[string]*Cluster
}

// LoadPolicyFromDir loads policy from a directory, recursively processing all files
func LoadPolicyFromDir(dir string) Policy {
	s := Policy{
		Services: make(map[string]*Service),
		Contexts: make(map[string][]*Context),
		Clusters: make(map[string]*Cluster),
	}

	// read all clusters
	files, _ := zglob.Glob(dir + "/**/cluster.*.yaml")
	sort.Strings(files)
	for _, f := range files {
		cluster := loadClusterFromFile(f)
		s.Clusters[cluster.Name] = cluster
	}

	// read all services
	files, _ = zglob.Glob(dir + "/**/service.*.yaml")
	sort.Strings(files)
	for _, f := range files {
		service := loadServiceFromFile(f)
		s.Services[service.Name] = service
	}

	// read all contexts
	files, _ = zglob.Glob(dir + "/**/context.*.yaml")
	sort.Strings(files)
	for _, f := range files {
		context := loadContextFromFile(f)
		s.Contexts[context.Service] = append(s.Contexts[context.Service], context)
	}

	return s
}

// Loads service from YAML file
func loadServiceFromFile(fileName string) *Service {
	debug.WithFields(log.Fields{
		"file": fileName,
	}).Info("Loading service")

	dat, e := ioutil.ReadFile(fileName)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to read file")
	}
	t := Service{}
	e = yaml.Unmarshal([]byte(dat), &t)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to unmarshal service")
	}
	return &t
}

// Loads context from YAML file
func loadContextFromFile(fileName string) *Context {
	debug.WithFields(log.Fields{
		"file": fileName,
	}).Info("Loading context")

	dat, e := ioutil.ReadFile(fileName)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to read file")
	}
	t := Context{}
	e = yaml.Unmarshal([]byte(dat), &t)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to unmarshal context")
	}
	return &t
}

// Loads cluster from YAML file
func loadClusterFromFile(fileName string) *Cluster {
	debug.WithFields(log.Fields{
		"file": fileName,
	}).Info("Loading cluster")

	dat, e := ioutil.ReadFile(fileName)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to read file")
	}
	t := Cluster{}
	e = yaml.Unmarshal([]byte(dat), &t)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to unmarshal cluster")
	}
	return &t
}

// Serialize object into YAML
func serializeObject(t interface{}) string {
	d, e := yaml.Marshal(&t)
	if e != nil {
		debug.WithFields(log.Fields{
			"object": t,
			"error":  e,
		}).Fatal("Can't serialize object", e)
	}
	return string(d)
}

// Prints slinga object onto screen
//noinspection GoUnusedFunction
func printObject(t interface{}) {
	fmt.Printf("--- dump:\n%s\n", serializeObject(t))

	m := make(map[interface{}]interface{})
	e := yaml.Unmarshal([]byte(serializeObject(t)), &m)
	if e != nil {
		fmt.Printf("error: %v", e)
	}
	fmt.Printf("%v\n\n", m)
}
<commit_msg>Add labels to clusters<commit_after>package slinga

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/mattn/go-zglob"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"sort"
)

/*
	This file declares all the necessary structures for Slinga
*/

// LabelOperations defines the set of label manipulations (e.g. set/remove)
type LabelOperations map[string]map[string]string

// Criteria defines a structure with criteria accept/reject syntax
type Criteria struct {
	Accept []string
	Reject []string
}

// Allocation defines within a Context for a given service
type Allocation struct {
	Name     string
	Criteria *Criteria
	Labels   *LabelOperations

	// Evaluated field (when parameters in name are substituted with real values)
	NameResolved string
}

// Context for a given service
type Context struct {
	Name        string
	Service     string
	Criteria    *Criteria
	Labels      *LabelOperations
	Allocations []*Allocation
}

// Code with type and parameters, used to instantiate/update/delete component instances
type Code struct {
	Type     string
	Metadata map[string]string
	Params   interface{}
}

// ServiceComponent defines component within a service
type ServiceComponent struct {
	Name         string
	Service      string
	Code         *Code
	Discovery    interface{}
	Dependencies []string
	Labels       *LabelOperations
}

// Service defines individual service
type Service struct {
	Name       string
	Labels     *LabelOperations
	Components []*ServiceComponent

	// Lazily evaluated field (all components topologically sorted). Use via getter
	componentsOrdered []*ServiceComponent

	// Lazily evaluated field (not serialized). Use via getter
	componentsMap map[string]*ServiceComponent
}

// Cluster defines individual K8s cluster and way to access it
type Cluster struct {
	Name     string
	Type     string
	Labels   map[string]string
	Metadata struct {
		KubeContext     string
		TillerNamespace string
		Namespace       string

		// store local proxy address when connection established
		tillerHost string

		// store kube external address
		kubeExternalAddress string
	}
}

// Policy is a global policy object with services and contexts
type Policy struct {
	Services map[string]*Service
	Contexts map[string][]*Context
	Clusters map[string]*Cluster
}

// LoadPolicyFromDir loads policy from a directory, recursively processing all files
func LoadPolicyFromDir(dir string) Policy {
	s := Policy{
		Services: make(map[string]*Service),
		Contexts: make(map[string][]*Context),
		Clusters: make(map[string]*Cluster),
	}

	// read all clusters
	files, _ := zglob.Glob(dir + "/**/cluster.*.yaml")
	sort.Strings(files)
	for _, f := range files {
		cluster := loadClusterFromFile(f)
		s.Clusters[cluster.Name] = cluster
	}

	// read all services
	files, _ = zglob.Glob(dir + "/**/service.*.yaml")
	sort.Strings(files)
	for _, f := range files {
		service := loadServiceFromFile(f)
		s.Services[service.Name] = service
	}

	// read all contexts
	files, _ = zglob.Glob(dir + "/**/context.*.yaml")
	sort.Strings(files)
	for _, f := range files {
		context := loadContextFromFile(f)
		s.Contexts[context.Service] = append(s.Contexts[context.Service], context)
	}

	return s
}

// Loads service from YAML file
func loadServiceFromFile(fileName string) *Service {
	debug.WithFields(log.Fields{
		"file": fileName,
	}).Info("Loading service")

	dat, e := ioutil.ReadFile(fileName)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to read file")
	}
	t := Service{}
	e = yaml.Unmarshal([]byte(dat), &t)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to unmarshal service")
	}
	return &t
}

// Loads context from YAML file
func loadContextFromFile(fileName string) *Context {
	debug.WithFields(log.Fields{
		"file": fileName,
	}).Info("Loading context")

	dat, e := ioutil.ReadFile(fileName)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to read file")
	}
	t := Context{}
	e = yaml.Unmarshal([]byte(dat), &t)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to unmarshal context")
	}
	return &t
}

// Loads cluster from YAML file
func loadClusterFromFile(fileName string) *Cluster {
	debug.WithFields(log.Fields{
		"file": fileName,
	}).Info("Loading cluster")

	dat, e := ioutil.ReadFile(fileName)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to read file")
	}
	t := Cluster{}
	e = yaml.Unmarshal([]byte(dat), &t)
	if e != nil {
		debug.WithFields(log.Fields{
			"file":  fileName,
			"error": e,
		}).Fatal("Unable to unmarshal cluster")
	}
	return &t
}

// Serialize object into YAML
func serializeObject(t interface{}) string {
	d, e := yaml.Marshal(&t)
	if e != nil {
		debug.WithFields(log.Fields{
			"object": t,
			"error":  e,
		}).Fatal("Can't serialize object", e)
	}
	return string(d)
}

// Prints slinga object onto screen
//noinspection GoUnusedFunction
func printObject(t interface{}) {
	fmt.Printf("--- dump:\n%s\n", serializeObject(t))

	m := make(map[interface{}]interface{})
	e := yaml.Unmarshal([]byte(serializeObject(t)), &m)
	if e != nil {
		fmt.Printf("error: %v", e)
	}
	fmt.Printf("%v\n\n", m)
}
<|endoftext|>


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

package platform

import (
	"bytes"
	"fmt"
	"io"
	"time"

	"path/filepath"
	"sync"

	"github.com/coreos/mantle/util"

	"golang.org/x/crypto/ssh"
)

const (
	sshRetries = 10
	sshTimeout = 5 * time.Second
)

// Machine represents a CoreOS instance.
type Machine interface {
	// ID returns the plaform-specific machine identifier.
	ID() string

	// IP returns the machine's public IP.
	IP() string

	// PrivateIP returns the machine's private IP.
	PrivateIP() string

	// SSHClient establishes a new SSH connection to the machine.
	SSHClient() (*ssh.Client, error)

	// PasswordSSHClient establishes a new SSH connection using the provided credentials.
	PasswordSSHClient(user string, password string) (*ssh.Client, error)

	// SSH runs a single command over a new SSH connection.
	SSH(cmd string) ([]byte, error)

	// Destroy terminates the machine and frees associated resources.
	Destroy() error
}

// Cluster represents a cluster of CoreOS machines within a single platform.
type Cluster interface {
	// NewMachine creates a new CoreOS machine.
	NewMachine(config string) (Machine, error)

	// Machines returns a slice of the active machines in the Cluster.
	Machines() []Machine

	// GetDiscoveryURL returns a new etcd discovery URL.
	GetDiscoveryURL(size int) (string, error)

	// Destroy terminates each machine in the cluster and frees any other
	// associated resources.
	Destroy() error
}

// Options contains the base options for all clusters.
type Options struct {
	BaseName string
}

// Wrap a StdoutPipe as a io.ReadCloser
type sshPipe struct {
	s   *ssh.Session
	c   *ssh.Client
	err *bytes.Buffer
	io.Reader
}

func (p *sshPipe) Close() error {
	if err := p.s.Wait(); err != nil {
		return fmt.Errorf("%s: %s", err, p.err)
	}
	if err := p.s.Close(); err != nil {
		return err
	}
	return p.c.Close()
}

// Copy a file between two machines in a cluster.
func TransferFile(src Machine, srcPath string, dst Machine, dstPath string) error {
	srcPipe, err := ReadFile(src, srcPath)
	if err != nil {
		return err
	}
	defer srcPipe.Close()

	if err := InstallFile(srcPipe, dst, dstPath); err != nil {
		return err
	}
	return nil
}

// ReadFile returns a io.ReadCloser that streams the requested file. The
// caller should close the reader when finished.
func ReadFile(m Machine, path string) (io.ReadCloser, error) {
	client, err := m.SSHClient()
	if err != nil {
		return nil, fmt.Errorf("failed creating SSH client: %v", err)
	}

	session, err := client.NewSession()
	if err != nil {
		client.Close()
		return nil, fmt.Errorf("failed creating SSH session: %v", err)
	}

	// connect session stdout
	stdoutPipe, err := session.StdoutPipe()
	if err != nil {
		session.Close()
		client.Close()
		return nil, err
	}

	// collect stderr
	errBuf := bytes.NewBuffer(nil)
	session.Stderr = errBuf

	// stream file to stdout
	err = session.Start(fmt.Sprintf("sudo cat %s", path))
	if err != nil {
		session.Close()
		client.Close()
		return nil, err
	}

	// pass stdoutPipe as a io.ReadCloser that cleans up the ssh session
	// on when closed.
	return &sshPipe{session, client, errBuf, stdoutPipe}, nil
}

// InstallFile copies data from in to the path to on m.
func InstallFile(in io.Reader, m Machine, to string) error {
	dir := filepath.Dir(to)
	out, err := m.SSH(fmt.Sprintf("sudo mkdir -p %s", dir))
	if err != nil {
		return fmt.Errorf("failed creating directory %s: %s", dir, out)
	}

	client, err := m.SSHClient()
	if err != nil {
		return fmt.Errorf("failed creating SSH client: %v", err)
	}

	defer client.Close()

	session, err := client.NewSession()
	if err != nil {
		return fmt.Errorf("failed creating SSH session: %v", err)
	}

	defer session.Close()

	// write file to fs from stdin
	session.Stdin = in
	out, err = session.CombinedOutput(fmt.Sprintf("sudo install -m 0755 /dev/stdin %s", to))
	if err != nil {
		return fmt.Errorf("failed executing install: %q: %v", out, err)
	}

	return nil
}

// NewMachines spawns len(userdatas) instances in cluster c, with
// each instance passed the respective userdata.
func NewMachines(c Cluster, userdatas []string) ([]Machine, error) {
	var wg sync.WaitGroup

	n := len(userdatas)

	if n <= 0 {
		return nil, fmt.Errorf("must provide one or more userdatas")
	}

	mchan := make(chan Machine, n)
	errchan := make(chan error, n)

	for i := 0; i < n; i++ {
		ud := userdatas[i]
		wg.Add(1)
		go func() {
			defer wg.Done()
			m, err := c.NewMachine(ud)
			if err != nil {
				errchan <- err
			}
			if m != nil {
				mchan <- m
			}
		}()
	}

	wg.Wait()
	close(mchan)
	close(errchan)

	machs := []Machine{}

	for m := range mchan {
		machs = append(machs, m)
	}

	if firsterr, ok := <-errchan; ok {
		for _, m := range machs {
			m.Destroy()
		}
		return nil, firsterr
	}

	return machs, nil
}

// CheckMachine tests a machine for various error conditions such as ssh
// being available and no systemd units failing at the time ssh is reachable.
// It also ensures the remote system is running CoreOS.
//
// TODO(mischief): better error messages.
func CheckMachine(m Machine) error {
	// ensure ssh works
	sshChecker := func() error {
		_, err := m.SSH("true")
		if err != nil {
			return err
		}
		return nil
	}

	if err := util.Retry(sshRetries, sshTimeout, sshChecker); err != nil {
		return fmt.Errorf("ssh unreachable: %v", err)
	}

	// ensure we're talking to a CoreOS system
	out, err := m.SSH("grep ^ID= /etc/os-release")
	if err != nil {
		return fmt.Errorf("no /etc/os-release file")
	}

	if !bytes.Equal(out, []byte("ID=coreos")) {
		return fmt.Errorf("not a CoreOS instance")
	}

	// ensure no systemd units failed during boot
	out, err = m.SSH("systemctl --no-legend --state failed list-units")
	if err != nil {
		return fmt.Errorf("systemctl: %v: %v", out, err)
	}

	if len(out) > 0 {
		return fmt.Errorf("some systemd units failed:\n%s", out)
	}

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

package platform

import (
	"bytes"
	"fmt"
	"io"
	"path/filepath"
	"sync"
	"time"

	"github.com/coreos/pkg/capnslog"
	"golang.org/x/crypto/ssh"

	"github.com/coreos/mantle/util"
)

const (
	sshRetries = 10
	sshTimeout = 5 * time.Second
)

var (
	plog = capnslog.NewPackageLogger("github.com/coreos/mantle", "platform")
)

// Machine represents a CoreOS instance.
type Machine interface {
	// ID returns the plaform-specific machine identifier.
	ID() string

	// IP returns the machine's public IP.
	IP() string

	// PrivateIP returns the machine's private IP.
	PrivateIP() string

	// SSHClient establishes a new SSH connection to the machine.
	SSHClient() (*ssh.Client, error)

	// PasswordSSHClient establishes a new SSH connection using the provided credentials.
	PasswordSSHClient(user string, password string) (*ssh.Client, error)

	// SSH runs a single command over a new SSH connection.
	SSH(cmd string) ([]byte, error)

	// Destroy terminates the machine and frees associated resources.
	Destroy() error
}

// Cluster represents a cluster of CoreOS machines within a single platform.
type Cluster interface {
	// NewMachine creates a new CoreOS machine.
	NewMachine(config string) (Machine, error)

	// Machines returns a slice of the active machines in the Cluster.
	Machines() []Machine

	// GetDiscoveryURL returns a new etcd discovery URL.
	GetDiscoveryURL(size int) (string, error)

	// Destroy terminates each machine in the cluster and frees any other
	// associated resources.
	Destroy() error
}

// Options contains the base options for all clusters.
type Options struct {
	BaseName string
}

// Wrap a StdoutPipe as a io.ReadCloser
type sshPipe struct {
	s   *ssh.Session
	c   *ssh.Client
	err *bytes.Buffer
	io.Reader
}

func (p *sshPipe) Close() error {
	if err := p.s.Wait(); err != nil {
		return fmt.Errorf("%s: %s", err, p.err)
	}
	if err := p.s.Close(); err != nil {
		return err
	}
	return p.c.Close()
}

// Copy a file between two machines in a cluster.
func TransferFile(src Machine, srcPath string, dst Machine, dstPath string) error {
	srcPipe, err := ReadFile(src, srcPath)
	if err != nil {
		return err
	}
	defer srcPipe.Close()

	if err := InstallFile(srcPipe, dst, dstPath); err != nil {
		return err
	}
	return nil
}

// ReadFile returns a io.ReadCloser that streams the requested file. The
// caller should close the reader when finished.
func ReadFile(m Machine, path string) (io.ReadCloser, error) {
	client, err := m.SSHClient()
	if err != nil {
		return nil, fmt.Errorf("failed creating SSH client: %v", err)
	}

	session, err := client.NewSession()
	if err != nil {
		client.Close()
		return nil, fmt.Errorf("failed creating SSH session: %v", err)
	}

	// connect session stdout
	stdoutPipe, err := session.StdoutPipe()
	if err != nil {
		session.Close()
		client.Close()
		return nil, err
	}

	// collect stderr
	errBuf := bytes.NewBuffer(nil)
	session.Stderr = errBuf

	// stream file to stdout
	err = session.Start(fmt.Sprintf("sudo cat %s", path))
	if err != nil {
		session.Close()
		client.Close()
		return nil, err
	}

	// pass stdoutPipe as a io.ReadCloser that cleans up the ssh session
	// on when closed.
	return &sshPipe{session, client, errBuf, stdoutPipe}, nil
}

// InstallFile copies data from in to the path to on m.
func InstallFile(in io.Reader, m Machine, to string) error {
	dir := filepath.Dir(to)
	out, err := m.SSH(fmt.Sprintf("sudo mkdir -p %s", dir))
	if err != nil {
		return fmt.Errorf("failed creating directory %s: %s", dir, out)
	}

	client, err := m.SSHClient()
	if err != nil {
		return fmt.Errorf("failed creating SSH client: %v", err)
	}

	defer client.Close()

	session, err := client.NewSession()
	if err != nil {
		return fmt.Errorf("failed creating SSH session: %v", err)
	}

	defer session.Close()

	// write file to fs from stdin
	session.Stdin = in
	out, err = session.CombinedOutput(fmt.Sprintf("sudo install -m 0755 /dev/stdin %s", to))
	if err != nil {
		return fmt.Errorf("failed executing install: %q: %v", out, err)
	}

	return nil
}

// NewMachines spawns len(userdatas) instances in cluster c, with
// each instance passed the respective userdata.
func NewMachines(c Cluster, userdatas []string) ([]Machine, error) {
	var wg sync.WaitGroup

	n := len(userdatas)

	if n <= 0 {
		return nil, fmt.Errorf("must provide one or more userdatas")
	}

	mchan := make(chan Machine, n)
	errchan := make(chan error, n)

	for i := 0; i < n; i++ {
		ud := userdatas[i]
		wg.Add(1)
		go func() {
			defer wg.Done()
			m, err := c.NewMachine(ud)
			if err != nil {
				errchan <- err
			}
			if m != nil {
				mchan <- m
			}
		}()
	}

	wg.Wait()
	close(mchan)
	close(errchan)

	machs := []Machine{}

	for m := range mchan {
		machs = append(machs, m)
	}

	if firsterr, ok := <-errchan; ok {
		for _, m := range machs {
			m.Destroy()
		}
		return nil, firsterr
	}

	return machs, nil
}

// CheckMachine tests a machine for various error conditions such as ssh
// being available and no systemd units failing at the time ssh is reachable.
// It also ensures the remote system is running CoreOS.
//
// TODO(mischief): better error messages.
func CheckMachine(m Machine) error {
	// ensure ssh works
	sshChecker := func() error {
		_, err := m.SSH("true")
		if err != nil {
			return err
		}
		return nil
	}

	if err := util.Retry(sshRetries, sshTimeout, sshChecker); err != nil {
		return fmt.Errorf("ssh unreachable: %v", err)
	}

	// ensure we're talking to a CoreOS system
	out, err := m.SSH("grep ^ID= /etc/os-release")
	if err != nil {
		return fmt.Errorf("no /etc/os-release file")
	}

	if !bytes.Equal(out, []byte("ID=coreos")) {
		return fmt.Errorf("not a CoreOS instance")
	}

	// ensure no systemd units failed during boot
	out, err = m.SSH("systemctl --no-legend --state failed list-units")
	if err != nil {
		return fmt.Errorf("systemctl: %v: %v", out, err)
	}

	if len(out) > 0 {
		if plog.LevelAt(capnslog.DEBUG) {
			log, err := m.SSH("journalctl -b")
			if err != nil {
				plog.Errorf("Failed to read journal: %v", err)
			} else {
				plog.Debugf("Journal:\n%s", log)
			}
		}

		return fmt.Errorf("some systemd units failed:\n%s", out)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd 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 platforms

import (
	"bufio"
	"os"
	"runtime"
	"strings"

	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/log"
	"github.com/pkg/errors"
)

// Present the ARM instruction set architecture, eg: v7, v8
var cpuVariant string

func init() {
	if isArmArch(runtime.GOARCH) {
		cpuVariant = getCPUVariant()
	} else {
		cpuVariant = ""
	}
}

// For Linux, the kernel has already detected the ABI, ISA and Features.
// So we don't need to access the ARM registers to detect platform information
// by ourselves. We can just parse these information from /proc/cpuinfo
func getCPUInfo(pattern string) (info string, err error) {
	if !isLinuxOS(runtime.GOOS) {
		return "", errors.Wrapf(errdefs.ErrNotImplemented, "getCPUInfo for OS %s", runtime.GOOS)
	}

	cpuinfo, err := os.Open("/proc/cpuinfo")
	if err != nil {
		return "", err
	}
	defer cpuinfo.Close()

	// Start to Parse the Cpuinfo line by line. For SMP SoC, we parse
	// the first core is enough.
	scanner := bufio.NewScanner(cpuinfo)
	for scanner.Scan() {
		newline := scanner.Text()
		list := strings.Split(newline, ":")

		if len(list) > 1 && strings.EqualFold(strings.TrimSpace(list[0]), pattern) {
			return strings.TrimSpace(list[1]), nil
		}
	}

	// Check whether the scanner encountered errors
	err = scanner.Err()
	if err != nil {
		return "", err
	}

	return "", errors.Wrapf(errdefs.ErrNotFound, "getCPUInfo for pattern: %s", pattern)
}

func getCPUVariant() string {
	if runtime.GOOS == "windows" {
		// Windows only supports v7 for ARM32 and v8 for ARM64 and so we can use
		// runtime.GOARCH to determine the variants
		var variant string
		switch runtime.GOARCH {
		case "arm64":
			variant = "v8"
		case "arm":
			variant = "v7"
		default:
			variant = "unknown"
		}

		return variant
	}

	variant, err := getCPUInfo("Cpu architecture")
	if err != nil {
		log.L.WithError(err).Error("failure getting variant")
		return ""
	}

	switch strings.ToLower(variant) {
	case "8", "aarch64":
		// special case: if running a 32-bit userspace on aarch64, the variant should be "v7"
		if runtime.GOARCH == "arm" {
			variant = "v7"
		} else {
			variant = "v8"
		}
	case "7", "7m", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)":
		variant = "v7"
	case "6", "6tej":
		variant = "v6"
	case "5", "5t", "5te", "5tej":
		variant = "v5"
	case "4", "4t":
		variant = "v4"
	case "3":
		variant = "v3"
	default:
		variant = "unknown"
	}

	return variant
}
<commit_msg>platforms: don’t log arm variant error on darwin<commit_after>/*
   Copyright The containerd 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 platforms

import (
	"bufio"
	"os"
	"runtime"
	"strings"

	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/log"
	"github.com/pkg/errors"
)

// Present the ARM instruction set architecture, eg: v7, v8
var cpuVariant string

func init() {
	if isArmArch(runtime.GOARCH) {
		cpuVariant = getCPUVariant()
	} else {
		cpuVariant = ""
	}
}

// For Linux, the kernel has already detected the ABI, ISA and Features.
// So we don't need to access the ARM registers to detect platform information
// by ourselves. We can just parse these information from /proc/cpuinfo
func getCPUInfo(pattern string) (info string, err error) {
	if !isLinuxOS(runtime.GOOS) {
		return "", errors.Wrapf(errdefs.ErrNotImplemented, "getCPUInfo for OS %s", runtime.GOOS)
	}

	cpuinfo, err := os.Open("/proc/cpuinfo")
	if err != nil {
		return "", err
	}
	defer cpuinfo.Close()

	// Start to Parse the Cpuinfo line by line. For SMP SoC, we parse
	// the first core is enough.
	scanner := bufio.NewScanner(cpuinfo)
	for scanner.Scan() {
		newline := scanner.Text()
		list := strings.Split(newline, ":")

		if len(list) > 1 && strings.EqualFold(strings.TrimSpace(list[0]), pattern) {
			return strings.TrimSpace(list[1]), nil
		}
	}

	// Check whether the scanner encountered errors
	err = scanner.Err()
	if err != nil {
		return "", err
	}

	return "", errors.Wrapf(errdefs.ErrNotFound, "getCPUInfo for pattern: %s", pattern)
}

func getCPUVariant() string {
	if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
		// Windows/Darwin only supports v7 for ARM32 and v8 for ARM64 and so we can use
		// runtime.GOARCH to determine the variants
		var variant string
		switch runtime.GOARCH {
		case "arm64":
			variant = "v8"
		case "arm":
			variant = "v7"
		default:
			variant = "unknown"
		}

		return variant
	}

	variant, err := getCPUInfo("Cpu architecture")
	if err != nil {
		log.L.WithError(err).Error("failure getting variant")
		return ""
	}

	switch strings.ToLower(variant) {
	case "8", "aarch64":
		// special case: if running a 32-bit userspace on aarch64, the variant should be "v7"
		if runtime.GOARCH == "arm" {
			variant = "v7"
		} else {
			variant = "v8"
		}
	case "7", "7m", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)":
		variant = "v7"
	case "6", "6tej":
		variant = "v6"
	case "5", "5t", "5te", "5tej":
		variant = "v5"
	case "4", "4t":
		variant = "v4"
	case "3":
		variant = "v3"
	default:
		variant = "unknown"
	}

	return variant
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 - Michael Woolnough <michael.woolnough@gmail.com>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package minecraft

import (
	"github.com/MJKWoolnough/minecraft/nbt"
)

func yzx(x, y, z int32) uint32 {
	return (uint32(y&15) << 8) | (uint32(z&15) << 4) | uint32(x&15)
}

func getNibble(arr nbt.ByteArray, x, y, z int32) byte {
	coord := yzx(x, y, z)
	data := byte(arr[coord>>1])
	if coord&1 == 0 {
		data &= 15
	} else {
		data >>= 4
	}
	return data
}

func setNibble(arr nbt.ByteArray, x, y, z int32, data byte) {
	coord := yzx(x, y, z)
	oldData := byte(arr[coord>>1])
	if coord&1 == 0 {
		oldData = oldData&240 | data&15
	} else {
		oldData = oldData&15 | data<<4
	}
	arr[coord>>1] = int8(oldData)
	return
}

type section struct {
	section    *nbt.Compound
	blocks     *nbt.ByteArray
	add        *nbt.ByteArray
	data       *nbt.ByteArray
	blockLight *nbt.ByteArray
	skyLight   *nbt.ByteArray
}

func newSection(y int32) *section {
	s := new(section)
	s.blocks = nbt.NewByteArray(make([]int8, 4096))
	s.add = nbt.NewByteArray(make([]int8, 2048))
	s.data = nbt.NewByteArray(make([]int8, 2048))
	s.blockLight = nbt.NewByteArray(make([]int8, 2048))
	s.skyLight = nbt.NewByteArray(make([]int8, 2048))
	s.section = nbt.NewCompound([]nbt.Tag{
		nbt.NewTag("Blocks", s.blocks),
		nbt.NewTag("Add", s.add),
		nbt.NewTag("Data", s.data),
		nbt.NewTag("BlockLight", s.blockLight),
		nbt.NewTag("SkyLight", s.skyLight),
		nbt.NewTag("Y", nbt.NewByte(int8(y>>4))),
	})
	return s
}

func loadSection(c *nbt.Compound) (*section, error) {
	s := new(section)
	s.section = c
	blocks := c.Get("Blocks")
	if blocks.TagId() != nbt.Tag_ByteArray {
		return nil, &WrongTypeError{"Blocks", nbt.Tag_ByteArray, blocks.TagId()}
	}
	s.blocks = blocks.Data().(*nbt.ByteArray)
	if len(*s.blocks) != 4096 {
		return nil, &OOB{}
	}
	add := c.Get("Add")
	if add != nil {
		if add.TagId() != nbt.Tag_ByteArray {
			return nil, &WrongTypeError{"Add", nbt.Tag_ByteArray, add.TagId()}
		}
		s.add = add.Data().(*nbt.ByteArray)
	} else {
		s.add = nbt.NewByteArray(make([]int8, 2048))
		c.Set(nbt.NewTag("Add", s.add))
	}
	if len(*s.add) != 2048 {
		return nil, &OOB{}
	}
	data := c.Get("Data")
	if data.TagId() != nbt.Tag_ByteArray {
		return nil, &WrongTypeError{"Data", nbt.Tag_ByteArray, data.TagId()}
	}
	s.data = data.Data().(*nbt.ByteArray)
	if len(*s.data) != 2048 {
		return nil, &OOB{}
	}
	blockLight := c.Get("BlockLight")
	if blockLight.TagId() != nbt.Tag_ByteArray {
		return nil, &WrongTypeError{"BlockLight", nbt.Tag_ByteArray, blockLight.TagId()}
	}
	s.blockLight = blockLight.Data().(*nbt.ByteArray)
	if len(*s.blockLight) != 2048 {
		return nil, &OOB{}
	}
	skyLight := c.Get("SkyLight")
	if skyLight.TagId() != nbt.Tag_ByteArray {
		return nil, &WrongTypeError{"SkyLight", nbt.Tag_ByteArray, skyLight.TagId()}
	}
	s.skyLight = skyLight.Data().(*nbt.ByteArray)
	if len(*s.skyLight) != 2048 {
		return nil, &OOB{}
	}
	y := c.Get("Y")
	if y.TagId() != nbt.Tag_Byte {
		return nil, &WrongTypeError{"Y", nbt.Tag_Byte, y.TagId()}
	}
	return s, nil
}

func (s *section) GetBlock(x, y, z int32) *Block {
	return &Block{
		BlockId: uint16(getNibble(*s.add, x, y, z))<<8 | uint16(byte((*s.blocks)[yzx(x, y, z)])),
		Data:    getNibble(*s.data, x, y, z),
	}
}

func (s *section) SetBlock(x, y, z int32, b *Block) {
	(*s.blocks)[yzx(x, y, z)] = int8(b.BlockId & 255)
	setNibble(*s.add, x, y, z, byte(b.BlockId>>8))
	setNibble(*s.data, x, y, z, byte(b.Data))
}

func (s *section) SetY(y int32) {
	s.section.Set(nbt.NewTag("Y", nbt.NewByte(int8(y>>4))))
}

func (s *section) IsEmpty() bool {
	for _, b := range *s.blocks {
		if b != 0 {
			return false
		}
	}
	return true
}
<commit_msg>Added existence checking to several tags in sections<commit_after>// Copyright (c) 2013 - Michael Woolnough <michael.woolnough@gmail.com>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package minecraft

import (
	"github.com/MJKWoolnough/minecraft/nbt"
)

func yzx(x, y, z int32) uint32 {
	return (uint32(y&15) << 8) | (uint32(z&15) << 4) | uint32(x&15)
}

func getNibble(arr nbt.ByteArray, x, y, z int32) byte {
	coord := yzx(x, y, z)
	data := byte(arr[coord>>1])
	if coord&1 == 0 {
		data &= 15
	} else {
		data >>= 4
	}
	return data
}

func setNibble(arr nbt.ByteArray, x, y, z int32, data byte) {
	coord := yzx(x, y, z)
	oldData := byte(arr[coord>>1])
	if coord&1 == 0 {
		oldData = oldData&240 | data&15
	} else {
		oldData = oldData&15 | data<<4
	}
	arr[coord>>1] = int8(oldData)
	return
}

type section struct {
	section    *nbt.Compound
	blocks     *nbt.ByteArray
	add        *nbt.ByteArray
	data       *nbt.ByteArray
	blockLight *nbt.ByteArray
	skyLight   *nbt.ByteArray
}

func newSection(y int32) *section {
	s := new(section)
	s.blocks = nbt.NewByteArray(make([]int8, 4096))
	s.add = nbt.NewByteArray(make([]int8, 2048))
	s.data = nbt.NewByteArray(make([]int8, 2048))
	s.blockLight = nbt.NewByteArray(make([]int8, 2048))
	s.skyLight = nbt.NewByteArray(make([]int8, 2048))
	s.section = nbt.NewCompound([]nbt.Tag{
		nbt.NewTag("Blocks", s.blocks),
		nbt.NewTag("Add", s.add),
		nbt.NewTag("Data", s.data),
		nbt.NewTag("BlockLight", s.blockLight),
		nbt.NewTag("SkyLight", s.skyLight),
		nbt.NewTag("Y", nbt.NewByte(int8(y>>4))),
	})
	return s
}

func loadSection(c *nbt.Compound) (*section, error) {
	s := new(section)
	s.section = c
	blocks := c.Get("Blocks")
	if blocks == nil {
		return nil, &MissingTagError{"[SECTION]->Blocks"}
	} else if blocks.TagId() != nbt.Tag_ByteArray {
		return nil, &WrongTypeError{"Blocks", nbt.Tag_ByteArray, blocks.TagId()}
	}
	s.blocks = blocks.Data().(*nbt.ByteArray)
	if len(*s.blocks) != 4096 {
		return nil, &OOB{}
	}
	add := c.Get("Add")
	if add != nil {
		if add.TagId() != nbt.Tag_ByteArray {
			return nil, &WrongTypeError{"Add", nbt.Tag_ByteArray, add.TagId()}
		}
		s.add = add.Data().(*nbt.ByteArray)
	} else {
		s.add = nbt.NewByteArray(make([]int8, 2048))
		c.Set(nbt.NewTag("Add", s.add))
	}
	if len(*s.add) != 2048 {
		return nil, &OOB{}
	}
	data := c.Get("Data")
	if data == nil {
		return nil, &MissingTagError{"[SECTION]->Data"}
	} else if data.TagId() != nbt.Tag_ByteArray {
		return nil, &WrongTypeError{"Data", nbt.Tag_ByteArray, data.TagId()}
	}
	s.data = data.Data().(*nbt.ByteArray)
	if len(*s.data) != 2048 {
		return nil, &OOB{}
	}
	blockLight := c.Get("BlockLight")
	if blockLight == nil {
		return nil, &MissingTagError{"[SECTION]->BlockLight"}
	} else if blockLight.TagId() != nbt.Tag_ByteArray {
		return nil, &WrongTypeError{"BlockLight", nbt.Tag_ByteArray, blockLight.TagId()}
	}
	s.blockLight = blockLight.Data().(*nbt.ByteArray)
	if len(*s.blockLight) != 2048 {
		return nil, &OOB{}
	}
	skyLight := c.Get("SkyLight")
	if skyLight == nil {
		return nil, &MissingTagError{"[SECTION]->SkyLight"}
	} else if skyLight.TagId() != nbt.Tag_ByteArray {
		return nil, &WrongTypeError{"SkyLight", nbt.Tag_ByteArray, skyLight.TagId()}
	}
	s.skyLight = skyLight.Data().(*nbt.ByteArray)
	if len(*s.skyLight) != 2048 {
		return nil, &OOB{}
	}
	y := c.Get("Y")
	if blockLight == nil {
		return nil, &MissingTagError{"[SECTION]->Y"}
	} else if y.TagId() != nbt.Tag_Byte {
		return nil, &WrongTypeError{"Y", nbt.Tag_Byte, y.TagId()}
	}
	return s, nil
}

func (s *section) GetBlock(x, y, z int32) *Block {
	return &Block{
		BlockId: uint16(getNibble(*s.add, x, y, z))<<8 | uint16(byte((*s.blocks)[yzx(x, y, z)])),
		Data:    getNibble(*s.data, x, y, z),
	}
}

func (s *section) SetBlock(x, y, z int32, b *Block) {
	(*s.blocks)[yzx(x, y, z)] = int8(b.BlockId & 255)
	setNibble(*s.add, x, y, z, byte(b.BlockId>>8))
	setNibble(*s.data, x, y, z, byte(b.Data))
}

func (s *section) SetY(y int32) {
	s.section.Set(nbt.NewTag("Y", nbt.NewByte(int8(y>>4))))
}

func (s *section) IsEmpty() bool {
	for _, b := range *s.blocks {
		if b != 0 {
			return false
		}
	}
	return true
}
<|endoftext|>