<commit_before>package main

import (
	"image/color"
	"image/png"
	"os"
)

var (
	r1   = color.RGBA{153, 255, 255, 0}
	r5   = color.RGBA{103, 205, 255, 0}
	r10  = color.RGBA{30, 146, 255, 0}
	r20  = color.RGBA{38, 0, 255, 0}
	r30  = color.RGBA{248, 252, 0, 0}
	r50  = color.RGBA{255, 147, 0, 0}
	r80  = color.RGBA{252, 0, 0, 0}
	r100 = color.RGBA{154, 0, 121, 0}
)

func toInteger(rgba color.RGBA) (i int) {
	i = (int)(rgba.R)
	i <<= 8
	i += (int)(rgba.G)
	i <<= 8
	i += (int)(rgba.B)
	return
}

func toRGBA(c color.Color) (rgba color.RGBA) {
	r, g, b, _ := c.RGBA()
	rgba.R = (uint8)(r >> 8)
	rgba.G = (uint8)(g >> 8)
	rgba.B = (uint8)(b >> 8)
	rgba.A = 0
	//fmt.Printf("R:%d G:%d B:%d\n", rgba.R, rgba.G, rgba.B)
	return
}

func rgbToInteger(r, g, b uint8) (i int) {
	i = (int)(r)
	i <<= 8
	i += (int)(g)
	i <<= 8
	i += (int)(b)
	return
}

func limSet(v uint8, i int) uint8 {
	if (int)(v)+i < 0 {
		return 0
	} else if (int)(v)+i > 255 {
		return 255
	} else {
		return v + (uint8)(i)
	}
}

func appColSearch(c color.RGBA, rx color.RGBA) bool {
	r := rx.R
	g := rx.G
	b := rx.B
	ci := toInteger(c)

	for i := -APP_RANGE; i <= APP_RANGE; i++ {
		for j := -APP_RANGE; j <= APP_RANGE; j++ {
			for k := -APP_RANGE; k <= APP_RANGE; k++ {
				tr := limSet(r, i)
				tg := limSet(g, j)
				td := limSet(b, k)
				xi := rgbToInteger(tr, tg, td)
				if ci == xi {
					return true
				}
			}
		}
	}
	return false
}

func ColorCount() (int, error) {
	var w int
	f, err := os.Open(TRIM)
	if err != nil {
		return 0, err
	}
	defer f.Close()
	img, _ := png.Decode(f)
	bounds := img.Bounds()
	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			c := color.RGBAModel.Convert(img.At(x, y))
			rgba := toRGBA(c)
			if appColSearch(rgba, r1) {
				w += 1
			} else if appColSearch(rgba, r5) {
				w += 5
			} else if appColSearch(rgba, r10) {
				w += 10
			} else if appColSearch(rgba, r20) {
				w += 20
			} else if appColSearch(rgba, r30) {
				w += 30
			} else if appColSearch(rgba, r50) {
				w += 50
			} else if appColSearch(rgba, r100) {
				w += 100
			}
		}
	}

	return w, nil
}
<commit_msg>Fix Color Value<commit_after>package main

import (
	//"fmt"
	"image/color"
	"image/png"
	"os"
)

var (
	r1   = color.RGBA{153, 255, 255, 0}
	r5   = color.RGBA{103, 205, 255, 0}
	r10  = color.RGBA{30, 146, 255, 0}
	r20  = color.RGBA{0, 56, 255, 0}
	r30  = color.RGBA{250, 245, 0, 0}
	r50  = color.RGBA{255, 147, 0, 0}
	r80  = color.RGBA{252, 0, 0, 0}
	r100 = color.RGBA{154, 0, 121, 0}
)

func toInteger(rgba color.RGBA) (i int) {
	i = (int)(rgba.R)
	i <<= 8
	i += (int)(rgba.G)
	i <<= 8
	i += (int)(rgba.B)
	return
}

func toRGBA(c color.Color) (rgba color.RGBA) {
	r, g, b, _ := c.RGBA()
	rgba.R = (uint8)(r >> 8)
	rgba.G = (uint8)(g >> 8)
	rgba.B = (uint8)(b >> 8)
	rgba.A = 0
	//fmt.Printf("R:%d G:%d B:%d\n", rgba.R, rgba.G, rgba.B)
	return
}

func rgbToInteger(r, g, b uint8) (i int) {
	i = (int)(r)
	i <<= 8
	i += (int)(g)
	i <<= 8
	i += (int)(b)
	return
}

func limSet(v uint8, i int) uint8 {
	if (int)(v)+i < 0 {
		return 0
	} else if (int)(v)+i > 255 {
		return 255
	} else {
		return v + (uint8)(i)
	}
}

func appColSearch(c color.RGBA, rx color.RGBA) bool {
	r := rx.R
	g := rx.G
	b := rx.B
	ci := toInteger(c)
	//fmt.Printf("R:%d G:%d B:%d\n", c.R, c.G, c.B)

	for i := -APP_RANGE; i <= APP_RANGE; i++ {
		for j := -APP_RANGE; j <= APP_RANGE; j++ {
			for k := -APP_RANGE; k <= APP_RANGE; k++ {
				tr := limSet(r, i)
				tg := limSet(g, j)
				td := limSet(b, k)
				xi := rgbToInteger(tr, tg, td)
				if ci == xi {
					return true
				}
			}
		}
	}
	return false
}

func ColorCount() (int, error) {
	var w int
	f, err := os.Open(TRIM)
	if err != nil {
		return 0, err
	}
	defer f.Close()
	img, _ := png.Decode(f)
	bounds := img.Bounds()
	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			c := color.RGBAModel.Convert(img.At(x, y))
			rgba := toRGBA(c)
			if appColSearch(rgba, r1) {
				w += 1
			} else if appColSearch(rgba, r5) {
				w += 5
			} else if appColSearch(rgba, r10) {
				w += 10
			} else if appColSearch(rgba, r20) {
				w += 20
			} else if appColSearch(rgba, r30) {
				w += 30
			} else if appColSearch(rgba, r50) {
				w += 50
			} else if appColSearch(rgba, r100) {
				w += 100
			}
		}
	}

	return w, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"net/http"
	"os"
	"path/filepath"
	"strconv"

	"time"

	. "github.com/claudetech/loggo/default"
	"github.com/orcaman/concurrent-map"
)

var instances cmap.ConcurrentMap
var chunkPath string
var chunkSize int64
var chunkDirMaxSize int64

func init() {
	instances = cmap.New()
}

// Buffer is a buffered stream
type Buffer struct {
	numberOfInstances int
	client            *http.Client
	object            *APIObject
	tempDir           string
	preload           bool
	chunkDir          string
}

// GetBufferInstance gets a singleton instance of buffer
func GetBufferInstance(client *http.Client, object *APIObject) (*Buffer, error) {
	if !instances.Has(object.ObjectID) {
		i, err := newBuffer(client, object)
		if nil != err {
			return nil, err
		}

		instances.Set(object.ObjectID, i)
	}

	instance, ok := instances.Get(object.ObjectID)
	// if buffer allocation failed due to race conditions it will try to fetch a new one
	if !ok {
		i, err := GetBufferInstance(client, object)
		if nil != err {
			return nil, err
		}
		instance = i
	}
	instance.(*Buffer).numberOfInstances++
	return instance.(*Buffer), nil
}

// SetChunkPath sets the global chunk path
func SetChunkPath(path string) {
	chunkPath = path
}

// SetChunkSize sets the global chunk size
func SetChunkSize(size int64) {
	chunkSize = size
}

// SetChunkDirMaxSize sets the maximum size of the chunk directory
func SetChunkDirMaxSize(size int64) {
	chunkDirMaxSize = size
}

// NewBuffer creates a new buffer instance
func newBuffer(client *http.Client, object *APIObject) (*Buffer, error) {
	Log.Infof("Starting playback of %v", object.Name)
	Log.Debugf("Creating buffer for object %v", object.ObjectID)

	tempDir := filepath.Join(chunkPath, object.ObjectID)
	if err := os.MkdirAll(tempDir, 0777); nil != err {
		Log.Debugf("%v", err)
		return nil, fmt.Errorf("Could not create temp path for object %v", object.ObjectID)
	}

	if 0 == chunkSize {
		Log.Debugf("ChunkSize was 0, setting to default (5 MB)")
		chunkSize = 5 * 1024 * 1024
	}

	buffer := Buffer{
		numberOfInstances: 0,
		client:            client,
		object:            object,
		tempDir:           tempDir,
		preload:           true,
	}

	return &buffer, nil
}

// Close all handles
func (b *Buffer) Close() error {
	b.numberOfInstances--
	if 0 == b.numberOfInstances {
		Log.Infof("Stopping playback of %v", b.object.Name)
		Log.Debugf("Stop buffering for object %v", b.object.ObjectID)

		b.preload = false
		instances.Remove(b.object.ObjectID)
	}
	return nil
}

// ReadBytes on a specific location
func (b *Buffer) ReadBytes(start, size int64, isPreload bool) ([]byte, error) {
	fOffset := start % chunkSize
	offset := start - fOffset
	offsetEnd := offset + chunkSize

	Log.Debugf("Getting object %v - chunk %v - offset %v for %v bytes (is preload: %v)", b.object.ObjectID, strconv.Itoa(int(offset)), fOffset, size, isPreload)

	filename := filepath.Join(b.tempDir, strconv.Itoa(int(offset)))
	if f, err := os.Open(filename); nil == err {
		defer f.Close()
		buf := make([]byte, size)
		if n, err := f.ReadAt(buf, fOffset); n > 0 && (nil == err || io.EOF == err) {
			Log.Debugf("Found file %s bytes %v - %v in cache", filename, offset, offsetEnd)

			// update the last modified time for files that are often in use
			if err := os.Chtimes(filename, time.Now(), time.Now()); nil != err {
				Log.Warningf("Could not update last modified time for %v", filename)
			}

			return buf[:size], nil
		} else {
			Log.Debugf("Could not read file %s at %v - err : %v", filename, fOffset, err)
		}
	}

	go func() {
		if chunkDirMaxSize > 0 {
			if err := cleanChunkDir(chunkPath); nil != err {
				Log.Debugf("%v", err)
				Log.Warningf("Could not delete oldest chunk")
			}
		}
	}()

	Log.Debugf("Requesting object %v bytes %v - %v from API", b.object.ObjectID, offset, offsetEnd)
	req, err := http.NewRequest("GET", b.object.DownloadURL, nil)
	if nil != err {
		return nil, err
	}

	req.Header.Add("Range", fmt.Sprintf("bytes=%v-%v", offset, offsetEnd))

	Log.Tracef("Sending HTTP Request %v", req)

	res, err := b.client.Do(req)
	if nil != err {
		return nil, err
	}

	if res.StatusCode != 206 {
		return nil, fmt.Errorf("Wrong status code %v", res)
	}

	bytes, err := ioutil.ReadAll(res.Body)
	if nil != err {
		return nil, err
	}

	f, err := os.Create(filename)
	if nil != err {
		return nil, err
	}
	defer f.Close()

	_, err = f.Write(bytes)
	if nil != err {
		return nil, err
	}

	if !isPreload && b.preload && uint64(offsetEnd) < b.object.Size {
		go func() {
			b.ReadBytes(offsetEnd+1, size, true)
		}()
	}

	return bytes[fOffset:int64(math.Min(float64(fOffset+size), float64(len(bytes))))], nil
}

// cleanChunkDir checks if the chunk folder is grown to big and clears the oldest file if necessary
func cleanChunkDir(chunkPath string) error {
	chunkDirSize, err := dirSize(chunkPath)
	if nil != err {
		return err
	}

	if chunkDirSize+chunkSize > chunkDirMaxSize {
		if err := deleteOldestFile(chunkPath); nil != err {
			return err
		}
	}

	return nil
}

// deleteOldestFile deletes the oldest file in the directory
func deleteOldestFile(path string) error {
	var fpath string
	lastMod := time.Now()

	err := filepath.Walk(path, func(file string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			modTime := info.ModTime()
			if modTime.Before(lastMod) {
				lastMod = modTime
				fpath = file
			}
		}
		return err
	})

	os.Remove(fpath)

	return err
}

// dirSize gets the total directory size
func dirSize(path string) (int64, error) {
	var size int64
	err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			size += info.Size()
		}
		return err
	})
	return size, err
}
<commit_msg>minor optimizations<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"net/http"
	"os"
	"path/filepath"
	"strconv"

	"time"

	. "github.com/claudetech/loggo/default"
	"github.com/orcaman/concurrent-map"
)

var instances cmap.ConcurrentMap
var chunkPath string
var chunkSize int64
var chunkDirMaxSize int64

func init() {
	instances = cmap.New()
}

// Buffer is a buffered stream
type Buffer struct {
	numberOfInstances int
	client            *http.Client
	object            *APIObject
	tempDir           string
	preload           bool
	chunkDir          string
}

// GetBufferInstance gets a singleton instance of buffer
func GetBufferInstance(client *http.Client, object *APIObject) (*Buffer, error) {
	if !instances.Has(object.ObjectID) {
		i, err := newBuffer(client, object)
		if nil != err {
			return nil, err
		}

		instances.Set(object.ObjectID, i)
	}

	instance, ok := instances.Get(object.ObjectID)
	// if buffer allocation failed due to race conditions it will try to fetch a new one
	if !ok {
		i, err := GetBufferInstance(client, object)
		if nil != err {
			return nil, err
		}
		instance = i
	}
	instance.(*Buffer).numberOfInstances++
	return instance.(*Buffer), nil
}

// SetChunkPath sets the global chunk path
func SetChunkPath(path string) {
	chunkPath = path
}

// SetChunkSize sets the global chunk size
func SetChunkSize(size int64) {
	chunkSize = size
}

// SetChunkDirMaxSize sets the maximum size of the chunk directory
func SetChunkDirMaxSize(size int64) {
	chunkDirMaxSize = size
}

// NewBuffer creates a new buffer instance
func newBuffer(client *http.Client, object *APIObject) (*Buffer, error) {
	Log.Infof("Starting playback of %v", object.Name)
	Log.Debugf("Creating buffer for object %v", object.ObjectID)

	tempDir := filepath.Join(chunkPath, object.ObjectID)
	if err := os.MkdirAll(tempDir, 0777); nil != err {
		Log.Debugf("%v", err)
		return nil, fmt.Errorf("Could not create temp path for object %v", object.ObjectID)
	}

	if 0 == chunkSize {
		Log.Debugf("ChunkSize was 0, setting to default (5 MB)")
		chunkSize = 5 * 1024 * 1024
	}

	buffer := Buffer{
		numberOfInstances: 0,
		client:            client,
		object:            object,
		tempDir:           tempDir,
		preload:           true,
	}

	return &buffer, nil
}

// Close all handles
func (b *Buffer) Close() error {
	b.numberOfInstances--
	if 0 == b.numberOfInstances {
		Log.Infof("Stopping playback of %v", b.object.Name)
		Log.Debugf("Stop buffering for object %v", b.object.ObjectID)

		b.preload = false
		instances.Remove(b.object.ObjectID)
	}
	return nil
}

// ReadBytes on a specific location
func (b *Buffer) ReadBytes(start, size int64, isPreload bool) ([]byte, error) {
	fOffset := start % chunkSize
	offset := start - fOffset
	offsetEnd := offset + chunkSize

	Log.Debugf("Getting object %v - chunk %v - offset %v for %v bytes (is preload: %v)", b.object.ObjectID, strconv.Itoa(int(offset)), fOffset, size, isPreload)

	filename := filepath.Join(b.tempDir, strconv.Itoa(int(offset)))
	if f, err := os.Open(filename); nil == err {
		defer f.Close()

		buf := make([]byte, size)
		if n, err := f.ReadAt(buf, fOffset); n > 0 && (nil == err || io.EOF == err) {
			Log.Debugf("Found file %s bytes %v - %v in cache", filename, offset, offsetEnd)

			// update the last modified time for files that are often in use
			if err := os.Chtimes(filename, time.Now(), time.Now()); nil != err {
				Log.Warningf("Could not update last modified time for %v", filename)
			}

			return buf[:size], nil
		}

		Log.Debugf("%v", err)
		Log.Debugf("Could not read file %s at %v", filename, fOffset)
	}

	if chunkDirMaxSize > 0 {
		go func() {
			if err := cleanChunkDir(chunkPath); nil != err {
				Log.Debugf("%v", err)
				Log.Warningf("Could not delete oldest chunk")
			}
		}()
	}

	Log.Debugf("Requesting object %v bytes %v - %v from API", b.object.ObjectID, offset, offsetEnd)
	req, err := http.NewRequest("GET", b.object.DownloadURL, nil)
	if nil != err {
		return nil, err
	}

	req.Header.Add("Range", fmt.Sprintf("bytes=%v-%v", offset, offsetEnd))

	Log.Tracef("Sending HTTP Request %v", req)

	res, err := b.client.Do(req)
	if nil != err {
		return nil, err
	}

	if res.StatusCode != 206 {
		return nil, fmt.Errorf("Wrong status code %v", res)
	}

	bytes, err := ioutil.ReadAll(res.Body)
	if nil != err {
		return nil, err
	}

	f, err := os.Create(filename)
	if nil != err {
		return nil, err
	}
	defer f.Close()

	_, err = f.Write(bytes)
	if nil != err {
		return nil, err
	}

	if !isPreload && b.preload && uint64(offsetEnd) < b.object.Size {
		go func() {
			b.ReadBytes(offsetEnd+1, size, true)
		}()
	}

	return bytes[fOffset:int64(math.Min(float64(fOffset+size), float64(len(bytes))))], nil
}

// cleanChunkDir checks if the chunk folder is grown to big and clears the oldest file if necessary
func cleanChunkDir(chunkPath string) error {
	chunkDirSize, err := dirSize(chunkPath)
	if nil != err {
		return err
	}

	if chunkDirSize+chunkSize > chunkDirMaxSize {
		if err := deleteOldestFile(chunkPath); nil != err {
			return err
		}
	}

	return nil
}

// deleteOldestFile deletes the oldest file in the directory
func deleteOldestFile(path string) error {
	var fpath string
	lastMod := time.Now()

	err := filepath.Walk(path, func(file string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			modTime := info.ModTime()
			if modTime.Before(lastMod) {
				lastMod = modTime
				fpath = file
			}
		}
		return err
	})

	os.Remove(fpath)

	return err
}

// dirSize gets the total directory size
func dirSize(path string) (int64, error) {
	var size int64
	err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			size += info.Size()
		}
		return err
	})
	return size, err
}
<|endoftext|>


<commit_before>package registry

import (
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/spf13/cobra"
	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	kclient "k8s.io/kubernetes/pkg/client/unversioned"
	kclientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/runtime"
	kutil "k8s.io/kubernetes/pkg/util"

	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
	"github.com/openshift/origin/pkg/cmd/util/variable"
	configcmd "github.com/openshift/origin/pkg/config/cmd"
	dapi "github.com/openshift/origin/pkg/deploy/api"
	"github.com/openshift/origin/pkg/generate/app"
)

const (
	registryLong = `
Install or configure an integrated Docker registry

This command sets up a Docker registry integrated with your cluster to provide notifications when
images are pushed. With no arguments, the command will check for the existing registry service
called 'docker-registry' and try to create it. If you want to test whether the registry has
been created add the --dry-run flag and the command will exit with 1 if the registry does not
exist.

To run a highly available registry, you should be using a remote storage mechanism like an
object store (several are supported by the Docker registry). The default Docker registry image
is configured to accept configuration as environment variables - refer to the configuration file in
that image for more on setting up alternative storage. Once you've made those changes, you can
pass --replicas=2 or higher to ensure you have failover protection. The default registry setup
uses a local volume and the data will be lost if you delete the running pod.

NOTE: This command is intended to simplify the tasks of setting up a Docker registry in a new
  installation. Some configuration beyond this command is still required to make
  your registry persist data.`

	registryExample = `  # Check if default Docker registry ("docker-registry") has been created
  $ %[1]s %[2]s --dry-run

  # See what the registry will look like if created
  $ %[1]s %[2]s -o json --credentials=/path/to/registry-user.kubeconfig

  # Create a registry if it does not exist with two replicas
  $ %[1]s %[2]s --replicas=2 --credentials=/path/to/registry-user.kubeconfig

  # Use a different registry image and see the registry configuration
  $ %[1]s %[2]s -o yaml --images=myrepo/docker-registry:mytag --credentials=/path/to/registry-user.kubeconfig`
)

type RegistryConfig struct {
	Type           string
	ImageTemplate  variable.ImageTemplate
	Ports          string
	Replicas       int
	Labels         string
	Volume         string
	HostMount      string
	DryRun         bool
	Credentials    string
	Selector       string
	ServiceAccount string

	// TODO: accept environment values.
}

var errExit = fmt.Errorf("exit")

const (
	defaultLabel = "docker-registry=default"
	defaultPort  = 5000
	/* TODO: `/healthz` has been deprecated by `/`; keep it temporarily for backwards compatibility until
	 * a next major release with a strict requirement on newer registry image
	 * NOTE that `/` is supported since ose `v3.1.1.0`
	 * To make the transition safe, we could change `HTTPGetAction` to an `ExecAction` which would first curl
	 * `/` and then fallback to `/healthz` if unreachable. Reachable endpoint could be cached on tmpfs inside
	 * a container and be used on subsequent checks. */
	healthzRoute               = "/healthz"
	healthzRouteTimeoutSeconds = 5
)

// NewCmdRegistry implements the OpenShift cli registry command
func NewCmdRegistry(f *clientcmd.Factory, parentName, name string, out io.Writer) *cobra.Command {
	cfg := &RegistryConfig{
		ImageTemplate: variable.NewDefaultImageTemplate(),

		Labels:   defaultLabel,
		Ports:    strconv.Itoa(defaultPort),
		Volume:   "/registry",
		Replicas: 1,
	}

	cmd := &cobra.Command{
		Use:     name,
		Short:   "Install the integrated Docker registry",
		Long:    registryLong,
		Example: fmt.Sprintf(registryExample, parentName, name),
		Run: func(cmd *cobra.Command, args []string) {
			err := RunCmdRegistry(f, cmd, out, cfg, args)
			if err != errExit {
				cmdutil.CheckErr(err)
			} else {
				os.Exit(1)
			}
		},
	}

	cmd.Flags().StringVar(&cfg.Type, "type", "docker-registry", "The registry image to use - if you specify --images this flag may be ignored.")
	cmd.Flags().StringVar(&cfg.ImageTemplate.Format, "images", cfg.ImageTemplate.Format, "The image to base this registry on - ${component} will be replaced with --type")
	cmd.Flags().BoolVar(&cfg.ImageTemplate.Latest, "latest-images", cfg.ImageTemplate.Latest, "If true, attempt to use the latest image for the registry instead of the latest release.")
	cmd.Flags().StringVar(&cfg.Ports, "ports", cfg.Ports, fmt.Sprintf("A comma delimited list of ports or port pairs to expose on the registry pod. The default is set for %d.", defaultPort))
	cmd.Flags().IntVar(&cfg.Replicas, "replicas", cfg.Replicas, "The replication factor of the registry; commonly 2 when high availability is desired.")
	cmd.Flags().StringVar(&cfg.Labels, "labels", cfg.Labels, "A set of labels to uniquely identify the registry and its components.")
	cmd.Flags().StringVar(&cfg.Volume, "volume", cfg.Volume, "The volume path to use for registry storage; defaults to /registry which is the default for origin-docker-registry.")
	cmd.Flags().StringVar(&cfg.HostMount, "mount-host", cfg.HostMount, "If set, the registry volume will be created as a host-mount at this path.")
	cmd.Flags().BoolVar(&cfg.DryRun, "dry-run", cfg.DryRun, "Check if the registry exists instead of creating.")
	cmd.Flags().Bool("create", false, "deprecated; this is now the default behavior")
	cmd.Flags().StringVar(&cfg.Credentials, "credentials", "", "Path to a .kubeconfig file that will contain the credentials the registry should use to contact the master.")
	cmd.Flags().StringVar(&cfg.ServiceAccount, "service-account", cfg.ServiceAccount, "Name of the service account to use to run the registry pod.")
	cmd.Flags().StringVar(&cfg.Selector, "selector", cfg.Selector, "Selector used to filter nodes on deployment. Used to run registries on a specific set of nodes.")

	// autocompletion hints
	cmd.MarkFlagFilename("credentials", "kubeconfig")

	cmdutil.AddPrinterFlags(cmd)

	return cmd
}

// RunCmdRegistry contains all the necessary functionality for the OpenShift cli registry command
func RunCmdRegistry(f *clientcmd.Factory, cmd *cobra.Command, out io.Writer, cfg *RegistryConfig, args []string) error {
	var name string
	switch len(args) {
	case 0:
		name = "docker-registry"
	default:
		return cmdutil.UsageError(cmd, "No arguments are allowed to this command")
	}

	ports, err := app.ContainerPortsFromString(cfg.Ports)
	if err != nil {
		return err
	}

	label := map[string]string{
		"docker-registry": "default",
	}
	if cfg.Labels != defaultLabel {
		valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Labels, ","))
		if err != nil {
			return err
		}
		if len(remove) > 0 {
			return cmdutil.UsageError(cmd, "You may not pass negative labels in %q", cfg.Labels)
		}
		label = valid
	}

	nodeSelector := map[string]string{}
	if len(cfg.Selector) > 0 {
		valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Selector, ","))
		if err != nil {
			return err
		}
		if len(remove) > 0 {
			return cmdutil.UsageError(cmd, "You may not pass negative labels in selector %q", cfg.Selector)
		}
		nodeSelector = valid
	}

	image := cfg.ImageTemplate.ExpandOrDie(cfg.Type)

	namespace, _, err := f.OpenShiftClientConfig.Namespace()
	if err != nil {
		return fmt.Errorf("error getting client: %v", err)
	}
	_, kClient, err := f.Clients()
	if err != nil {
		return fmt.Errorf("error getting client: %v", err)
	}

	_, output, err := cmdutil.PrinterForCommand(cmd)
	if err != nil {
		return fmt.Errorf("unable to configure printer: %v", err)
	}

	generate := output
	if !generate {
		_, err = kClient.Services(namespace).Get(name)
		if err != nil {
			if !errors.IsNotFound(err) {
				return fmt.Errorf("can't check for existing docker-registry %q: %v", name, err)
			}
			generate = true
		}
	}

	if generate {
		if cfg.DryRun && !output {
			return fmt.Errorf("docker-registry %q does not exist (no service).", name)
		}

		// create new registry
		if len(cfg.Credentials) == 0 {
			return fmt.Errorf("registry does not exist; you must specify a .kubeconfig file path containing credentials for connecting the registry to the master with --credentials")
		}
		clientConfigLoadingRules := &kclientcmd.ClientConfigLoadingRules{ExplicitPath: cfg.Credentials}
		credentials, err := clientConfigLoadingRules.Load()
		if err != nil {
			return fmt.Errorf("registry does not exist; the provided credentials %q could not be loaded: %v", cfg.Credentials, err)
		}
		config, err := kclientcmd.NewDefaultClientConfig(*credentials, &kclientcmd.ConfigOverrides{}).ClientConfig()
		if err != nil {
			return fmt.Errorf("registry does not exist; the provided credentials %q could not be used: %v", cfg.Credentials, err)
		}
		if err := kclient.LoadTLSFiles(config); err != nil {
			return fmt.Errorf("registry does not exist; the provided credentials %q could not load certificate info: %v", cfg.Credentials, err)
		}
		insecure := "false"
		if config.Insecure {
			insecure = "true"
		} else {
			if len(config.KeyData) == 0 || len(config.CertData) == 0 {
				return fmt.Errorf("registry does not exist; the provided credentials %q are missing the client certificate and/or key", cfg.Credentials)
			}
		}

		env := app.Environment{
			"OPENSHIFT_MASTER":    config.Host,
			"OPENSHIFT_CA_DATA":   string(config.CAData),
			"OPENSHIFT_KEY_DATA":  string(config.KeyData),
			"OPENSHIFT_CERT_DATA": string(config.CertData),
			"OPENSHIFT_INSECURE":  insecure,
		}

		healthzPort := defaultPort
		if len(ports) > 0 {
			healthzPort = ports[0].ContainerPort
		}
		livenessProbe := generateLivenessProbeConfig(healthzPort)
		readinessProbe := generateReadinessProbeConfig(healthzPort)

		mountHost := len(cfg.HostMount) > 0
		podTemplate := &kapi.PodTemplateSpec{
			ObjectMeta: kapi.ObjectMeta{Labels: label},
			Spec: kapi.PodSpec{
				ServiceAccountName: cfg.ServiceAccount,
				NodeSelector:       nodeSelector,
				Containers: []kapi.Container{
					{
						Name:  "registry",
						Image: image,
						Ports: ports,
						Env:   env.List(),
						VolumeMounts: []kapi.VolumeMount{
							{
								Name:      "registry-storage",
								MountPath: cfg.Volume,
							},
						},
						SecurityContext: &kapi.SecurityContext{
							Privileged: &mountHost,
						},
						LivenessProbe:  livenessProbe,
						ReadinessProbe: readinessProbe,
					},
				},
				Volumes: []kapi.Volume{
					{
						Name:         "registry-storage",
						VolumeSource: kapi.VolumeSource{},
					},
				},
			},
		}
		if mountHost {
			podTemplate.Spec.Volumes[0].HostPath = &kapi.HostPathVolumeSource{Path: cfg.HostMount}
		} else {
			podTemplate.Spec.Volumes[0].EmptyDir = &kapi.EmptyDirVolumeSource{}
		}

		objects := []runtime.Object{
			&dapi.DeploymentConfig{
				ObjectMeta: kapi.ObjectMeta{
					Name:   name,
					Labels: label,
				},
				Spec: dapi.DeploymentConfigSpec{
					Replicas: cfg.Replicas,
					Selector: label,
					Triggers: []dapi.DeploymentTriggerPolicy{
						{Type: dapi.DeploymentTriggerOnConfigChange},
					},
					Template: podTemplate,
				},
			},
		}
		objects = app.AddServices(objects, true)

		// Set registry service's sessionAffinity to ClientIP to prevent push
		// failures due to a use of poorly consistent storage shared by
		// multiple replicas.
		for _, obj := range objects {
			switch t := obj.(type) {
			case *kapi.Service:
				t.Spec.SessionAffinity = kapi.ServiceAffinityClientIP
			}
		}

		// TODO: label all created objects with the same label
		list := &kapi.List{Items: objects}

		if output {
			if err := f.PrintObject(cmd, list, out); err != nil {
				return fmt.Errorf("unable to print object: %v", err)
			}
			return nil
		}

		mapper, typer := f.Factory.Object()
		bulk := configcmd.Bulk{
			Mapper:            mapper,
			Typer:             typer,
			RESTClientFactory: f.Factory.RESTClient,

			After: configcmd.NewPrintNameOrErrorAfter(mapper, cmdutil.GetFlagString(cmd, "output") == "name", "created", out, cmd.Out()),
		}
		if errs := bulk.Create(list, namespace); len(errs) != 0 {
			return errExit
		}
		return nil
	}

	fmt.Fprintf(out, "Docker registry %q service exists\n", name)
	return nil
}

func generateLivenessProbeConfig(port int) *kapi.Probe {
	return &kapi.Probe{
		InitialDelaySeconds: 10,
		TimeoutSeconds:      healthzRouteTimeoutSeconds,
		Handler: kapi.Handler{
			HTTPGet: &kapi.HTTPGetAction{
				Path: healthzRoute,
				Port: kutil.IntOrString{
					IntVal: port,
				},
			},
		},
	}
}

func generateReadinessProbeConfig(port int) *kapi.Probe {
	return &kapi.Probe{
		TimeoutSeconds: healthzRouteTimeoutSeconds,
		Handler: kapi.Handler{
			HTTPGet: &kapi.HTTPGetAction{
				Path: healthzRoute,
				Port: kutil.IntOrString{
					IntVal: port,
				},
			},
		},
	}
}
<commit_msg>Set REGISTRY_HTTP_ADDR to first specified port<commit_after>package registry

import (
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/spf13/cobra"
	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	kclient "k8s.io/kubernetes/pkg/client/unversioned"
	kclientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/runtime"
	kutil "k8s.io/kubernetes/pkg/util"

	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
	"github.com/openshift/origin/pkg/cmd/util/variable"
	configcmd "github.com/openshift/origin/pkg/config/cmd"
	dapi "github.com/openshift/origin/pkg/deploy/api"
	"github.com/openshift/origin/pkg/generate/app"
)

const (
	registryLong = `
Install or configure an integrated Docker registry

This command sets up a Docker registry integrated with your cluster to provide notifications when
images are pushed. With no arguments, the command will check for the existing registry service
called 'docker-registry' and try to create it. If you want to test whether the registry has
been created add the --dry-run flag and the command will exit with 1 if the registry does not
exist.

To run a highly available registry, you should be using a remote storage mechanism like an
object store (several are supported by the Docker registry). The default Docker registry image
is configured to accept configuration as environment variables - refer to the configuration file in
that image for more on setting up alternative storage. Once you've made those changes, you can
pass --replicas=2 or higher to ensure you have failover protection. The default registry setup
uses a local volume and the data will be lost if you delete the running pod.

If multiple ports are specified using the option --ports, the first specified port will be
chosen for use as the REGISTRY_HTTP_ADDR and will be passed to Docker registry.

NOTE: This command is intended to simplify the tasks of setting up a Docker registry in a new
  installation. Some configuration beyond this command is still required to make
  your registry persist data.`

	registryExample = `  # Check if default Docker registry ("docker-registry") has been created
  $ %[1]s %[2]s --dry-run

  # See what the registry will look like if created
  $ %[1]s %[2]s -o json --credentials=/path/to/registry-user.kubeconfig

  # Create a registry if it does not exist with two replicas
  $ %[1]s %[2]s --replicas=2 --credentials=/path/to/registry-user.kubeconfig

  # Use a different registry image and see the registry configuration
  $ %[1]s %[2]s -o yaml --images=myrepo/docker-registry:mytag --credentials=/path/to/registry-user.kubeconfig`
)

type RegistryConfig struct {
	Type           string
	ImageTemplate  variable.ImageTemplate
	Ports          string
	Replicas       int
	Labels         string
	Volume         string
	HostMount      string
	DryRun         bool
	Credentials    string
	Selector       string
	ServiceAccount string

	// TODO: accept environment values.
}

var errExit = fmt.Errorf("exit")

const (
	defaultLabel = "docker-registry=default"
	defaultPort  = 5000
	/* TODO: `/healthz` has been deprecated by `/`; keep it temporarily for backwards compatibility until
	 * a next major release with a strict requirement on newer registry image
	 * NOTE that `/` is supported since ose `v3.1.1.0`
	 * To make the transition safe, we could change `HTTPGetAction` to an `ExecAction` which would first curl
	 * `/` and then fallback to `/healthz` if unreachable. Reachable endpoint could be cached on tmpfs inside
	 * a container and be used on subsequent checks. */
	healthzRoute               = "/healthz"
	healthzRouteTimeoutSeconds = 5
)

// NewCmdRegistry implements the OpenShift cli registry command
func NewCmdRegistry(f *clientcmd.Factory, parentName, name string, out io.Writer) *cobra.Command {
	cfg := &RegistryConfig{
		ImageTemplate: variable.NewDefaultImageTemplate(),

		Labels:   defaultLabel,
		Ports:    strconv.Itoa(defaultPort),
		Volume:   "/registry",
		Replicas: 1,
	}

	cmd := &cobra.Command{
		Use:     name,
		Short:   "Install the integrated Docker registry",
		Long:    registryLong,
		Example: fmt.Sprintf(registryExample, parentName, name),
		Run: func(cmd *cobra.Command, args []string) {
			err := RunCmdRegistry(f, cmd, out, cfg, args)
			if err != errExit {
				cmdutil.CheckErr(err)
			} else {
				os.Exit(1)
			}
		},
	}

	cmd.Flags().StringVar(&cfg.Type, "type", "docker-registry", "The registry image to use - if you specify --images this flag may be ignored.")
	cmd.Flags().StringVar(&cfg.ImageTemplate.Format, "images", cfg.ImageTemplate.Format, "The image to base this registry on - ${component} will be replaced with --type")
	cmd.Flags().BoolVar(&cfg.ImageTemplate.Latest, "latest-images", cfg.ImageTemplate.Latest, "If true, attempt to use the latest image for the registry instead of the latest release.")
	cmd.Flags().StringVar(&cfg.Ports, "ports", cfg.Ports, fmt.Sprintf("A comma delimited list of ports or port pairs to expose on the registry pod. The default is set for %d.", defaultPort))
	cmd.Flags().IntVar(&cfg.Replicas, "replicas", cfg.Replicas, "The replication factor of the registry; commonly 2 when high availability is desired.")
	cmd.Flags().StringVar(&cfg.Labels, "labels", cfg.Labels, "A set of labels to uniquely identify the registry and its components.")
	cmd.Flags().StringVar(&cfg.Volume, "volume", cfg.Volume, "The volume path to use for registry storage; defaults to /registry which is the default for origin-docker-registry.")
	cmd.Flags().StringVar(&cfg.HostMount, "mount-host", cfg.HostMount, "If set, the registry volume will be created as a host-mount at this path.")
	cmd.Flags().BoolVar(&cfg.DryRun, "dry-run", cfg.DryRun, "Check if the registry exists instead of creating.")
	cmd.Flags().Bool("create", false, "deprecated; this is now the default behavior")
	cmd.Flags().StringVar(&cfg.Credentials, "credentials", "", "Path to a .kubeconfig file that will contain the credentials the registry should use to contact the master.")
	cmd.Flags().StringVar(&cfg.ServiceAccount, "service-account", cfg.ServiceAccount, "Name of the service account to use to run the registry pod.")
	cmd.Flags().StringVar(&cfg.Selector, "selector", cfg.Selector, "Selector used to filter nodes on deployment. Used to run registries on a specific set of nodes.")

	// autocompletion hints
	cmd.MarkFlagFilename("credentials", "kubeconfig")

	cmdutil.AddPrinterFlags(cmd)

	return cmd
}

// RunCmdRegistry contains all the necessary functionality for the OpenShift cli registry command
func RunCmdRegistry(f *clientcmd.Factory, cmd *cobra.Command, out io.Writer, cfg *RegistryConfig, args []string) error {
	var name string
	switch len(args) {
	case 0:
		name = "docker-registry"
	default:
		return cmdutil.UsageError(cmd, "No arguments are allowed to this command")
	}

	ports, err := app.ContainerPortsFromString(cfg.Ports)
	if err != nil {
		return err
	}

	label := map[string]string{
		"docker-registry": "default",
	}
	if cfg.Labels != defaultLabel {
		valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Labels, ","))
		if err != nil {
			return err
		}
		if len(remove) > 0 {
			return cmdutil.UsageError(cmd, "You may not pass negative labels in %q", cfg.Labels)
		}
		label = valid
	}

	nodeSelector := map[string]string{}
	if len(cfg.Selector) > 0 {
		valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Selector, ","))
		if err != nil {
			return err
		}
		if len(remove) > 0 {
			return cmdutil.UsageError(cmd, "You may not pass negative labels in selector %q", cfg.Selector)
		}
		nodeSelector = valid
	}

	image := cfg.ImageTemplate.ExpandOrDie(cfg.Type)

	namespace, _, err := f.OpenShiftClientConfig.Namespace()
	if err != nil {
		return fmt.Errorf("error getting client: %v", err)
	}
	_, kClient, err := f.Clients()
	if err != nil {
		return fmt.Errorf("error getting client: %v", err)
	}

	_, output, err := cmdutil.PrinterForCommand(cmd)
	if err != nil {
		return fmt.Errorf("unable to configure printer: %v", err)
	}

	generate := output
	if !generate {
		_, err = kClient.Services(namespace).Get(name)
		if err != nil {
			if !errors.IsNotFound(err) {
				return fmt.Errorf("can't check for existing docker-registry %q: %v", name, err)
			}
			generate = true
		}
	}

	if generate {
		if cfg.DryRun && !output {
			return fmt.Errorf("docker-registry %q does not exist (no service).", name)
		}

		// create new registry
		if len(cfg.Credentials) == 0 {
			return fmt.Errorf("registry does not exist; you must specify a .kubeconfig file path containing credentials for connecting the registry to the master with --credentials")
		}
		clientConfigLoadingRules := &kclientcmd.ClientConfigLoadingRules{ExplicitPath: cfg.Credentials}
		credentials, err := clientConfigLoadingRules.Load()
		if err != nil {
			return fmt.Errorf("registry does not exist; the provided credentials %q could not be loaded: %v", cfg.Credentials, err)
		}
		config, err := kclientcmd.NewDefaultClientConfig(*credentials, &kclientcmd.ConfigOverrides{}).ClientConfig()
		if err != nil {
			return fmt.Errorf("registry does not exist; the provided credentials %q could not be used: %v", cfg.Credentials, err)
		}
		if err := kclient.LoadTLSFiles(config); err != nil {
			return fmt.Errorf("registry does not exist; the provided credentials %q could not load certificate info: %v", cfg.Credentials, err)
		}
		insecure := "false"
		if config.Insecure {
			insecure = "true"
		} else {
			if len(config.KeyData) == 0 || len(config.CertData) == 0 {
				return fmt.Errorf("registry does not exist; the provided credentials %q are missing the client certificate and/or key", cfg.Credentials)
			}
		}

		env := app.Environment{
			"OPENSHIFT_MASTER":    config.Host,
			"OPENSHIFT_CA_DATA":   string(config.CAData),
			"OPENSHIFT_KEY_DATA":  string(config.KeyData),
			"OPENSHIFT_CERT_DATA": string(config.CertData),
			"OPENSHIFT_INSECURE":  insecure,
		}

		healthzPort := defaultPort
		if len(ports) > 0 {
			healthzPort = ports[0].ContainerPort
			env["REGISTRY_HTTP_ADDR"] = fmt.Sprintf(":%d", healthzPort)
			env["REGISTRY_HTTP_NET"] = "tcp"
		}
		livenessProbe := generateLivenessProbeConfig(healthzPort)
		readinessProbe := generateReadinessProbeConfig(healthzPort)

		mountHost := len(cfg.HostMount) > 0
		podTemplate := &kapi.PodTemplateSpec{
			ObjectMeta: kapi.ObjectMeta{Labels: label},
			Spec: kapi.PodSpec{
				ServiceAccountName: cfg.ServiceAccount,
				NodeSelector:       nodeSelector,
				Containers: []kapi.Container{
					{
						Name:  "registry",
						Image: image,
						Ports: ports,
						Env:   env.List(),
						VolumeMounts: []kapi.VolumeMount{
							{
								Name:      "registry-storage",
								MountPath: cfg.Volume,
							},
						},
						SecurityContext: &kapi.SecurityContext{
							Privileged: &mountHost,
						},
						LivenessProbe:  livenessProbe,
						ReadinessProbe: readinessProbe,
					},
				},
				Volumes: []kapi.Volume{
					{
						Name:         "registry-storage",
						VolumeSource: kapi.VolumeSource{},
					},
				},
			},
		}
		if mountHost {
			podTemplate.Spec.Volumes[0].HostPath = &kapi.HostPathVolumeSource{Path: cfg.HostMount}
		} else {
			podTemplate.Spec.Volumes[0].EmptyDir = &kapi.EmptyDirVolumeSource{}
		}

		objects := []runtime.Object{
			&dapi.DeploymentConfig{
				ObjectMeta: kapi.ObjectMeta{
					Name:   name,
					Labels: label,
				},
				Spec: dapi.DeploymentConfigSpec{
					Replicas: cfg.Replicas,
					Selector: label,
					Triggers: []dapi.DeploymentTriggerPolicy{
						{Type: dapi.DeploymentTriggerOnConfigChange},
					},
					Template: podTemplate,
				},
			},
		}
		objects = app.AddServices(objects, true)

		// Set registry service's sessionAffinity to ClientIP to prevent push
		// failures due to a use of poorly consistent storage shared by
		// multiple replicas.
		for _, obj := range objects {
			switch t := obj.(type) {
			case *kapi.Service:
				t.Spec.SessionAffinity = kapi.ServiceAffinityClientIP
			}
		}

		// TODO: label all created objects with the same label
		list := &kapi.List{Items: objects}

		if output {
			if err := f.PrintObject(cmd, list, out); err != nil {
				return fmt.Errorf("unable to print object: %v", err)
			}
			return nil
		}

		mapper, typer := f.Factory.Object()
		bulk := configcmd.Bulk{
			Mapper:            mapper,
			Typer:             typer,
			RESTClientFactory: f.Factory.RESTClient,

			After: configcmd.NewPrintNameOrErrorAfter(mapper, cmdutil.GetFlagString(cmd, "output") == "name", "created", out, cmd.Out()),
		}
		if errs := bulk.Create(list, namespace); len(errs) != 0 {
			return errExit
		}
		return nil
	}

	fmt.Fprintf(out, "Docker registry %q service exists\n", name)
	return nil
}

func generateLivenessProbeConfig(port int) *kapi.Probe {
	return &kapi.Probe{
		InitialDelaySeconds: 10,
		TimeoutSeconds:      healthzRouteTimeoutSeconds,
		Handler: kapi.Handler{
			HTTPGet: &kapi.HTTPGetAction{
				Path: healthzRoute,
				Port: kutil.IntOrString{
					IntVal: port,
				},
			},
		},
	}
}

func generateReadinessProbeConfig(port int) *kapi.Probe {
	return &kapi.Probe{
		TimeoutSeconds: healthzRouteTimeoutSeconds,
		Handler: kapi.Handler{
			HTTPGet: &kapi.HTTPGetAction{
				Path: healthzRoute,
				Port: kutil.IntOrString{
					IntVal: port,
				},
			},
		},
	}
}
<|endoftext|>


<commit_before>package humanize

import (
	"math/big"
	"testing"
)

func TestCommas(t *testing.T) {
	testList{
		{"0", Comma(0), "0"},
		{"10", Comma(10), "10"},
		{"100", Comma(100), "100"},
		{"1,000", Comma(1000), "1,000"},
		{"10,000", Comma(10000), "10,000"},
		{"100,000", Comma(100000), "100,000"},
		{"10,000,000", Comma(10000000), "10,000,000"},
		{"10,100,000", Comma(10100000), "10,100,000"},
		{"10,010,000", Comma(10010000), "10,010,000"},
		{"10,001,000", Comma(10001000), "10,001,000"},
		{"123,456,789", Comma(123456789), "123,456,789"},
		{"maxint", Comma(9.223372e+18), "9,223,372,000,000,000,000"},
		{"minint", Comma(-9.223372e+18), "-9,223,372,000,000,000,000"},
		{"-123,456,789", Comma(-123456789), "-123,456,789"},
		{"-10,100,000", Comma(-10100000), "-10,100,000"},
		{"-10,010,000", Comma(-10010000), "-10,010,000"},
		{"-10,001,000", Comma(-10001000), "-10,001,000"},
		{"-10,000,000", Comma(-10000000), "-10,000,000"},
		{"-100,000", Comma(-100000), "-100,000"},
		{"-10,000", Comma(-10000), "-10,000"},
		{"-1,000", Comma(-1000), "-1,000"},
		{"-100", Comma(-100), "-100"},
		{"-10", Comma(-10), "-10"},
	}.validate(t)
}

func BenchmarkCommas(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Comma(1000000000)
		Comma(1234567890)
	}
}

func bigComma(i int64) string {
	return BigComma(big.NewInt(i))
}

func TestBigCommas(t *testing.T) {
	testList{
		{"0", bigComma(0), "0"},
		{"10", bigComma(10), "10"},
		{"100", bigComma(100), "100"},
		{"1,000", bigComma(1000), "1,000"},
		{"10,000", bigComma(10000), "10,000"},
		{"100,000", bigComma(100000), "100,000"},
		{"10,000,000", bigComma(10000000), "10,000,000"},
		{"10,100,000", bigComma(10100000), "10,100,000"},
		{"10,010,000", bigComma(10010000), "10,010,000"},
		{"10,001,000", bigComma(10001000), "10,001,000"},
		{"123,456,789", bigComma(123456789), "123,456,789"},
		{"maxint", bigComma(9.223372e+18), "9,223,372,000,000,000,000"},
		{"minint", bigComma(-9.223372e+18), "-9,223,372,000,000,000,000"},
		{"-123,456,789", bigComma(-123456789), "-123,456,789"},
		{"-10,100,000", bigComma(-10100000), "-10,100,000"},
		{"-10,010,000", bigComma(-10010000), "-10,010,000"},
		{"-10,001,000", bigComma(-10001000), "-10,001,000"},
		{"-10,000,000", bigComma(-10000000), "-10,000,000"},
		{"-100,000", bigComma(-100000), "-100,000"},
		{"-10,000", bigComma(-10000), "-10,000"},
		{"-1,000", bigComma(-1000), "-1,000"},
		{"-100", bigComma(-100), "-100"},
		{"-10", bigComma(-10), "-10"},
	}.validate(t)
}

func TestVeryBigCommas(t *testing.T) {
	tests := []struct{ in, exp string }{
		{
			"84889279597249724975972597249849757294578485",
			"84,889,279,597,249,724,975,972,597,249,849,757,294,578,485",
		},
		{
			"-84889279597249724975972597249849757294578485",
			"-84,889,279,597,249,724,975,972,597,249,849,757,294,578,485",
		},
	}
	for _, test := range tests {
		n, _ := (&big.Int{}).SetString(test.in, 10)
		got := BigComma(n)
		if test.exp != got {
			t.Errorf("Expected %q, got %q", test.exp, got)
		}
	}
}
<commit_msg>Benchmark BigComma<commit_after>package humanize

import (
	"math/big"
	"testing"
)

func TestCommas(t *testing.T) {
	testList{
		{"0", Comma(0), "0"},
		{"10", Comma(10), "10"},
		{"100", Comma(100), "100"},
		{"1,000", Comma(1000), "1,000"},
		{"10,000", Comma(10000), "10,000"},
		{"100,000", Comma(100000), "100,000"},
		{"10,000,000", Comma(10000000), "10,000,000"},
		{"10,100,000", Comma(10100000), "10,100,000"},
		{"10,010,000", Comma(10010000), "10,010,000"},
		{"10,001,000", Comma(10001000), "10,001,000"},
		{"123,456,789", Comma(123456789), "123,456,789"},
		{"maxint", Comma(9.223372e+18), "9,223,372,000,000,000,000"},
		{"minint", Comma(-9.223372e+18), "-9,223,372,000,000,000,000"},
		{"-123,456,789", Comma(-123456789), "-123,456,789"},
		{"-10,100,000", Comma(-10100000), "-10,100,000"},
		{"-10,010,000", Comma(-10010000), "-10,010,000"},
		{"-10,001,000", Comma(-10001000), "-10,001,000"},
		{"-10,000,000", Comma(-10000000), "-10,000,000"},
		{"-100,000", Comma(-100000), "-100,000"},
		{"-10,000", Comma(-10000), "-10,000"},
		{"-1,000", Comma(-1000), "-1,000"},
		{"-100", Comma(-100), "-100"},
		{"-10", Comma(-10), "-10"},
	}.validate(t)
}

func BenchmarkCommas(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Comma(1234567890)
	}
}

func BenchmarkBigCommas(b *testing.B) {
	for i := 0; i < b.N; i++ {
		BigComma(big.NewInt(1234567890))
	}
}

func bigComma(i int64) string {
	return BigComma(big.NewInt(i))
}

func TestBigCommas(t *testing.T) {
	testList{
		{"0", bigComma(0), "0"},
		{"10", bigComma(10), "10"},
		{"100", bigComma(100), "100"},
		{"1,000", bigComma(1000), "1,000"},
		{"10,000", bigComma(10000), "10,000"},
		{"100,000", bigComma(100000), "100,000"},
		{"10,000,000", bigComma(10000000), "10,000,000"},
		{"10,100,000", bigComma(10100000), "10,100,000"},
		{"10,010,000", bigComma(10010000), "10,010,000"},
		{"10,001,000", bigComma(10001000), "10,001,000"},
		{"123,456,789", bigComma(123456789), "123,456,789"},
		{"maxint", bigComma(9.223372e+18), "9,223,372,000,000,000,000"},
		{"minint", bigComma(-9.223372e+18), "-9,223,372,000,000,000,000"},
		{"-123,456,789", bigComma(-123456789), "-123,456,789"},
		{"-10,100,000", bigComma(-10100000), "-10,100,000"},
		{"-10,010,000", bigComma(-10010000), "-10,010,000"},
		{"-10,001,000", bigComma(-10001000), "-10,001,000"},
		{"-10,000,000", bigComma(-10000000), "-10,000,000"},
		{"-100,000", bigComma(-100000), "-100,000"},
		{"-10,000", bigComma(-10000), "-10,000"},
		{"-1,000", bigComma(-1000), "-1,000"},
		{"-100", bigComma(-100), "-100"},
		{"-10", bigComma(-10), "-10"},
	}.validate(t)
}

func TestVeryBigCommas(t *testing.T) {
	tests := []struct{ in, exp string }{
		{
			"84889279597249724975972597249849757294578485",
			"84,889,279,597,249,724,975,972,597,249,849,757,294,578,485",
		},
		{
			"-84889279597249724975972597249849757294578485",
			"-84,889,279,597,249,724,975,972,597,249,849,757,294,578,485",
		},
	}
	for _, test := range tests {
		n, _ := (&big.Int{}).SetString(test.in, 10)
		got := BigComma(n)
		if test.exp != got {
			t.Errorf("Expected %q, got %q", test.exp, got)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 certificate

import (
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"fmt"
	"math"
	"net"
	"sort"
	"time"

	certificates "k8s.io/api/certificates/v1"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/util/certificate"
	compbasemetrics "k8s.io/component-base/metrics"
	"k8s.io/component-base/metrics/legacyregistry"
	kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
	"k8s.io/kubernetes/pkg/kubelet/metrics"
)

// NewKubeletServerCertificateManager creates a certificate manager for the kubelet when retrieving a server certificate
// or returns an error.
func NewKubeletServerCertificateManager(kubeClient clientset.Interface, kubeCfg *kubeletconfig.KubeletConfiguration, nodeName types.NodeName, getAddresses func() []v1.NodeAddress, certDirectory string) (certificate.Manager, error) {
	var clientsetFn certificate.ClientsetFunc
	if kubeClient != nil {
		clientsetFn = func(current *tls.Certificate) (clientset.Interface, error) {
			return kubeClient, nil
		}
	}
	certificateStore, err := certificate.NewFileStore(
		"kubelet-server",
		certDirectory,
		certDirectory,
		kubeCfg.TLSCertFile,
		kubeCfg.TLSPrivateKeyFile)
	if err != nil {
		return nil, fmt.Errorf("failed to initialize server certificate store: %v", err)
	}
	var certificateRenewFailure = compbasemetrics.NewCounter(
		&compbasemetrics.CounterOpts{
			Subsystem:      metrics.KubeletSubsystem,
			Name:           "server_expiration_renew_errors",
			Help:           "Counter of certificate renewal errors.",
			StabilityLevel: compbasemetrics.STABLE,
		},
	)
	legacyregistry.MustRegister(certificateRenewFailure)

	certificateRotationAge := compbasemetrics.NewHistogram(
		&compbasemetrics.HistogramOpts{
			Subsystem: metrics.KubeletSubsystem,
			Name:      "certificate_manager_server_rotation_seconds",
			Help:      "Histogram of the number of seconds the previous certificate lived before being rotated.",
			Buckets: []float64{
				60,        // 1  minute
				3600,      // 1  hour
				14400,     // 4  hours
				86400,     // 1  day
				604800,    // 1  week
				2592000,   // 1  month
				7776000,   // 3  months
				15552000,  // 6  months
				31104000,  // 1  year
				124416000, // 4  years
			},
			StabilityLevel: compbasemetrics.ALPHA,
		},
	)
	legacyregistry.MustRegister(certificateRotationAge)

	getTemplate := func() *x509.CertificateRequest {
		hostnames, ips := addressesToHostnamesAndIPs(getAddresses())
		// don't return a template if we have no addresses to request for
		if len(hostnames) == 0 && len(ips) == 0 {
			return nil
		}
		return &x509.CertificateRequest{
			Subject: pkix.Name{
				CommonName:   fmt.Sprintf("system:node:%s", nodeName),
				Organization: []string{"system:nodes"},
			},
			DNSNames:    hostnames,
			IPAddresses: ips,
		}
	}

	m, err := certificate.NewManager(&certificate.Config{
		ClientsetFn: clientsetFn,
		GetTemplate: getTemplate,
		SignerName:  certificates.KubeletServingSignerName,
		Usages: []certificates.KeyUsage{
			// https://tools.ietf.org/html/rfc5280#section-4.2.1.3
			//
			// Digital signature allows the certificate to be used to verify
			// digital signatures used during TLS negotiation.
			certificates.UsageDigitalSignature,
			// KeyEncipherment allows the cert/key pair to be used to encrypt
			// keys, including the symmetric keys negotiated during TLS setup
			// and used for data transfer.
			certificates.UsageKeyEncipherment,
			// ServerAuth allows the cert to be used by a TLS server to
			// authenticate itself to a TLS client.
			certificates.UsageServerAuth,
		},
		CertificateStore:        certificateStore,
		CertificateRotation:     certificateRotationAge,
		CertificateRenewFailure: certificateRenewFailure,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to initialize server certificate manager: %v", err)
	}
	legacyregistry.RawMustRegister(compbasemetrics.NewGaugeFunc(
		compbasemetrics.GaugeOpts{
			Subsystem: metrics.KubeletSubsystem,
			Name:      "certificate_manager_server_ttl_seconds",
			Help: "Gauge of the shortest TTL (time-to-live) of " +
				"the Kubelet's serving certificate. The value is in seconds " +
				"until certificate expiry (negative if already expired). If " +
				"serving certificate is invalid or unused, the value will " +
				"be +INF.",
			StabilityLevel: compbasemetrics.ALPHA,
		},
		func() float64 {
			if c := m.Current(); c != nil && c.Leaf != nil {
				return math.Trunc(c.Leaf.NotAfter.Sub(time.Now()).Seconds())
			}
			return math.Inf(1)
		},
	))
	return m, nil
}

func addressesToHostnamesAndIPs(addresses []v1.NodeAddress) (dnsNames []string, ips []net.IP) {
	seenDNSNames := map[string]bool{}
	seenIPs := map[string]bool{}
	for _, address := range addresses {
		if len(address.Address) == 0 {
			continue
		}

		switch address.Type {
		case v1.NodeHostName:
			if ip := net.ParseIP(address.Address); ip != nil {
				seenIPs[address.Address] = true
			} else {
				seenDNSNames[address.Address] = true
			}
		case v1.NodeExternalIP, v1.NodeInternalIP:
			if ip := net.ParseIP(address.Address); ip != nil {
				seenIPs[address.Address] = true
			}
		case v1.NodeExternalDNS, v1.NodeInternalDNS:
			seenDNSNames[address.Address] = true
		}
	}

	for dnsName := range seenDNSNames {
		dnsNames = append(dnsNames, dnsName)
	}
	for ip := range seenIPs {
		ips = append(ips, net.ParseIP(ip))
	}

	// return in stable order
	sort.Strings(dnsNames)
	sort.Slice(ips, func(i, j int) bool { return ips[i].String() < ips[j].String() })

	return dnsNames, ips
}

// NewKubeletClientCertificateManager sets up a certificate manager without a
// client that can be used to sign new certificates (or rotate). If a CSR
// client is set later, it may begin rotating/renewing the client cert.
func NewKubeletClientCertificateManager(
	certDirectory string,
	nodeName types.NodeName,
	bootstrapCertData []byte,
	bootstrapKeyData []byte,
	certFile string,
	keyFile string,
	clientsetFn certificate.ClientsetFunc,
) (certificate.Manager, error) {

	certificateStore, err := certificate.NewFileStore(
		"kubelet-client",
		certDirectory,
		certDirectory,
		certFile,
		keyFile)
	if err != nil {
		return nil, fmt.Errorf("failed to initialize client certificate store: %v", err)
	}
	var certificateRenewFailure = compbasemetrics.NewCounter(
		&compbasemetrics.CounterOpts{
			Namespace:      metrics.KubeletSubsystem,
			Subsystem:      "certificate_manager",
			Name:           "client_expiration_renew_errors",
			Help:           "Counter of certificate renewal errors.",
			StabilityLevel: compbasemetrics.ALPHA,
		},
	)
	legacyregistry.Register(certificateRenewFailure)

	m, err := certificate.NewManager(&certificate.Config{
		ClientsetFn: clientsetFn,
		Template: &x509.CertificateRequest{
			Subject: pkix.Name{
				CommonName:   fmt.Sprintf("system:node:%s", nodeName),
				Organization: []string{"system:nodes"},
			},
		},
		SignerName: certificates.KubeAPIServerClientKubeletSignerName,
		Usages: []certificates.KeyUsage{
			// https://tools.ietf.org/html/rfc5280#section-4.2.1.3
			//
			// DigitalSignature allows the certificate to be used to verify
			// digital signatures including signatures used during TLS
			// negotiation.
			certificates.UsageDigitalSignature,
			// KeyEncipherment allows the cert/key pair to be used to encrypt
			// keys, including the symmetric keys negotiated during TLS setup
			// and used for data transfer..
			certificates.UsageKeyEncipherment,
			// ClientAuth allows the cert to be used by a TLS client to
			// authenticate itself to the TLS server.
			certificates.UsageClientAuth,
		},

		// For backwards compatibility, the kubelet supports the ability to
		// provide a higher privileged certificate as initial data that will
		// then be rotated immediately. This code path is used by kubeadm on
		// the masters.
		BootstrapCertificatePEM: bootstrapCertData,
		BootstrapKeyPEM:         bootstrapKeyData,

		CertificateStore:        certificateStore,
		CertificateRenewFailure: certificateRenewFailure,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to initialize client certificate manager: %v", err)
	}

	return m, nil
}
<commit_msg>revert test STABLE declaration<commit_after>/*
Copyright 2017 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 certificate

import (
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"fmt"
	"math"
	"net"
	"sort"
	"time"

	certificates "k8s.io/api/certificates/v1"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/util/certificate"
	compbasemetrics "k8s.io/component-base/metrics"
	"k8s.io/component-base/metrics/legacyregistry"
	kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
	"k8s.io/kubernetes/pkg/kubelet/metrics"
)

// NewKubeletServerCertificateManager creates a certificate manager for the kubelet when retrieving a server certificate
// or returns an error.
func NewKubeletServerCertificateManager(kubeClient clientset.Interface, kubeCfg *kubeletconfig.KubeletConfiguration, nodeName types.NodeName, getAddresses func() []v1.NodeAddress, certDirectory string) (certificate.Manager, error) {
	var clientsetFn certificate.ClientsetFunc
	if kubeClient != nil {
		clientsetFn = func(current *tls.Certificate) (clientset.Interface, error) {
			return kubeClient, nil
		}
	}
	certificateStore, err := certificate.NewFileStore(
		"kubelet-server",
		certDirectory,
		certDirectory,
		kubeCfg.TLSCertFile,
		kubeCfg.TLSPrivateKeyFile)
	if err != nil {
		return nil, fmt.Errorf("failed to initialize server certificate store: %v", err)
	}
	var certificateRenewFailure = compbasemetrics.NewCounter(
		&compbasemetrics.CounterOpts{
			Subsystem:      metrics.KubeletSubsystem,
			Name:           "server_expiration_renew_errors",
			Help:           "Counter of certificate renewal errors.",
			StabilityLevel: compbasemetrics.ALPHA,
		},
	)
	legacyregistry.MustRegister(certificateRenewFailure)

	certificateRotationAge := compbasemetrics.NewHistogram(
		&compbasemetrics.HistogramOpts{
			Subsystem: metrics.KubeletSubsystem,
			Name:      "certificate_manager_server_rotation_seconds",
			Help:      "Histogram of the number of seconds the previous certificate lived before being rotated.",
			Buckets: []float64{
				60,        // 1  minute
				3600,      // 1  hour
				14400,     // 4  hours
				86400,     // 1  day
				604800,    // 1  week
				2592000,   // 1  month
				7776000,   // 3  months
				15552000,  // 6  months
				31104000,  // 1  year
				124416000, // 4  years
			},
			StabilityLevel: compbasemetrics.ALPHA,
		},
	)
	legacyregistry.MustRegister(certificateRotationAge)

	getTemplate := func() *x509.CertificateRequest {
		hostnames, ips := addressesToHostnamesAndIPs(getAddresses())
		// don't return a template if we have no addresses to request for
		if len(hostnames) == 0 && len(ips) == 0 {
			return nil
		}
		return &x509.CertificateRequest{
			Subject: pkix.Name{
				CommonName:   fmt.Sprintf("system:node:%s", nodeName),
				Organization: []string{"system:nodes"},
			},
			DNSNames:    hostnames,
			IPAddresses: ips,
		}
	}

	m, err := certificate.NewManager(&certificate.Config{
		ClientsetFn: clientsetFn,
		GetTemplate: getTemplate,
		SignerName:  certificates.KubeletServingSignerName,
		Usages: []certificates.KeyUsage{
			// https://tools.ietf.org/html/rfc5280#section-4.2.1.3
			//
			// Digital signature allows the certificate to be used to verify
			// digital signatures used during TLS negotiation.
			certificates.UsageDigitalSignature,
			// KeyEncipherment allows the cert/key pair to be used to encrypt
			// keys, including the symmetric keys negotiated during TLS setup
			// and used for data transfer.
			certificates.UsageKeyEncipherment,
			// ServerAuth allows the cert to be used by a TLS server to
			// authenticate itself to a TLS client.
			certificates.UsageServerAuth,
		},
		CertificateStore:        certificateStore,
		CertificateRotation:     certificateRotationAge,
		CertificateRenewFailure: certificateRenewFailure,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to initialize server certificate manager: %v", err)
	}
	legacyregistry.RawMustRegister(compbasemetrics.NewGaugeFunc(
		compbasemetrics.GaugeOpts{
			Subsystem: metrics.KubeletSubsystem,
			Name:      "certificate_manager_server_ttl_seconds",
			Help: "Gauge of the shortest TTL (time-to-live) of " +
				"the Kubelet's serving certificate. The value is in seconds " +
				"until certificate expiry (negative if already expired). If " +
				"serving certificate is invalid or unused, the value will " +
				"be +INF.",
			StabilityLevel: compbasemetrics.ALPHA,
		},
		func() float64 {
			if c := m.Current(); c != nil && c.Leaf != nil {
				return math.Trunc(c.Leaf.NotAfter.Sub(time.Now()).Seconds())
			}
			return math.Inf(1)
		},
	))
	return m, nil
}

func addressesToHostnamesAndIPs(addresses []v1.NodeAddress) (dnsNames []string, ips []net.IP) {
	seenDNSNames := map[string]bool{}
	seenIPs := map[string]bool{}
	for _, address := range addresses {
		if len(address.Address) == 0 {
			continue
		}

		switch address.Type {
		case v1.NodeHostName:
			if ip := net.ParseIP(address.Address); ip != nil {
				seenIPs[address.Address] = true
			} else {
				seenDNSNames[address.Address] = true
			}
		case v1.NodeExternalIP, v1.NodeInternalIP:
			if ip := net.ParseIP(address.Address); ip != nil {
				seenIPs[address.Address] = true
			}
		case v1.NodeExternalDNS, v1.NodeInternalDNS:
			seenDNSNames[address.Address] = true
		}
	}

	for dnsName := range seenDNSNames {
		dnsNames = append(dnsNames, dnsName)
	}
	for ip := range seenIPs {
		ips = append(ips, net.ParseIP(ip))
	}

	// return in stable order
	sort.Strings(dnsNames)
	sort.Slice(ips, func(i, j int) bool { return ips[i].String() < ips[j].String() })

	return dnsNames, ips
}

// NewKubeletClientCertificateManager sets up a certificate manager without a
// client that can be used to sign new certificates (or rotate). If a CSR
// client is set later, it may begin rotating/renewing the client cert.
func NewKubeletClientCertificateManager(
	certDirectory string,
	nodeName types.NodeName,
	bootstrapCertData []byte,
	bootstrapKeyData []byte,
	certFile string,
	keyFile string,
	clientsetFn certificate.ClientsetFunc,
) (certificate.Manager, error) {

	certificateStore, err := certificate.NewFileStore(
		"kubelet-client",
		certDirectory,
		certDirectory,
		certFile,
		keyFile)
	if err != nil {
		return nil, fmt.Errorf("failed to initialize client certificate store: %v", err)
	}
	var certificateRenewFailure = compbasemetrics.NewCounter(
		&compbasemetrics.CounterOpts{
			Namespace:      metrics.KubeletSubsystem,
			Subsystem:      "certificate_manager",
			Name:           "client_expiration_renew_errors",
			Help:           "Counter of certificate renewal errors.",
			StabilityLevel: compbasemetrics.ALPHA,
		},
	)
	legacyregistry.Register(certificateRenewFailure)

	m, err := certificate.NewManager(&certificate.Config{
		ClientsetFn: clientsetFn,
		Template: &x509.CertificateRequest{
			Subject: pkix.Name{
				CommonName:   fmt.Sprintf("system:node:%s", nodeName),
				Organization: []string{"system:nodes"},
			},
		},
		SignerName: certificates.KubeAPIServerClientKubeletSignerName,
		Usages: []certificates.KeyUsage{
			// https://tools.ietf.org/html/rfc5280#section-4.2.1.3
			//
			// DigitalSignature allows the certificate to be used to verify
			// digital signatures including signatures used during TLS
			// negotiation.
			certificates.UsageDigitalSignature,
			// KeyEncipherment allows the cert/key pair to be used to encrypt
			// keys, including the symmetric keys negotiated during TLS setup
			// and used for data transfer..
			certificates.UsageKeyEncipherment,
			// ClientAuth allows the cert to be used by a TLS client to
			// authenticate itself to the TLS server.
			certificates.UsageClientAuth,
		},

		// For backwards compatibility, the kubelet supports the ability to
		// provide a higher privileged certificate as initial data that will
		// then be rotated immediately. This code path is used by kubeadm on
		// the masters.
		BootstrapCertificatePEM: bootstrapCertData,
		BootstrapKeyPEM:         bootstrapKeyData,

		CertificateStore:        certificateStore,
		CertificateRenewFailure: certificateRenewFailure,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to initialize client certificate manager: %v", err)
	}

	return m, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Mirantis

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 libvirttools

/*
#include <libvirt/libvirt.h>
#include <libvirt/virterror.h>
#include <stdlib.h>
#include "virtualization.h"
*/
import "C"

import (
	"fmt"
	"reflect"
	"unsafe"

	kubeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"

	"encoding/xml"
	"github.com/Mirantis/virtlet/pkg/bolttools"
	"github.com/Mirantis/virtlet/pkg/utils"
	"github.com/golang/glog"
)

const (
	defaultMemory = 1024
	defaultVcpu   = 1
)

type Drive struct {
	DriveName string `xml:"name,attr"`
	DriveType string `xml:"type,attr"`
}

type Source struct {
	SrcFile string `xml:"file,attr"`
}

type Target struct {
	TargetDev string `xml:"dev,attr"`
	TargetBus string `xml:"bus,attr"`
}

type Disk struct {
	DiskType   string `xml:"type,attr"`
	DiskDevice string `xml:"device,attr"`
	Drive      Drive  `xml:"drive"`
	Src        Source `xml:"source"`
	Target     Target `xml:"target"`
}

type Devices struct {
	DiskList []Disk   `xml:"disk"`
	Inpt     Input    `xml:"input"`
	Graph    Graphics `xml:"graphics"`
	Serial   Serial   `xml:"serial"`
	Consl    Console  `xml:"console"`
	Snd      Sound    `xml:"sound"`
	Items    []Tag    `xml:",any"`
}

type Tag struct {
	XMLName xml.Name
	Content string `xml:",innerxml"`
}

type Domain struct {
	XMLName xml.Name `xml:"domain"`
	DomType string   `xml:"type,attr"`
	Devs    Devices  `xml:"devices"`
	Items   []Tag    `xml:",any"`
}

type Input struct {
	Type string `xml:"type,attr"`
	Bus  string `xml:"bus,attr"`
}

type Graphics struct {
	Type string `xml:"type,attr"`
	Port string `xml:"port,attr"`
}

type Console struct {
	Type   string        `xml:"type,attr"`
	Target TargetConsole `xml:"target"`
}

type TargetConsole struct {
	Type string `xml:"type,attr"`
	Port string `xml:"port,attr"`
}

type Serial struct {
	Type   string       `xml:"type,attr"`
	Target TargetSerial `xml:"target"`
}

type TargetSerial struct {
	Port string `xml:"port,attr"`
}

type Sound struct {
	Model string `xml:"model,attr"`
}

var volXML string = `
<disk type='file' device='disk'>
    <drive name='qemu' type='raw'/>
    <source file='%s'/>
    <target dev='vda' bus='virtio'/>
</disk>`

func (v *VirtualizationTool) processVolumes(mounts []*kubeapi.Mount, domXML string) (string, error) {
	copyDomXML := domXML
	if len(mounts) == 0 {
		return domXML, nil
	}
	glog.Infof("INPUT domain:\n%s\n\n", domXML)
	domainXML := &Domain{}
	err := xml.Unmarshal([]byte(domXML), domainXML)
	if err != nil {
		return domXML, err
	}

	for _, mount := range mounts {
		if mount.HostPath != nil {
			vol, err := v.volumeStorage.CreateVol(v.volumePool, *mount.Name, defaultCapacity, defaultCapacityUnit)
			if err != nil {
				return domXML, err
			}
			path, err := VolGetPath(vol)
			if err != nil {
				return copyDomXML, err
			}
			err = utils.FormatDisk(path)
			if err != nil {
				return copyDomXML, err
			}
			volXML = fmt.Sprintf(volXML, path)
			disk := &Disk{}
			err = xml.Unmarshal([]byte(volXML), disk)
			disk.Target.TargetDev = "vdc"
			if err != nil {
				return domXML, err
			}
			domainXML.Devs.DiskList = append(domainXML.Devs.DiskList, *disk)
			outArr, err := xml.MarshalIndent(domainXML, " ", "  ")
			if err != nil {
				return copyDomXML, err
			}
			domXML = string(outArr[:])
			glog.Infof("Creating domain:\n%s", domXML)
			break
		}
	}
	return domXML, nil
}

func generateDomXML(name string, memory int64, uuid string, vcpu int64, imageFilepath string) string {
	domXML := `
<domain type='kvm'>
    <name>%s</name>
    <memory>%d</memory>
    <uuid>%s</uuid>
    <features>
        <acpi/><apic/>
    </features>
    <vcpu>%d</vcpu>
    <os>
        <type>hvm</type>
        <boot dev='hd'/>
    </os>
    <on_poweroff>destroy</on_poweroff>
    <on_reboot>restart</on_reboot>
    <on_crash>restart</on_crash>
    <devices>
        <emulator>/usr/bin/kvm</emulator>
        <disk type='file' device='disk'>
            <drive name='qemu' type='qcow2'/>
            <source file='%s'/>
            <target dev='vda' bus='virtio'/>
        </disk>
        <input type='tablet' bus='usb'/>
        <graphics type='vnc' port='-1'/>
        <serial type='pty'>
            <target port='0'/>
        </serial>
        <console type='pty'>
            <target type='serial' port='0'/>
        </console>
        <sound model='ac97'/>
        <video>
            <model type='cirrus'/>
        </video>
    </devices>
</domain>`
	return fmt.Sprintf(domXML, name, memory, uuid, vcpu, imageFilepath)
}

type VirtualizationTool struct {
	conn           C.virConnectPtr
	volumeStorage  StorageBackend
	volumePool     C.virStoragePoolPtr
	volumePoolName string
}

func NewVirtualizationTool(conn C.virConnectPtr, poolName string, storageBackendName string) (*VirtualizationTool, error) {
	pool, err := LookupStoragePool(conn, poolName)
	if err != nil {
		return nil, err
	}

	storageBackend, err := GetStorageBackend(storageBackendName)
	if err != nil {
		return nil, err
	}
	return &VirtualizationTool{conn: conn, volumeStorage: storageBackend, volumePool: pool, volumePoolName: poolName}, nil
}

func (v *VirtualizationTool) CreateContainer(in *kubeapi.CreateContainerRequest, imageFilepath string) (string, error) {
	var name string
	var memory int64
	var vcpu int64

	uuid, err := utils.NewUuid()
	if err != nil {
		return "", err
	}

	if in.Config.Metadata != nil && in.Config.Metadata.Name != nil {
		name = *in.Config.Metadata.Name
	} else {
		name = uuid
	}

	if in.Config.Linux != nil && in.Config.Linux.Resources != nil && in.Config.Linux.Resources.MemoryLimitInBytes != nil {
		memory = *in.Config.Linux.Resources.MemoryLimitInBytes
	} else {
		memory = defaultMemory
	}

	if in.Config.Linux != nil && in.Config.Linux.Resources != nil && in.Config.Linux.Resources.CpuPeriod != nil {
		vcpu = *in.Config.Linux.Resources.CpuPeriod
	} else {
		vcpu = defaultVcpu
	}

	domXML := generateDomXML(name, memory, uuid, vcpu, imageFilepath)
	domXML, err = v.processVolumes(in.Config.Mounts, domXML)
	if err != nil {
		return "", err
	}

	cDomXML := C.CString(domXML)
	defer C.free(unsafe.Pointer(cDomXML))

	if status := C.defineDomain(v.conn, cDomXML); status < 0 {
		return "", GetLastError()
	}

	cContainerId := C.CString(uuid)
	defer C.free(unsafe.Pointer(cContainerId))
	domain := C.virDomainLookupByUUIDString(v.conn, cContainerId)
	if domain == nil {
		return "", GetLastError()
	}
	defer C.virDomainFree(domain)
	var domainInfo C.virDomainInfo
	if status := C.virDomainGetInfo(domain, &domainInfo); status < 0 {
		return "", GetLastError()
	}

	return uuid, nil
}

func (v *VirtualizationTool) StartContainer(containerId string) error {
	cContainerId := C.CString(containerId)
	defer C.free(unsafe.Pointer(cContainerId))

	if status := C.createDomain(v.conn, cContainerId); status < 0 {
		return GetLastError()
	}

	return nil
}

func (v *VirtualizationTool) StopContainer(containerId string) error {
	cContainerId := C.CString(containerId)
	defer C.free(unsafe.Pointer(cContainerId))

	if status := C.stopDomain(v.conn, cContainerId); status < 0 {
		return GetLastError()
	}

	return nil
}

func (v *VirtualizationTool) RemoveContainer(containerId string) error {
	v.StopContainer(containerId)

	cContainerId := C.CString(containerId)
	defer C.free(unsafe.Pointer(cContainerId))

	if status := C.destroyAndUndefineDomain(v.conn, cContainerId); status < 0 {
		return GetLastError()
	}

	return nil
}

func libvirtToKubeState(domainInfo C.virDomainInfo) kubeapi.ContainerState {
	var containerState kubeapi.ContainerState

	switch domainInfo.state {
	case C.VIR_DOMAIN_RUNNING:
		containerState = kubeapi.ContainerState_RUNNING
	case C.VIR_DOMAIN_PAUSED:
		containerState = kubeapi.ContainerState_EXITED
	case C.VIR_DOMAIN_SHUTDOWN:
		containerState = kubeapi.ContainerState_EXITED
	case C.VIR_DOMAIN_SHUTOFF:
		containerState = kubeapi.ContainerState_CREATED
	case C.VIR_DOMAIN_CRASHED:
		containerState = kubeapi.ContainerState_EXITED
	case C.VIR_DOMAIN_PMSUSPENDED:
		containerState = kubeapi.ContainerState_EXITED
	default:
		containerState = kubeapi.ContainerState_UNKNOWN
	}

	return containerState
}

func filterContainer(container *kubeapi.Container, filter *kubeapi.ContainerFilter) bool {
	if filter.State != nil && *container.State != *filter.State {
		return false
	}
	return true
}

func (v *VirtualizationTool) ListContainers(boltClient *bolttools.BoltClient, filter *kubeapi.ContainerFilter) ([]*kubeapi.Container, error) {
	var domainInfo C.virDomainInfo
	var cList *C.virDomainPtr
	count := C.virConnectListAllDomains(v.conn, (**C.virDomainPtr)(&cList), 0)
	if count < 0 {
		return nil, GetLastError()
	}
	header := reflect.SliceHeader{
		Data: uintptr(unsafe.Pointer(cList)),
		Len:  int(count),
		Cap:  int(count),
	}
	domains := *(*[]C.virDomainPtr)(unsafe.Pointer(&header))

	containers := make([]*kubeapi.Container, 0, count)

	for _, domain := range domains {
		id := C.GoString(C.virDomainGetName(domain))

		if status := C.virDomainGetInfo(domain, &domainInfo); status < 0 {
			return nil, GetLastError()
		}

		containerState := libvirtToKubeState(domainInfo)

		metadata := &kubeapi.ContainerMetadata{
			Name: &id,
		}

		labels, err := boltClient.GetLabels(id)
		if err != nil {
			return nil, err
		}
		annotations, err := boltClient.GetAnnotations(id)
		if err != nil {
			return nil, err
		}

		container := &kubeapi.Container{
			Id:          &id,
			State:       &containerState,
			Metadata:    metadata,
			Labels:      labels,
			Annotations: annotations,
		}

		if filterContainer(container, filter) {
			containers = append(containers, container)
		}
	}

	return containers, nil
}

func (v *VirtualizationTool) ContainerStatus(containerId string) (*kubeapi.ContainerStatus, error) {
	var domainInfo C.virDomainInfo

	cContainerId := C.CString(containerId)
	defer C.free(unsafe.Pointer(cContainerId))

	domain := C.virDomainLookupByName(v.conn, cContainerId)
	if domain == nil {
		return nil, GetLastError()
	}
	defer C.virDomainFree(domain)

	id := C.GoString(C.virDomainGetName(domain))

	if status := C.virDomainGetInfo(domain, &domainInfo); status < 0 {
		return nil, GetLastError()
	}

	containerState := libvirtToKubeState(domainInfo)

	return &kubeapi.ContainerStatus{
		Id:       &id,
		Metadata: &kubeapi.ContainerMetadata{},
		State:    &containerState,
	}, nil
}
<commit_msg>virtualization: Fix cpu/memory limits.<commit_after>/*
Copyright 2016 Mirantis

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 libvirttools

/*
#include <libvirt/libvirt.h>
#include <libvirt/virterror.h>
#include <stdlib.h>
#include "virtualization.h"
*/
import "C"

import (
	"fmt"
	"reflect"
	"unsafe"

	kubeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"

	"encoding/xml"
	"github.com/Mirantis/virtlet/pkg/bolttools"
	"github.com/Mirantis/virtlet/pkg/utils"
	"github.com/golang/glog"
)

const (
	defaultMemory = 1024
	defaultVcpu   = 1
)

type Drive struct {
	DriveName string `xml:"name,attr"`
	DriveType string `xml:"type,attr"`
}

type Source struct {
	SrcFile string `xml:"file,attr"`
}

type Target struct {
	TargetDev string `xml:"dev,attr"`
	TargetBus string `xml:"bus,attr"`
}

type Disk struct {
	DiskType   string `xml:"type,attr"`
	DiskDevice string `xml:"device,attr"`
	Drive      Drive  `xml:"drive"`
	Src        Source `xml:"source"`
	Target     Target `xml:"target"`
}

type Devices struct {
	DiskList []Disk   `xml:"disk"`
	Inpt     Input    `xml:"input"`
	Graph    Graphics `xml:"graphics"`
	Serial   Serial   `xml:"serial"`
	Consl    Console  `xml:"console"`
	Snd      Sound    `xml:"sound"`
	Items    []Tag    `xml:",any"`
}

type Tag struct {
	XMLName xml.Name
	Content string `xml:",innerxml"`
}

type Domain struct {
	XMLName xml.Name `xml:"domain"`
	DomType string   `xml:"type,attr"`
	Devs    Devices  `xml:"devices"`
	Items   []Tag    `xml:",any"`
}

type Input struct {
	Type string `xml:"type,attr"`
	Bus  string `xml:"bus,attr"`
}

type Graphics struct {
	Type string `xml:"type,attr"`
	Port string `xml:"port,attr"`
}

type Console struct {
	Type   string        `xml:"type,attr"`
	Target TargetConsole `xml:"target"`
}

type TargetConsole struct {
	Type string `xml:"type,attr"`
	Port string `xml:"port,attr"`
}

type Serial struct {
	Type   string       `xml:"type,attr"`
	Target TargetSerial `xml:"target"`
}

type TargetSerial struct {
	Port string `xml:"port,attr"`
}

type Sound struct {
	Model string `xml:"model,attr"`
}

var volXML string = `
<disk type='file' device='disk'>
    <drive name='qemu' type='raw'/>
    <source file='%s'/>
    <target dev='vda' bus='virtio'/>
</disk>`

func (v *VirtualizationTool) processVolumes(mounts []*kubeapi.Mount, domXML string) (string, error) {
	copyDomXML := domXML
	if len(mounts) == 0 {
		return domXML, nil
	}
	glog.Infof("INPUT domain:\n%s\n\n", domXML)
	domainXML := &Domain{}
	err := xml.Unmarshal([]byte(domXML), domainXML)
	if err != nil {
		return domXML, err
	}

	for _, mount := range mounts {
		if mount.HostPath != nil {
			vol, err := v.volumeStorage.CreateVol(v.volumePool, *mount.Name, defaultCapacity, defaultCapacityUnit)
			if err != nil {
				return domXML, err
			}
			path, err := VolGetPath(vol)
			if err != nil {
				return copyDomXML, err
			}
			err = utils.FormatDisk(path)
			if err != nil {
				return copyDomXML, err
			}
			volXML = fmt.Sprintf(volXML, path)
			disk := &Disk{}
			err = xml.Unmarshal([]byte(volXML), disk)
			disk.Target.TargetDev = "vdc"
			if err != nil {
				return domXML, err
			}
			domainXML.Devs.DiskList = append(domainXML.Devs.DiskList, *disk)
			outArr, err := xml.MarshalIndent(domainXML, " ", "  ")
			if err != nil {
				return copyDomXML, err
			}
			domXML = string(outArr[:])
			glog.Infof("Creating domain:\n%s", domXML)
			break
		}
	}
	return domXML, nil
}

func generateDomXML(name string, memory int64, uuid string, vcpu int64, imageFilepath string) string {
	domXML := `
<domain type='kvm'>
    <name>%s</name>
    <memory>%d</memory>
    <uuid>%s</uuid>
    <features>
        <acpi/><apic/>
    </features>
    <vcpu>%d</vcpu>
    <os>
        <type>hvm</type>
        <boot dev='hd'/>
    </os>
    <on_poweroff>destroy</on_poweroff>
    <on_reboot>restart</on_reboot>
    <on_crash>restart</on_crash>
    <devices>
        <emulator>/usr/bin/kvm</emulator>
        <disk type='file' device='disk'>
            <drive name='qemu' type='qcow2'/>
            <source file='%s'/>
            <target dev='vda' bus='virtio'/>
        </disk>
        <input type='tablet' bus='usb'/>
        <graphics type='vnc' port='-1'/>
        <serial type='pty'>
            <target port='0'/>
        </serial>
        <console type='pty'>
            <target type='serial' port='0'/>
        </console>
        <sound model='ac97'/>
        <video>
            <model type='cirrus'/>
        </video>
    </devices>
</domain>`
	return fmt.Sprintf(domXML, name, memory, uuid, vcpu, imageFilepath)
}

type VirtualizationTool struct {
	conn           C.virConnectPtr
	volumeStorage  StorageBackend
	volumePool     C.virStoragePoolPtr
	volumePoolName string
}

func NewVirtualizationTool(conn C.virConnectPtr, poolName string, storageBackendName string) (*VirtualizationTool, error) {
	pool, err := LookupStoragePool(conn, poolName)
	if err != nil {
		return nil, err
	}

	storageBackend, err := GetStorageBackend(storageBackendName)
	if err != nil {
		return nil, err
	}
	return &VirtualizationTool{conn: conn, volumeStorage: storageBackend, volumePool: pool, volumePoolName: poolName}, nil
}

func (v *VirtualizationTool) CreateContainer(in *kubeapi.CreateContainerRequest, imageFilepath string) (string, error) {
	var name string
	var memory int64
	var vcpu int64

	uuid, err := utils.NewUuid()
	if err != nil {
		return "", err
	}

	if in.Config.Metadata != nil && in.Config.Metadata.Name != nil {
		name = *in.Config.Metadata.Name
	} else {
		name = uuid
	}

	if in.Config.Linux != nil && in.Config.Linux.Resources != nil && in.Config.Linux.Resources.MemoryLimitInBytes != nil && *in.Config.Linux.Resources.MemoryLimitInBytes > 0 {
		memory = *in.Config.Linux.Resources.MemoryLimitInBytes
	} else {
		memory = defaultMemory
	}

	if in.Config.Linux != nil && in.Config.Linux.Resources != nil && in.Config.Linux.Resources.CpuPeriod != nil && *in.Config.Linux.Resources.CpuPeriod > 0 {
		vcpu = *in.Config.Linux.Resources.CpuPeriod
	} else {
		vcpu = defaultVcpu
	}

	domXML := generateDomXML(name, memory, uuid, vcpu, imageFilepath)
	domXML, err = v.processVolumes(in.Config.Mounts, domXML)
	if err != nil {
		return "", err
	}

	cDomXML := C.CString(domXML)
	defer C.free(unsafe.Pointer(cDomXML))

	if status := C.defineDomain(v.conn, cDomXML); status < 0 {
		return "", GetLastError()
	}

	cContainerId := C.CString(uuid)
	defer C.free(unsafe.Pointer(cContainerId))
	domain := C.virDomainLookupByUUIDString(v.conn, cContainerId)
	if domain == nil {
		return "", GetLastError()
	}
	defer C.virDomainFree(domain)
	var domainInfo C.virDomainInfo
	if status := C.virDomainGetInfo(domain, &domainInfo); status < 0 {
		return "", GetLastError()
	}

	return uuid, nil
}

func (v *VirtualizationTool) StartContainer(containerId string) error {
	cContainerId := C.CString(containerId)
	defer C.free(unsafe.Pointer(cContainerId))

	if status := C.createDomain(v.conn, cContainerId); status < 0 {
		return GetLastError()
	}

	return nil
}

func (v *VirtualizationTool) StopContainer(containerId string) error {
	cContainerId := C.CString(containerId)
	defer C.free(unsafe.Pointer(cContainerId))

	if status := C.stopDomain(v.conn, cContainerId); status < 0 {
		return GetLastError()
	}

	return nil
}

func (v *VirtualizationTool) RemoveContainer(containerId string) error {
	v.StopContainer(containerId)

	cContainerId := C.CString(containerId)
	defer C.free(unsafe.Pointer(cContainerId))

	if status := C.destroyAndUndefineDomain(v.conn, cContainerId); status < 0 {
		return GetLastError()
	}

	return nil
}

func libvirtToKubeState(domainInfo C.virDomainInfo) kubeapi.ContainerState {
	var containerState kubeapi.ContainerState

	switch domainInfo.state {
	case C.VIR_DOMAIN_RUNNING:
		containerState = kubeapi.ContainerState_RUNNING
	case C.VIR_DOMAIN_PAUSED:
		containerState = kubeapi.ContainerState_EXITED
	case C.VIR_DOMAIN_SHUTDOWN:
		containerState = kubeapi.ContainerState_EXITED
	case C.VIR_DOMAIN_SHUTOFF:
		containerState = kubeapi.ContainerState_CREATED
	case C.VIR_DOMAIN_CRASHED:
		containerState = kubeapi.ContainerState_EXITED
	case C.VIR_DOMAIN_PMSUSPENDED:
		containerState = kubeapi.ContainerState_EXITED
	default:
		containerState = kubeapi.ContainerState_UNKNOWN
	}

	return containerState
}

func filterContainer(container *kubeapi.Container, filter *kubeapi.ContainerFilter) bool {
	if filter.State != nil && *container.State != *filter.State {
		return false
	}
	return true
}

func (v *VirtualizationTool) ListContainers(boltClient *bolttools.BoltClient, filter *kubeapi.ContainerFilter) ([]*kubeapi.Container, error) {
	var domainInfo C.virDomainInfo
	var cList *C.virDomainPtr
	count := C.virConnectListAllDomains(v.conn, (**C.virDomainPtr)(&cList), 0)
	if count < 0 {
		return nil, GetLastError()
	}
	header := reflect.SliceHeader{
		Data: uintptr(unsafe.Pointer(cList)),
		Len:  int(count),
		Cap:  int(count),
	}
	domains := *(*[]C.virDomainPtr)(unsafe.Pointer(&header))

	containers := make([]*kubeapi.Container, 0, count)

	for _, domain := range domains {
		id := C.GoString(C.virDomainGetName(domain))

		if status := C.virDomainGetInfo(domain, &domainInfo); status < 0 {
			return nil, GetLastError()
		}

		containerState := libvirtToKubeState(domainInfo)

		metadata := &kubeapi.ContainerMetadata{
			Name: &id,
		}

		labels, err := boltClient.GetLabels(id)
		if err != nil {
			return nil, err
		}
		annotations, err := boltClient.GetAnnotations(id)
		if err != nil {
			return nil, err
		}

		container := &kubeapi.Container{
			Id:          &id,
			State:       &containerState,
			Metadata:    metadata,
			Labels:      labels,
			Annotations: annotations,
		}

		if filterContainer(container, filter) {
			containers = append(containers, container)
		}
	}

	return containers, nil
}

func (v *VirtualizationTool) ContainerStatus(containerId string) (*kubeapi.ContainerStatus, error) {
	var domainInfo C.virDomainInfo

	cContainerId := C.CString(containerId)
	defer C.free(unsafe.Pointer(cContainerId))

	domain := C.virDomainLookupByName(v.conn, cContainerId)
	if domain == nil {
		return nil, GetLastError()
	}
	defer C.virDomainFree(domain)

	id := C.GoString(C.virDomainGetName(domain))

	if status := C.virDomainGetInfo(domain, &domainInfo); status < 0 {
		return nil, GetLastError()
	}

	containerState := libvirtToKubeState(domainInfo)

	return &kubeapi.ContainerStatus{
		Id:       &id,
		Metadata: &kubeapi.ContainerMetadata{},
		State:    &containerState,
	}, nil
}
<|endoftext|>