<commit_before>package crane

import (
	"encoding/json"
	"github.com/stretchr/testify/assert"
	"gopkg.in/v2/yaml"
	"os"
	"testing"
)

func TestImageWithTag(t *testing.T) {
	var c *container
	// No tag
	c = &container{
		RawImage: "ubuntu",
		RawRun:   RunParameters{},
	}
	assert.Equal(t, "ubuntu:latest", c.ImageWithTag())
	// Given tag
	c = &container{
		RawImage: "ubuntu:14.04",
		RawRun:   RunParameters{},
	}
	assert.Equal(t, "ubuntu:14.04", c.ImageWithTag())
	// With port in registry part
	c = &container{
		RawImage: "private.registry.com:5000/ubuntu",
		RawRun:   RunParameters{},
	}
	assert.Equal(t, "ubuntu:latest", c.ImageWithTag())
}

func TestDependencies(t *testing.T) {
	c := &container{
		RawRun: RunParameters{
			RawNet:         "container:n",
			RawLink:        []string{"a:b", "b:d"},
			RawVolumesFrom: []string{"c"},
		},
	}
	expected := &Dependencies{
		All:         []string{"a", "b", "c", "n"},
		Link:        []string{"a", "b"},
		VolumesFrom: []string{"c"},
		Net:         "n",
	}
	assert.Equal(t, expected, c.Dependencies([]string{}))

	c = &container{}
	expected = &Dependencies{}
	assert.Equal(t, expected, c.Dependencies([]string{}))

	c = &container{
		RawRun: RunParameters{
			RawNet:         "container:n",
			RawLink:        []string{"a:b", "b:d"},
			RawVolumesFrom: []string{"c"},
		},
	}
	expected = &Dependencies{
		All:         []string{"a", "c", "n"},
		Link:        []string{"a"},
		VolumesFrom: []string{"c"},
		Net:         "n",
	}
	assert.Equal(t, expected, c.Dependencies([]string{"b"}))
}

func TestVolumesFromSuffixes(t *testing.T) {
	c := &container{RawRun: RunParameters{RawVolumesFrom: []string{"a:rw", "b:ro"}}}
	expected := &Dependencies{
		All:         []string{"a", "b"},
		VolumesFrom: []string{"a", "b"},
	}
	assert.Equal(t, expected, c.Dependencies([]string{}))
}

func TestMultipleLinkAliases(t *testing.T) {
	c := &container{RawRun: RunParameters{RawLink: []string{"a:b", "a:c"}}}
	expected := &Dependencies{
		All:  []string{"a"},
		Link: []string{"a"},
	}
	assert.Equal(t, expected, c.Dependencies([]string{}))
}

func TestImplicitLinkAliases(t *testing.T) {
	c := &container{RawRun: RunParameters{RawLink: []string{"a"}}}
	expected := &Dependencies{
		All:  []string{"a"},
		Link: []string{"a"},
	}
	assert.Equal(t, expected, c.Dependencies([]string{}))
}

func TestVolume(t *testing.T) {
	var c *container
	// Absolute path
	c = &container{RawRun: RunParameters{RawVolume: []string{"/a:b"}}}
	assert.Equal(t, "/a:b", c.RunParams().Volume("foo")[0])
	// Relative path
	c = &container{RawRun: RunParameters{RawVolume: []string{"a:b"}}}
	dir, _ := os.Getwd()
	assert.Equal(t, dir+"/a:b", c.RunParams().Volume(dir)[0])
	// Environment variable
	c = &container{RawRun: RunParameters{RawVolume: []string{"$HOME/a:b"}}}
	os.Clearenv()
	os.Setenv("HOME", "/home")
	assert.Equal(t, os.Getenv("HOME")+"/a:b", c.RunParams().Volume("foo")[0])
	// Container-only path
	c = &container{RawRun: RunParameters{RawVolume: []string{"/b"}}}
	assert.Equal(t, "/b", c.RunParams().Volume("foo")[0])
}

func TestNet(t *testing.T) {
	var c *container
	// Empty defaults to "bridge"
	c = &container{RawRun: RunParameters{}}
	assert.Equal(t, "bridge", c.RunParams().Net())
	// Environment variable
	os.Clearenv()
	os.Setenv("NET", "container")
	c = &container{RawRun: RunParameters{RawNet: "$NET"}}
	assert.Equal(t, "container", c.RunParams().Net())
}

func TestCmd(t *testing.T) {
	var c *container
	// String
	os.Clearenv()
	os.Setenv("CMD", "true")
	c = &container{RawRun: RunParameters{RawCmd: "$CMD"}}
	assert.Equal(t, []string{"true"}, c.RunParams().Cmd())
	// String with multiple parts
	c = &container{RawRun: RunParameters{RawCmd: "bundle exec rails s -p 3000"}}
	assert.Equal(t, []string{"bundle", "exec", "rails", "s", "-p", "3000"}, c.RunParams().Cmd())
	// Array
	os.Clearenv()
	os.Setenv("CMD", "1")
	c = &container{RawRun: RunParameters{RawCmd: []interface{}{"echo", "$CMD"}}}
	if len(c.RunParams().Cmd()) != 2 || c.RunParams().Cmd()[0] != "echo" || c.RunParams().Cmd()[1] != "1" {
		t.Errorf("Command should have been true, got %v", c.RunParams().Cmd())
	}
}

type OptBoolWrapper struct {
	OptBool OptBool `json:"OptBool" yaml:"OptBool"`
}

func TestOptBoolJSON(t *testing.T) {
	wrapper := OptBoolWrapper{}
	json.Unmarshal([]byte("{\"OptBool\": true}"), &wrapper)
	assert.Equal(t, OptBool{Defined: true, Value: true}, wrapper.OptBool)

	wrapper = OptBoolWrapper{}
	json.Unmarshal([]byte("{\"OptBool\": false}"), &wrapper)
	assert.Equal(t, OptBool{Defined: true, Value: false}, wrapper.OptBool)

	wrapper = OptBoolWrapper{}
	json.Unmarshal([]byte("{}"), &wrapper)
	assert.False(t, wrapper.OptBool.Defined)

	wrapper = OptBoolWrapper{}
	err := json.Unmarshal([]byte("{\"OptBool\": \"notaboolean\"}"), &wrapper)
	assert.Error(t, err)
}

func TestOptBoolYAML(t *testing.T) {
	wrapper := OptBoolWrapper{}
	yaml.Unmarshal([]byte("OptBool: true"), &wrapper)
	assert.Equal(t, OptBool{Defined: true, Value: true}, wrapper.OptBool)

	wrapper = OptBoolWrapper{}
	yaml.Unmarshal([]byte("OptBool: false"), &wrapper)
	assert.Equal(t, OptBool{Defined: true, Value: false}, wrapper.OptBool)

	wrapper = OptBoolWrapper{}
	yaml.Unmarshal([]byte(""), &wrapper)
	assert.False(t, wrapper.OptBool.Defined)

	wrapper = OptBoolWrapper{}
	err := yaml.Unmarshal([]byte("OptBool: notaboolean"), &wrapper)
	assert.Error(t, err)
}
<commit_msg>Fix tests involing dependencies<commit_after>package crane

import (
	"encoding/json"
	"github.com/stretchr/testify/assert"
	"gopkg.in/v2/yaml"
	"os"
	"testing"
)

func TestImageWithTag(t *testing.T) {
	var c *container
	// No tag
	c = &container{
		RawImage: "ubuntu",
		RawRun:   RunParameters{},
	}
	assert.Equal(t, "ubuntu:latest", c.ImageWithTag())
	// Given tag
	c = &container{
		RawImage: "ubuntu:14.04",
		RawRun:   RunParameters{},
	}
	assert.Equal(t, "ubuntu:14.04", c.ImageWithTag())
	// With port in registry part
	c = &container{
		RawImage: "private.registry.com:5000/ubuntu",
		RawRun:   RunParameters{},
	}
	assert.Equal(t, "ubuntu:latest", c.ImageWithTag())
}

func TestDependencies(t *testing.T) {
	c := &container{
		RawRun: RunParameters{
			RawNet:         "container:n",
			RawLink:        []string{"a:b", "b:d"},
			RawVolumesFrom: []string{"c"},
		},
	}
	expected := &Dependencies{
		All:         []string{"a", "b", "c", "n"},
		Link:        []string{"a", "b"},
		VolumesFrom: []string{"c"},
		Net:         "n",
	}
	assert.Equal(t, expected, c.Dependencies())

	c = &container{}
	expected = &Dependencies{}
	assert.Equal(t, expected, c.Dependencies())

	c = &container{
		RawRun: RunParameters{
			RawNet:         "container:n",
			RawLink:        []string{"a:b", "b:d"},
			RawVolumesFrom: []string{"c"},
		},
	}
	expected = &Dependencies{
		All:         []string{"a", "c", "n"},
		Link:        []string{"a"},
		VolumesFrom: []string{"c"},
		Net:         "n",
	}
	excluded = []string{"b"}
	assert.Equal(t, expected, c.Dependencies())
	excluded = []string{}
}

func TestVolumesFromSuffixes(t *testing.T) {
	c := &container{RawRun: RunParameters{RawVolumesFrom: []string{"a:rw", "b:ro"}}}
	expected := &Dependencies{
		All:         []string{"a", "b"},
		VolumesFrom: []string{"a", "b"},
	}
	assert.Equal(t, expected, c.Dependencies())
}

func TestMultipleLinkAliases(t *testing.T) {
	c := &container{RawRun: RunParameters{RawLink: []string{"a:b", "a:c"}}}
	expected := &Dependencies{
		All:  []string{"a"},
		Link: []string{"a"},
	}
	assert.Equal(t, expected, c.Dependencies())
}

func TestImplicitLinkAliases(t *testing.T) {
	c := &container{RawRun: RunParameters{RawLink: []string{"a"}}}
	expected := &Dependencies{
		All:  []string{"a"},
		Link: []string{"a"},
	}
	assert.Equal(t, expected, c.Dependencies())
}

func TestVolume(t *testing.T) {
	var c *container
	// Absolute path
	c = &container{RawRun: RunParameters{RawVolume: []string{"/a:b"}}}
	assert.Equal(t, "/a:b", c.RunParams().Volume("foo")[0])
	// Relative path
	c = &container{RawRun: RunParameters{RawVolume: []string{"a:b"}}}
	dir, _ := os.Getwd()
	assert.Equal(t, dir+"/a:b", c.RunParams().Volume(dir)[0])
	// Environment variable
	c = &container{RawRun: RunParameters{RawVolume: []string{"$HOME/a:b"}}}
	os.Clearenv()
	os.Setenv("HOME", "/home")
	assert.Equal(t, os.Getenv("HOME")+"/a:b", c.RunParams().Volume("foo")[0])
	// Container-only path
	c = &container{RawRun: RunParameters{RawVolume: []string{"/b"}}}
	assert.Equal(t, "/b", c.RunParams().Volume("foo")[0])
}

func TestNet(t *testing.T) {
	var c *container
	// Empty defaults to "bridge"
	c = &container{RawRun: RunParameters{}}
	assert.Equal(t, "bridge", c.RunParams().Net())
	// Environment variable
	os.Clearenv()
	os.Setenv("NET", "container")
	c = &container{RawRun: RunParameters{RawNet: "$NET"}}
	assert.Equal(t, "container", c.RunParams().Net())
}

func TestCmd(t *testing.T) {
	var c *container
	// String
	os.Clearenv()
	os.Setenv("CMD", "true")
	c = &container{RawRun: RunParameters{RawCmd: "$CMD"}}
	assert.Equal(t, []string{"true"}, c.RunParams().Cmd())
	// String with multiple parts
	c = &container{RawRun: RunParameters{RawCmd: "bundle exec rails s -p 3000"}}
	assert.Equal(t, []string{"bundle", "exec", "rails", "s", "-p", "3000"}, c.RunParams().Cmd())
	// Array
	os.Clearenv()
	os.Setenv("CMD", "1")
	c = &container{RawRun: RunParameters{RawCmd: []interface{}{"echo", "$CMD"}}}
	if len(c.RunParams().Cmd()) != 2 || c.RunParams().Cmd()[0] != "echo" || c.RunParams().Cmd()[1] != "1" {
		t.Errorf("Command should have been true, got %v", c.RunParams().Cmd())
	}
}

type OptBoolWrapper struct {
	OptBool OptBool `json:"OptBool" yaml:"OptBool"`
}

func TestOptBoolJSON(t *testing.T) {
	wrapper := OptBoolWrapper{}
	json.Unmarshal([]byte("{\"OptBool\": true}"), &wrapper)
	assert.Equal(t, OptBool{Defined: true, Value: true}, wrapper.OptBool)

	wrapper = OptBoolWrapper{}
	json.Unmarshal([]byte("{\"OptBool\": false}"), &wrapper)
	assert.Equal(t, OptBool{Defined: true, Value: false}, wrapper.OptBool)

	wrapper = OptBoolWrapper{}
	json.Unmarshal([]byte("{}"), &wrapper)
	assert.False(t, wrapper.OptBool.Defined)

	wrapper = OptBoolWrapper{}
	err := json.Unmarshal([]byte("{\"OptBool\": \"notaboolean\"}"), &wrapper)
	assert.Error(t, err)
}

func TestOptBoolYAML(t *testing.T) {
	wrapper := OptBoolWrapper{}
	yaml.Unmarshal([]byte("OptBool: true"), &wrapper)
	assert.Equal(t, OptBool{Defined: true, Value: true}, wrapper.OptBool)

	wrapper = OptBoolWrapper{}
	yaml.Unmarshal([]byte("OptBool: false"), &wrapper)
	assert.Equal(t, OptBool{Defined: true, Value: false}, wrapper.OptBool)

	wrapper = OptBoolWrapper{}
	yaml.Unmarshal([]byte(""), &wrapper)
	assert.False(t, wrapper.OptBool.Defined)

	wrapper = OptBoolWrapper{}
	err := yaml.Unmarshal([]byte("OptBool: notaboolean"), &wrapper)
	assert.Error(t, err)
}
<|endoftext|>


<commit_before>package release

import (
	"flag"
	"io/ioutil"
	"os"
	"testing"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/goreleaser/goreleaser/internal/artifact"
	"github.com/stretchr/testify/assert"
)

var update = flag.Bool("update", false, "update .golden files")

func TestDescribeBody(t *testing.T) {
	var changelog = "\nfeature1: description\nfeature2: other description"
	var ctx = context.New(config.Project{})
	ctx.ReleaseNotes = changelog
	for _, d := range []string{
		"goreleaser/goreleaser:0.40.0",
		"goreleaser/goreleaser:latest",
		"goreleaser/godownloader:v0.1.0",
	} {
		ctx.Artifacts.Add(artifact.Artifact{
			Name: d,
			Type: artifact.DockerImage,
		})
	}
	out, err := describeBodyVersion(ctx, "go version go1.9 darwin/amd64")
	assert.NoError(t, err)

	var golden = "testdata/release1.golden"
	if *update {
		ioutil.WriteFile(golden, out.Bytes(), 0755)
	}
	bts, err := ioutil.ReadFile(golden)
	assert.NoError(t, err)
	assert.Equal(t, string(bts), out.String())
}

func TestDescribeBodyNoDockerImagesNoBrews(t *testing.T) {
	var changelog = "\nfeature1: description\nfeature2: other description"
	var ctx = &context.Context{
		ReleaseNotes: changelog,
	}
	out, err := describeBodyVersion(ctx, "go version go1.9 darwin/amd64")
	assert.NoError(t, err)

	var golden = "testdata/release2.golden"
	if *update {
		ioutil.WriteFile(golden, out.Bytes(), 0655)
	}
	bts, err := ioutil.ReadFile(golden)
	assert.NoError(t, err)

	assert.Equal(t, string(bts), out.String())
}

func TestDontEscapeHTML(t *testing.T) {
	var changelog = "
test
"
	var ctx = &context.Context{
		ReleaseNotes: changelog,
	}
	out, err := describeBody(ctx)
	assert.NoError(t, err)
	assert.Contains(t, out.String(), changelog)
}

func TestGoVersionFails(t *testing.T) {
	var path = os.Getenv("PATH")
	defer func() {
		assert.NoError(t, os.Setenv("PATH", path))
	}()
	assert.NoError(t, os.Setenv("PATH", ""))
	var ctx = &context.Context{
		ReleaseNotes: "changelog",
	}
	_, err := describeBody(ctx)
	assert.Error(t, err)
}
<commit_msg>test: fixed release tests<commit_after>package release

import (
	"flag"
	"io/ioutil"
	"os"
	"testing"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/goreleaser/goreleaser/internal/artifact"
	"github.com/stretchr/testify/assert"
)

var update = flag.Bool("update", false, "update .golden files")

func TestDescribeBody(t *testing.T) {
	var changelog = "\nfeature1: description\nfeature2: other description"
	var ctx = context.New(config.Project{})
	ctx.ReleaseNotes = changelog
	for _, d := range []string{
		"goreleaser/goreleaser:0.40.0",
		"goreleaser/goreleaser:latest",
		"goreleaser/godownloader:v0.1.0",
	} {
		ctx.Artifacts.Add(artifact.Artifact{
			Name: d,
			Type: artifact.DockerImage,
		})
	}
	out, err := describeBodyVersion(ctx, "go version go1.9 darwin/amd64")
	assert.NoError(t, err)

	var golden = "testdata/release1.golden"
	if *update {
		ioutil.WriteFile(golden, out.Bytes(), 0755)
	}
	bts, err := ioutil.ReadFile(golden)
	assert.NoError(t, err)
	assert.Equal(t, string(bts), out.String())
}

func TestDescribeBodyNoDockerImagesNoBrews(t *testing.T) {
	var changelog = "\nfeature1: description\nfeature2: other description"
	var ctx = &context.Context{
		ReleaseNotes: changelog,
	}
	out, err := describeBodyVersion(ctx, "go version go1.9 darwin/amd64")
	assert.NoError(t, err)

	var golden = "testdata/release2.golden"
	if *update {
		ioutil.WriteFile(golden, out.Bytes(), 0655)
	}
	bts, err := ioutil.ReadFile(golden)
	assert.NoError(t, err)

	assert.Equal(t, string(bts), out.String())
}

func TestDontEscapeHTML(t *testing.T) {
	var changelog = "
test
"
	var ctx = context.New(config.Project{})
	ctx.ReleaseNotes = changelog

	out, err := describeBody(ctx)
	assert.NoError(t, err)
	assert.Contains(t, out.String(), changelog)
}

func TestGoVersionFails(t *testing.T) {
	var path = os.Getenv("PATH")
	defer func() {
		assert.NoError(t, os.Setenv("PATH", path))
	}()
	assert.NoError(t, os.Setenv("PATH", ""))
	var ctx = &context.Context{
		ReleaseNotes: "changelog",
	}
	_, err := describeBody(ctx)
	assert.Error(t, err)
}
<|endoftext|>


<commit_before>package nativeregion

import (
	"image"
	"functorama.com/demo/base"
	"functorama.com/demo/nativebase"
	"functorama.com/demo/nativesequence"
	"functorama.com/demo/region"
)

type nativeSubregion struct {
	populated bool
	children  []nativeRegion
}

type nativeMandelbrotThunk struct {
	nativebase.NativeMandelbrotMember
	evaluated bool
}

type nativeRegion struct {
	topLeft     nativeMandelbrotThunk
	topRight    nativeMandelbrotThunk
	bottomLeft  nativeMandelbrotThunk
	bottomRight nativeMandelbrotThunk
	midPoint    nativeMandelbrotThunk
}

func (region *nativeRegion) rect(base *nativebase.NativeBaseNumerics) image.Rectangle {
	l, t := base.PlaneToPixel(region.topLeft.C)
	r, b := base.PlaneToPixel(region.bottomRight.C)
	return image.Rect(int(l), int(t), int(r), int(b))
}

// Extend NativeBaseNumerics and add support for regions
type NativeRegionNumerics struct {
	region.RegionConfig
	nativebase.NativeBaseNumerics
	Region             nativeRegion
	SequenceNumerics   *nativesequence.NativeSequenceNumerics
	subregion          nativeSubregion
}

// Check that we implement the interface
var _ region.RegionNumerics = (*NativeRegionNumerics)(nil)

func NewRegionNumerics(specialized nativebase.NativeBaseNumerics, config region.RegionConfig, seq *nativesequence.NativeSequenceNumerics) *NativeRegionNumerics {
	planeMin := complex(specialized.RealMin, specialized.ImagMin)
	planeMax := complex(specialized.RealMax, specialized.ImagMax)
	return &NativeRegionNumerics{
		RegionConfig: config,
		NativeBaseNumerics: specialized,
		SequenceNumerics: seq,
		Region: createNativeRegion(planeMin, planeMax),
	}
}

func (native *NativeRegionNumerics) ClaimExtrinsics() {
	// Region already present
}

// Return the children of this region
// This implementation does not create many new objects
func (native *NativeRegionNumerics) Children() []region.RegionNumerics {
	const childCount = 4
	if native.subregion.populated {
		nextContexts := make([]region.RegionNumerics, childCount)
		for i, child := range native.subregion.children {
			nextContexts[i] = native.Proxy(child)
		}
		return nextContexts
	}
	panic("Region asked to provide non-existent children")
	return nil
}

// Return the children of this region without hiding their types
// This implementation does not create many new objects
func (native *NativeRegionNumerics) NativeChildRegions() []nativeRegion {
	if native.subregion.populated {
		return native.subregion.children
	}
	panic("Region asked to provide non-existent children")
	return nil
}

func (native *NativeRegionNumerics) RegionSequence() region.ProxySequence {
	return native.NativeSequence()
}

func (native *NativeRegionNumerics) NativeSequence() NativeSequenceProxy {
	return NativeSequenceProxy{
		LocalRegion:   native.Region,
		NativeSequenceNumerics: native.SequenceNumerics,
	}
}

func (native *NativeRegionNumerics) Proxy(region nativeRegion) NativeRegionProxy {
	return NativeRegionProxy{
		LocalRegion:   region,
		NativeRegionNumerics: native,
	}
}

func (native *NativeRegionNumerics) MandelbrotPoints() []base.MandelbrotMember {
	r := native.Region
	return []base.MandelbrotMember{
		r.topLeft,
		r.topRight,
		r.bottomLeft,
		r.bottomRight,
		r.midPoint,
	}
}

func (native *NativeRegionNumerics) EvaluateAllPoints(iterateLimit uint8) {
	points := []*nativeMandelbrotThunk{
		&native.Region.topLeft,
		&native.Region.topRight,
		&native.Region.bottomLeft,
		&native.Region.bottomRight,
		&native.Region.midPoint,
	}
	// Ensure points are all evaluated
	for _, p := range points {
		if !p.evaluated {
			p.Mandelbrot(iterateLimit)
			p.evaluated = true
		}
	}
}

// A glitch is possible when points are uniform near the set
// Due to the shape of the set, a rectangular nativeRegion is not a good approximation
// An anologous glitch happens when the entire nativeRegion is much larger than the set
// We handle both these cases here
func (native *NativeRegionNumerics) OnGlitchCurve(iterateLimit uint8, glitchSamples uint) bool {
	r := native.Region
	tlMember := r.topLeft
	iDiv := tlMember.InvDivergence
	if iDiv == 0 || iDiv == 1 || tlMember.InSet {
		sqrtDLimit := native.SqrtDivergeLimit
		glitchSamplesF := float64(glitchSamples)
		tl := tlMember.C
		br := r.bottomRight.C
		w := real(br) - real(tl)
		h := imag(tl) - imag(br)
		vUnit := h / glitchSamplesF
		hUnit := w / glitchSamplesF
		x := real(tl)
		for i := uint(0); i < glitchSamples; i++ {
			y := imag(tl)
			for j := uint(0); j < glitchSamples; j++ {
				checkMember := nativebase.NativeMandelbrotMember{
					C: complex(x, y),
					SqrtDivergeLimit: sqrtDLimit,
				}
				checkMember.Mandelbrot(iterateLimit)
				if checkMember.InvDivergence != iDiv {
					return true
				}
				y -= vUnit
			}
			x += hUnit
		}
	}
	return false
}

func (native *NativeRegionNumerics) Split() {
	r := native.Region

	topLeftPos := r.topLeft.C
	bottomRightPos := r.bottomRight.C
	midPos := r.midPoint.C

	left := real(topLeftPos)
	right := real(bottomRightPos)
	top := imag(topLeftPos)
	bottom := imag(bottomRightPos)
	midR := real(midPos)
	midI := imag(midPos)

	leftSectorMid := (midR + left) / 2.0
	rightSectorMid := (right + midR) / 2.0
	topSectorMid := (top + midI) / 2.0
	bottomSectorMid := (midI + bottom) / 2.0

	topSideMid := native.createThunk(complex(midR, top))
	bottomSideMid := native.createThunk(complex(midR, bottom))
	leftSideMid := native.createThunk(complex(left, midI))
	rightSideMid := native.createThunk(complex(right, midI))

	topLeftMid := native.createThunk(complex(leftSectorMid, topSectorMid))
	topRightMid := native.createThunk(complex(rightSectorMid, topSectorMid))
	bottomLeftMid := native.createThunk(complex(leftSectorMid, bottomSectorMid))
	bottomRightMid := native.createThunk(complex(rightSectorMid, bottomSectorMid))

	tl := nativeRegion{
		topLeft:     r.topLeft,
		topRight:    topSideMid,
		bottomLeft:  leftSideMid,
		bottomRight: r.midPoint,
		midPoint:    topLeftMid,
	}
	tr := nativeRegion{
		topLeft:     topSideMid,
		topRight:    r.topRight,
		bottomLeft:  r.midPoint,
		bottomRight: rightSideMid,
		midPoint:    topRightMid,
	}
	bl := nativeRegion{
		topLeft:     leftSideMid,
		topRight:    r.midPoint,
		bottomLeft:  r.bottomLeft,
		bottomRight: bottomSideMid,
		midPoint:    bottomLeftMid,
	}
	br := nativeRegion{
		topLeft:     r.midPoint,
		topRight:    rightSideMid,
		bottomLeft:  bottomSideMid,
		bottomRight: r.bottomRight,
		midPoint:    bottomRightMid,
	}

	native.subregion = nativeSubregion{
		populated: true,
		children:  []nativeRegion{tl, tr, bl, br},
	}
}

func (native *NativeRegionNumerics) Rect() image.Rectangle {
	base := native.NativeBaseNumerics
	return native.Region.rect(&base)
}

// Return MandelbrotMember
// Does not check if the region's thunks have been evaluated
func (native *NativeRegionNumerics) RegionMember() base.MandelbrotMember {
	return native.Region.topLeft
}

func (native *NativeRegionNumerics) createThunk(c complex128) nativeMandelbrotThunk {
	return nativeMandelbrotThunk{
		NativeMandelbrotMember: native.CreateMandelbrot(c),
	}
}

func (native *NativeRegionNumerics) thunks() []nativeMandelbrotThunk {
	region := native.Region
	return []nativeMandelbrotThunk{
		region.topLeft,
		region.topRight,
		region.bottomLeft,
		region.bottomRight,
		region.midPoint,
	}
}

func createNativeRegion(min complex128, max complex128) nativeRegion {
	left := real(min)
	right := real(max)
	top := imag(max)
	bottom := imag(min)
	mid := ((max - min) / 2) + min

	points := []complex128{
		complex(left, top),
		complex(right, top),
		complex(left, bottom),
		complex(right, bottom),
		mid,
	}

	thunks := make([]nativeMandelbrotThunk, len(points))
	for i, c := range points {
		thunks[i] = nativeMandelbrotThunk{
			NativeMandelbrotMember: nativebase.NativeMandelbrotMember{C: c, SqrtDivergeLimit: 2.0},
		}
	}

	region := nativeRegion{
		topLeft: thunks[0],
		topRight: thunks[1],
		bottomLeft: thunks[2],
		bottomRight: thunks[3],
		midPoint: thunks[4],
	}

	return region
}<commit_msg>Remove useless type conversion<commit_after>package nativeregion

import (
	"image"
	"functorama.com/demo/base"
	"functorama.com/demo/nativebase"
	"functorama.com/demo/nativesequence"
	"functorama.com/demo/region"
)

type nativeSubregion struct {
	populated bool
	children  []nativeRegion
}

type nativeMandelbrotThunk struct {
	nativebase.NativeMandelbrotMember
	evaluated bool
}

type nativeRegion struct {
	topLeft     nativeMandelbrotThunk
	topRight    nativeMandelbrotThunk
	bottomLeft  nativeMandelbrotThunk
	bottomRight nativeMandelbrotThunk
	midPoint    nativeMandelbrotThunk
}

func (region *nativeRegion) rect(base *nativebase.NativeBaseNumerics) image.Rectangle {
	l, t := base.PlaneToPixel(region.topLeft.C)
	r, b := base.PlaneToPixel(region.bottomRight.C)
	return image.Rect(l, t, r, b)
}

// Extend NativeBaseNumerics and add support for regions
type NativeRegionNumerics struct {
	region.RegionConfig
	nativebase.NativeBaseNumerics
	Region             nativeRegion
	SequenceNumerics   *nativesequence.NativeSequenceNumerics
	subregion          nativeSubregion
}

// Check that we implement the interface
var _ region.RegionNumerics = (*NativeRegionNumerics)(nil)

func NewRegionNumerics(specialized nativebase.NativeBaseNumerics, config region.RegionConfig, seq *nativesequence.NativeSequenceNumerics) *NativeRegionNumerics {
	planeMin := complex(specialized.RealMin, specialized.ImagMin)
	planeMax := complex(specialized.RealMax, specialized.ImagMax)
	return &NativeRegionNumerics{
		RegionConfig: config,
		NativeBaseNumerics: specialized,
		SequenceNumerics: seq,
		Region: createNativeRegion(planeMin, planeMax),
	}
}

func (native *NativeRegionNumerics) ClaimExtrinsics() {
	// Region already present
}

// Return the children of this region
// This implementation does not create many new objects
func (native *NativeRegionNumerics) Children() []region.RegionNumerics {
	const childCount = 4
	if native.subregion.populated {
		nextContexts := make([]region.RegionNumerics, childCount)
		for i, child := range native.subregion.children {
			nextContexts[i] = native.Proxy(child)
		}
		return nextContexts
	}
	panic("Region asked to provide non-existent children")
	return nil
}

// Return the children of this region without hiding their types
// This implementation does not create many new objects
func (native *NativeRegionNumerics) NativeChildRegions() []nativeRegion {
	if native.subregion.populated {
		return native.subregion.children
	}
	panic("Region asked to provide non-existent children")
	return nil
}

func (native *NativeRegionNumerics) RegionSequence() region.ProxySequence {
	return native.NativeSequence()
}

func (native *NativeRegionNumerics) NativeSequence() NativeSequenceProxy {
	return NativeSequenceProxy{
		LocalRegion:   native.Region,
		NativeSequenceNumerics: native.SequenceNumerics,
	}
}

func (native *NativeRegionNumerics) Proxy(region nativeRegion) NativeRegionProxy {
	return NativeRegionProxy{
		LocalRegion:   region,
		NativeRegionNumerics: native,
	}
}

func (native *NativeRegionNumerics) MandelbrotPoints() []base.MandelbrotMember {
	r := native.Region
	return []base.MandelbrotMember{
		r.topLeft,
		r.topRight,
		r.bottomLeft,
		r.bottomRight,
		r.midPoint,
	}
}

func (native *NativeRegionNumerics) EvaluateAllPoints(iterateLimit uint8) {
	points := []*nativeMandelbrotThunk{
		&native.Region.topLeft,
		&native.Region.topRight,
		&native.Region.bottomLeft,
		&native.Region.bottomRight,
		&native.Region.midPoint,
	}
	// Ensure points are all evaluated
	for _, p := range points {
		if !p.evaluated {
			p.Mandelbrot(iterateLimit)
			p.evaluated = true
		}
	}
}

// A glitch is possible when points are uniform near the set
// Due to the shape of the set, a rectangular nativeRegion is not a good approximation
// An anologous glitch happens when the entire nativeRegion is much larger than the set
// We handle both these cases here
func (native *NativeRegionNumerics) OnGlitchCurve(iterateLimit uint8, glitchSamples uint) bool {
	r := native.Region
	tlMember := r.topLeft
	iDiv := tlMember.InvDivergence
	if iDiv == 0 || iDiv == 1 || tlMember.InSet {
		sqrtDLimit := native.SqrtDivergeLimit
		glitchSamplesF := float64(glitchSamples)
		tl := tlMember.C
		br := r.bottomRight.C
		w := real(br) - real(tl)
		h := imag(tl) - imag(br)
		vUnit := h / glitchSamplesF
		hUnit := w / glitchSamplesF
		x := real(tl)
		for i := uint(0); i < glitchSamples; i++ {
			y := imag(tl)
			for j := uint(0); j < glitchSamples; j++ {
				checkMember := nativebase.NativeMandelbrotMember{
					C: complex(x, y),
					SqrtDivergeLimit: sqrtDLimit,
				}
				checkMember.Mandelbrot(iterateLimit)
				if checkMember.InvDivergence != iDiv {
					return true
				}
				y -= vUnit
			}
			x += hUnit
		}
	}
	return false
}

func (native *NativeRegionNumerics) Split() {
	r := native.Region

	topLeftPos := r.topLeft.C
	bottomRightPos := r.bottomRight.C
	midPos := r.midPoint.C

	left := real(topLeftPos)
	right := real(bottomRightPos)
	top := imag(topLeftPos)
	bottom := imag(bottomRightPos)
	midR := real(midPos)
	midI := imag(midPos)

	leftSectorMid := (midR + left) / 2.0
	rightSectorMid := (right + midR) / 2.0
	topSectorMid := (top + midI) / 2.0
	bottomSectorMid := (midI + bottom) / 2.0

	topSideMid := native.createThunk(complex(midR, top))
	bottomSideMid := native.createThunk(complex(midR, bottom))
	leftSideMid := native.createThunk(complex(left, midI))
	rightSideMid := native.createThunk(complex(right, midI))

	topLeftMid := native.createThunk(complex(leftSectorMid, topSectorMid))
	topRightMid := native.createThunk(complex(rightSectorMid, topSectorMid))
	bottomLeftMid := native.createThunk(complex(leftSectorMid, bottomSectorMid))
	bottomRightMid := native.createThunk(complex(rightSectorMid, bottomSectorMid))

	tl := nativeRegion{
		topLeft:     r.topLeft,
		topRight:    topSideMid,
		bottomLeft:  leftSideMid,
		bottomRight: r.midPoint,
		midPoint:    topLeftMid,
	}
	tr := nativeRegion{
		topLeft:     topSideMid,
		topRight:    r.topRight,
		bottomLeft:  r.midPoint,
		bottomRight: rightSideMid,
		midPoint:    topRightMid,
	}
	bl := nativeRegion{
		topLeft:     leftSideMid,
		topRight:    r.midPoint,
		bottomLeft:  r.bottomLeft,
		bottomRight: bottomSideMid,
		midPoint:    bottomLeftMid,
	}
	br := nativeRegion{
		topLeft:     r.midPoint,
		topRight:    rightSideMid,
		bottomLeft:  bottomSideMid,
		bottomRight: r.bottomRight,
		midPoint:    bottomRightMid,
	}

	native.subregion = nativeSubregion{
		populated: true,
		children:  []nativeRegion{tl, tr, bl, br},
	}
}

func (native *NativeRegionNumerics) Rect() image.Rectangle {
	base := native.NativeBaseNumerics
	return native.Region.rect(&base)
}

// Return MandelbrotMember
// Does not check if the region's thunks have been evaluated
func (native *NativeRegionNumerics) RegionMember() base.MandelbrotMember {
	return native.Region.topLeft
}

func (native *NativeRegionNumerics) createThunk(c complex128) nativeMandelbrotThunk {
	return nativeMandelbrotThunk{
		NativeMandelbrotMember: native.CreateMandelbrot(c),
	}
}

func (native *NativeRegionNumerics) thunks() []nativeMandelbrotThunk {
	region := native.Region
	return []nativeMandelbrotThunk{
		region.topLeft,
		region.topRight,
		region.bottomLeft,
		region.bottomRight,
		region.midPoint,
	}
}

func createNativeRegion(min complex128, max complex128) nativeRegion {
	left := real(min)
	right := real(max)
	top := imag(max)
	bottom := imag(min)
	mid := ((max - min) / 2) + min

	points := []complex128{
		complex(left, top),
		complex(right, top),
		complex(left, bottom),
		complex(right, bottom),
		mid,
	}

	thunks := make([]nativeMandelbrotThunk, len(points))
	for i, c := range points {
		thunks[i] = nativeMandelbrotThunk{
			NativeMandelbrotMember: nativebase.NativeMandelbrotMember{C: c, SqrtDivergeLimit: 2.0},
		}
	}

	region := nativeRegion{
		topLeft: thunks[0],
		topRight: thunks[1],
		bottomLeft: thunks[2],
		bottomRight: thunks[3],
		midPoint: thunks[4],
	}

	return region
}<|endoftext|>


<commit_before>// Copyright 2016 The etcd-operator 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 cluster

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"time"

	"github.com/coreos/etcd-operator/client/experimentalclient"
	"github.com/coreos/etcd-operator/pkg/backup/backupapi"
	"github.com/coreos/etcd-operator/pkg/cluster/backupstorage"
	"github.com/coreos/etcd-operator/pkg/spec"
	"github.com/coreos/etcd-operator/pkg/util/k8sutil"

	"github.com/Sirupsen/logrus"
	"github.com/coreos/etcd-operator/pkg/util/constants"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	appsv1beta1 "k8s.io/client-go/pkg/apis/apps/v1beta1"
)

const (
	defaultBackupHTTPTimeout     = 5 * time.Second
	defaultBackupCreatingTimeout = 1 * time.Minute
)

var (
	errNoPVForBackup       = errors.New("no backup could be created due to PVProvisioner (none) is set")
	errNoS3ConfigForBackup = errors.New("no backup could be created due to S3 configuration not set")
)

type backupManager struct {
	logger *logrus.Entry

	config  Config
	cluster *spec.Cluster
	s       backupstorage.Storage

	bc experimentalclient.Backup
}

func newBackupManager(c Config, cl *spec.Cluster, l *logrus.Entry) (*backupManager, error) {
	bm := &backupManager{
		config:  c,
		cluster: cl,
		logger:  l,
		bc:      experimentalclient.NewBackup(&http.Client{}, "http", cl.Metadata.GetName()),
	}
	var err error
	bm.s, err = bm.setupStorage()
	if err != nil {
		return nil, err
	}
	return bm, nil
}

// setupStorage will only set up the necessary structs in order for backup manager to
// use the storage. It doesn't creates the actual storage here.
// We also need to check some restore logic in setup().
func (bm *backupManager) setupStorage() (s backupstorage.Storage, err error) {
	cl, c := bm.cluster, bm.config

	b := cl.Spec.Backup
	switch b.StorageType {
	case spec.BackupStorageTypePersistentVolume, spec.BackupStorageTypeDefault:
		if c.PVProvisioner == constants.PVProvisionerNone {
			return nil, errNoPVForBackup
		}
		s, err = backupstorage.NewPVStorage(c.KubeCli, cl.Metadata.Name, cl.Metadata.Namespace, c.PVProvisioner, *b)
	case spec.BackupStorageTypeS3:
		if len(c.S3Context.AWSConfig) == 0 {
			return nil, errNoS3ConfigForBackup
		}
		s, err = backupstorage.NewS3Storage(c.S3Context, c.KubeCli, cl.Metadata.Name, cl.Metadata.Namespace, *b)
	}
	return s, err
}

func (bm *backupManager) setup() error {
	r := bm.cluster.Spec.Restore
	restoreSameNameCluster := r != nil && r.BackupClusterName == bm.cluster.Metadata.Name

	// There is only one case that we don't need to create underlying storage.
	// That is, the storage already exists and we are restoring cluster from it.
	if !restoreSameNameCluster {
		if err := bm.s.Create(); err != nil {
			return err
		}
	}

	if r != nil {
		bm.logger.Infof("restoring cluster from existing backup (%s)", r.BackupClusterName)
		if bm.cluster.Metadata.Name != r.BackupClusterName {
			if err := bm.s.Clone(r.BackupClusterName); err != nil {
				return err
			}
		}
	}

	return bm.runSidecar()
}

func (bm *backupManager) runSidecar() error {
	if err := bm.createSidecarDeployment(); err != nil {
		return fmt.Errorf("failed to create backup sidecar Deployment: %v", err)
	}
	if err := bm.createBackupService(); err != nil {
		return fmt.Errorf("failed to create backup sidecar service: %v", err)
	}
	bm.logger.Info("backup sidecar deployment and service created")
	return nil
}

func (bm *backupManager) createSidecarDeployment() error {
	cl, c := bm.cluster, bm.config

	podTemplate := k8sutil.NewBackupPodTemplate(cl.Metadata.Name, bm.config.ServiceAccount, cl.Spec)
	switch cl.Spec.Backup.StorageType {
	case spec.BackupStorageTypeDefault, spec.BackupStorageTypePersistentVolume:
		k8sutil.PodSpecWithPV(&podTemplate.Spec, cl.Metadata.Name)
	case spec.BackupStorageTypeS3:
		k8sutil.PodSpecWithS3(&podTemplate.Spec, c.S3Context)
	}

	name := k8sutil.BackupSidecarName(cl.Metadata.Name)
	dplSel := k8sutil.LabelsForCluster(cl.Metadata.Name)

	d := k8sutil.NewBackupDeploymentManifest(name, dplSel, podTemplate, bm.cluster.AsOwner())
	_, err := c.KubeCli.AppsV1beta1().Deployments(cl.Metadata.Namespace).Create(d)
	return err
}

func (bm *backupManager) createBackupService() error {
	svc := k8sutil.NewBackupServiceManifest(bm.cluster.Metadata.Name, bm.cluster.AsOwner())
	_, err := bm.config.KubeCli.CoreV1().Services(bm.cluster.Metadata.Namespace).Create(svc)
	if err != nil {
		if !k8sutil.IsKubernetesResourceAlreadyExistError(err) {
			return err
		}
	}
	return nil
}

func (bm *backupManager) cleanup() error {
	// Only need to delete storage, Kubernetes related resources will be deleted by the GC.
	err := bm.s.Delete()
	if err != nil {
		return fmt.Errorf("fail to delete backup storage: %v", err)
	}
	return nil
}

func (bm *backupManager) requestBackup() error {
	ctx, cancel := context.WithTimeout(context.Background(), defaultBackupHTTPTimeout+defaultBackupCreatingTimeout)
	defer cancel()
	return bm.bc.Request(ctx)
}

func (bm *backupManager) checkBackupExist(ver string) (bool, error) {
	ctx, cancel := context.WithTimeout(context.Background(), defaultBackupHTTPTimeout)
	defer cancel()
	return bm.bc.Exist(ctx, ver)
}

func (bm *backupManager) getStatus() (*backupapi.ServiceStatus, error) {
	ctx, cancel := context.WithTimeout(context.Background(), defaultBackupHTTPTimeout)
	defer cancel()
	return bm.bc.ServiceStatus(ctx)
}

func backupServiceStatusToTPRBackupServiceStatu(s *backupapi.ServiceStatus) *spec.BackupServiceStatus {
	b, err := json.Marshal(s)
	if err != nil {
		panic("unexpected json error")
	}

	var bs spec.BackupServiceStatus
	err = json.Unmarshal(b, &bs)
	if err != nil {
		panic("unexpected json error")
	}
	return &bs
}

func (bm *backupManager) upgradeIfNeeded() error {
	cl, c := bm.cluster, bm.config
	sidecarName := k8sutil.BackupSidecarName(cl.Metadata.Name)

	// backward compatibility: backup sidecar replica set existed and we need change that to deployment.
	// TODO: remove this after v0.2.6 .
	_, err := c.KubeCli.ExtensionsV1beta1().ReplicaSets(cl.Metadata.Namespace).Get(sidecarName, metav1.GetOptions{})
	if err == nil {
		err = c.KubeCli.ExtensionsV1beta1().ReplicaSets(cl.Metadata.Namespace).Delete(sidecarName, &metav1.DeleteOptions{
			GracePeriodSeconds: func(t int64) *int64 { return &t }(0),
			PropagationPolicy: func() *metav1.DeletionPropagation {
				foreground := metav1.DeletePropagationForeground
				return &foreground
			}(),
		})
		if err != nil {
			return err
		}
		// Note: If RS was deleted but deployment failed to be created, then the cluster's phase would be switched to FAILED.
		return bm.createSidecarDeployment()
	}
	if !apierrors.IsNotFound(err) {
		return err
	}
	// The following path should be run only if no previous backup RS existed.

	d, err := c.KubeCli.AppsV1beta1().Deployments(cl.Metadata.Namespace).Get(sidecarName, metav1.GetOptions{})
	if err != nil {
		return err
	}
	cd := k8sutil.CloneDeployment(d)
	cd.Spec.Template.Spec.Containers[0].Image = k8sutil.BackupImage
	patchData, err := k8sutil.CreatePatch(d, cd, appsv1beta1.Deployment{})
	_, err = c.KubeCli.AppsV1beta1().Deployments(cl.Metadata.Namespace).Patch(d.Name, types.StrategicMergePatchType, patchData)
	return err
}
<commit_msg>cluster: upgrade backup sidecar should also change it to use Recreate strategy<commit_after>// Copyright 2016 The etcd-operator 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 cluster

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"time"

	"github.com/coreos/etcd-operator/client/experimentalclient"
	"github.com/coreos/etcd-operator/pkg/backup/backupapi"
	"github.com/coreos/etcd-operator/pkg/cluster/backupstorage"
	"github.com/coreos/etcd-operator/pkg/spec"
	"github.com/coreos/etcd-operator/pkg/util/k8sutil"

	"github.com/Sirupsen/logrus"
	"github.com/coreos/etcd-operator/pkg/util/constants"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	appsv1beta1 "k8s.io/client-go/pkg/apis/apps/v1beta1"
)

const (
	defaultBackupHTTPTimeout     = 5 * time.Second
	defaultBackupCreatingTimeout = 1 * time.Minute
)

var (
	errNoPVForBackup       = errors.New("no backup could be created due to PVProvisioner (none) is set")
	errNoS3ConfigForBackup = errors.New("no backup could be created due to S3 configuration not set")
)

type backupManager struct {
	logger *logrus.Entry

	config  Config
	cluster *spec.Cluster
	s       backupstorage.Storage

	bc experimentalclient.Backup
}

func newBackupManager(c Config, cl *spec.Cluster, l *logrus.Entry) (*backupManager, error) {
	bm := &backupManager{
		config:  c,
		cluster: cl,
		logger:  l,
		bc:      experimentalclient.NewBackup(&http.Client{}, "http", cl.Metadata.GetName()),
	}
	var err error
	bm.s, err = bm.setupStorage()
	if err != nil {
		return nil, err
	}
	return bm, nil
}

// setupStorage will only set up the necessary structs in order for backup manager to
// use the storage. It doesn't creates the actual storage here.
// We also need to check some restore logic in setup().
func (bm *backupManager) setupStorage() (s backupstorage.Storage, err error) {
	cl, c := bm.cluster, bm.config

	b := cl.Spec.Backup
	switch b.StorageType {
	case spec.BackupStorageTypePersistentVolume, spec.BackupStorageTypeDefault:
		if c.PVProvisioner == constants.PVProvisionerNone {
			return nil, errNoPVForBackup
		}
		s, err = backupstorage.NewPVStorage(c.KubeCli, cl.Metadata.Name, cl.Metadata.Namespace, c.PVProvisioner, *b)
	case spec.BackupStorageTypeS3:
		if len(c.S3Context.AWSConfig) == 0 {
			return nil, errNoS3ConfigForBackup
		}
		s, err = backupstorage.NewS3Storage(c.S3Context, c.KubeCli, cl.Metadata.Name, cl.Metadata.Namespace, *b)
	}
	return s, err
}

func (bm *backupManager) setup() error {
	r := bm.cluster.Spec.Restore
	restoreSameNameCluster := r != nil && r.BackupClusterName == bm.cluster.Metadata.Name

	// There is only one case that we don't need to create underlying storage.
	// That is, the storage already exists and we are restoring cluster from it.
	if !restoreSameNameCluster {
		if err := bm.s.Create(); err != nil {
			return err
		}
	}

	if r != nil {
		bm.logger.Infof("restoring cluster from existing backup (%s)", r.BackupClusterName)
		if bm.cluster.Metadata.Name != r.BackupClusterName {
			if err := bm.s.Clone(r.BackupClusterName); err != nil {
				return err
			}
		}
	}

	return bm.runSidecar()
}

func (bm *backupManager) runSidecar() error {
	if err := bm.createSidecarDeployment(); err != nil {
		return fmt.Errorf("failed to create backup sidecar Deployment: %v", err)
	}
	if err := bm.createBackupService(); err != nil {
		return fmt.Errorf("failed to create backup sidecar service: %v", err)
	}
	bm.logger.Info("backup sidecar deployment and service created")
	return nil
}

func (bm *backupManager) createSidecarDeployment() error {
	cl, c := bm.cluster, bm.config

	podTemplate := k8sutil.NewBackupPodTemplate(cl.Metadata.Name, bm.config.ServiceAccount, cl.Spec)
	switch cl.Spec.Backup.StorageType {
	case spec.BackupStorageTypeDefault, spec.BackupStorageTypePersistentVolume:
		k8sutil.PodSpecWithPV(&podTemplate.Spec, cl.Metadata.Name)
	case spec.BackupStorageTypeS3:
		k8sutil.PodSpecWithS3(&podTemplate.Spec, c.S3Context)
	}

	name := k8sutil.BackupSidecarName(cl.Metadata.Name)
	dplSel := k8sutil.LabelsForCluster(cl.Metadata.Name)

	d := k8sutil.NewBackupDeploymentManifest(name, dplSel, podTemplate, bm.cluster.AsOwner())
	_, err := c.KubeCli.AppsV1beta1().Deployments(cl.Metadata.Namespace).Create(d)
	return err
}

func (bm *backupManager) createBackupService() error {
	svc := k8sutil.NewBackupServiceManifest(bm.cluster.Metadata.Name, bm.cluster.AsOwner())
	_, err := bm.config.KubeCli.CoreV1().Services(bm.cluster.Metadata.Namespace).Create(svc)
	if err != nil {
		if !k8sutil.IsKubernetesResourceAlreadyExistError(err) {
			return err
		}
	}
	return nil
}

func (bm *backupManager) cleanup() error {
	// Only need to delete storage, Kubernetes related resources will be deleted by the GC.
	err := bm.s.Delete()
	if err != nil {
		return fmt.Errorf("fail to delete backup storage: %v", err)
	}
	return nil
}

func (bm *backupManager) requestBackup() error {
	ctx, cancel := context.WithTimeout(context.Background(), defaultBackupHTTPTimeout+defaultBackupCreatingTimeout)
	defer cancel()
	return bm.bc.Request(ctx)
}

func (bm *backupManager) checkBackupExist(ver string) (bool, error) {
	ctx, cancel := context.WithTimeout(context.Background(), defaultBackupHTTPTimeout)
	defer cancel()
	return bm.bc.Exist(ctx, ver)
}

func (bm *backupManager) getStatus() (*backupapi.ServiceStatus, error) {
	ctx, cancel := context.WithTimeout(context.Background(), defaultBackupHTTPTimeout)
	defer cancel()
	return bm.bc.ServiceStatus(ctx)
}

func backupServiceStatusToTPRBackupServiceStatu(s *backupapi.ServiceStatus) *spec.BackupServiceStatus {
	b, err := json.Marshal(s)
	if err != nil {
		panic("unexpected json error")
	}

	var bs spec.BackupServiceStatus
	err = json.Unmarshal(b, &bs)
	if err != nil {
		panic("unexpected json error")
	}
	return &bs
}

func (bm *backupManager) upgradeIfNeeded() error {
	cl, c := bm.cluster, bm.config
	sidecarName := k8sutil.BackupSidecarName(cl.Metadata.Name)

	d, err := c.KubeCli.AppsV1beta1().Deployments(cl.Metadata.Namespace).Get(sidecarName, metav1.GetOptions{})
	if err != nil {
		return err
	}
	if d.Spec.Template.Spec.Containers[0].Image == k8sutil.BackupImage {
		return nil
	}
	bm.logger.Infof("upgrading backup sidecar from (%v) to (%v)",
		d.Spec.Template.Spec.Containers[0].Image, k8sutil.BackupImage)
	cd := k8sutil.CloneDeployment(d)

	// TODO: backward compatibility for v0.2.6 . Remove this after v0.2.7 .
	cd.Spec.Strategy = appsv1beta1.DeploymentStrategy{
		Type: appsv1beta1.RecreateDeploymentStrategyType,
	}

	cd.Spec.Template.Spec.Containers[0].Image = k8sutil.BackupImage
	patchData, err := k8sutil.CreatePatch(d, cd, appsv1beta1.Deployment{})
	_, err = c.KubeCli.AppsV1beta1().Deployments(cl.Metadata.Namespace).Patch(d.Name, types.StrategicMergePatchType, patchData)
	return err
}
<|endoftext|>


<commit_before>package netpol

import (
	"crypto/sha256"
	"encoding/base32"
	"reflect"
	"strings"

	api "k8s.io/api/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/klog/v2"
)

func (npc *NetworkPolicyController) newPodEventHandler() cache.ResourceEventHandler {
	return cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			npc.OnPodUpdate(obj)

		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			newPodObj := newObj.(*api.Pod)
			oldPodObj := oldObj.(*api.Pod)
			if newPodObj.Status.Phase != oldPodObj.Status.Phase ||
				newPodObj.Status.PodIP != oldPodObj.Status.PodIP ||
				!reflect.DeepEqual(newPodObj.Labels, oldPodObj.Labels) {
				// for the network policies, we are only interested in pod status phase change or IP change
				npc.OnPodUpdate(newObj)
			}
		},
		DeleteFunc: func(obj interface{}) {
			npc.handlePodDelete(obj)
		},
	}
}

// OnPodUpdate handles updates to pods from the Kubernetes api server
func (npc *NetworkPolicyController) OnPodUpdate(obj interface{}) {
	pod := obj.(*api.Pod)
	if pod.Spec.HostNetwork {
		klog.V(2).Infof("Ignoring update to hostNetwork pod: %s/%s", pod.Namespace, pod.Name)
		return
	}
	klog.V(2).Infof("Received update to pod: %s/%s", pod.Namespace, pod.Name)

	npc.RequestFullSync()
}

func (npc *NetworkPolicyController) handlePodDelete(obj interface{}) {
	pod, ok := obj.(*api.Pod)
	if !ok {
		tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
		if !ok {
			klog.Errorf("unexpected object type: %v", obj)
			return
		}
		if pod, ok = tombstone.Obj.(*api.Pod); !ok {
			klog.Errorf("unexpected object type: %v", obj)
			return
		}
	}
	klog.V(2).Infof("Received pod: %s/%s delete event", pod.Namespace, pod.Name)

	npc.RequestFullSync()
}

func (npc *NetworkPolicyController) syncPodFirewallChains(networkPoliciesInfo []networkPolicyInfo, version string) (map[string]bool, error) {

	activePodFwChains := make(map[string]bool)

	dropUnmarkedTrafficRules := func(podName, podNamespace, podFwChainName string) error {
		// add rule to log the packets that will be dropped due to network policy enforcement
		comment := "\"rule to log dropped traffic POD name:" + podName + " namespace: " + podNamespace + "\""
		args := []string{"-A", podFwChainName, "-m", "comment", "--comment", comment, "-m", "mark", "!", "--mark", "0x10000/0x10000", "-j", "NFLOG", "--nflog-group", "100", "-m", "limit", "--limit", "10/minute", "--limit-burst", "10", "\n"}
		// This used to be AppendUnique when we were using iptables directly, this checks to make sure we didn't drop unmarked for this chain already
		if strings.Contains(npc.filterTableRules.String(), strings.Join(args, " ")) {
			return nil
		}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// add rule to DROP if no applicable network policy permits the traffic
		comment = "\"rule to REJECT traffic destined for POD name:" + podName + " namespace: " + podNamespace + "\""
		args = []string{"-A", podFwChainName, "-m", "comment", "--comment", comment, "-m", "mark", "!", "--mark", "0x10000/0x10000", "-j", "REJECT", "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// reset mark to let traffic pass through rest of the chains
		args = []string{"-A", podFwChainName, "-j", "MARK", "--set-mark", "0/0x10000", "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		return nil
	}

	// loop through the pods running on the node which to which ingress network policies to be applied
	ingressNetworkPolicyEnabledPods, err := npc.getIngressNetworkPolicyEnabledPods(networkPoliciesInfo, npc.nodeIP.String())
	if err != nil {
		return nil, err
	}
	for _, pod := range *ingressNetworkPolicyEnabledPods {

		// below condition occurs when we get trasient update while removing or adding pod
		// subseqent update will do the correct action
		if len(pod.ip) == 0 || pod.ip == "" {
			continue
		}

		// ensure pod specific firewall chain exist for all the pods that need ingress firewall
		podFwChainName := podFirewallChainName(pod.namespace, pod.name, version)
		npc.filterTableRules.WriteString(":" + podFwChainName + "\n")

		activePodFwChains[podFwChainName] = true

		// add entries in pod firewall to run through required network policies
		for _, policy := range networkPoliciesInfo {
			if _, ok := policy.targetPods[pod.ip]; ok {
				comment := "\"run through nw policy " + policy.name + "\""
				policyChainName := networkPolicyChainName(policy.namespace, policy.name, version)
				args := []string{"-I", podFwChainName, "1", "-m", "comment", "--comment", comment, "-j", policyChainName, "\n"}
				npc.filterTableRules.WriteString(strings.Join(args, " "))
			}
		}

		comment := "\"rule to permit the traffic traffic to pods when source is the pod's local node\""
		args := []string{"-I", podFwChainName, "1", "-m", "comment", "--comment", comment, "-m", "addrtype", "--src-type", "LOCAL", "-d", pod.ip, "-j", "ACCEPT", "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// ensure statefull firewall, that permits return traffic for the traffic originated by the pod
		comment = "\"rule for stateful firewall for pod\""
		args = []string{"-I", podFwChainName, "1", "-m", "comment", "--comment", comment, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT", "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// ensure there is rule in filter table and FORWARD chain to jump to pod specific firewall chain
		// this rule applies to the traffic getting routed (coming for other node pods)
		comment = "\"rule to jump traffic destined to POD name:" + pod.name + " namespace: " + pod.namespace +
			" to chain " + podFwChainName + "\""
		args = []string{"-I", kubeForwardChainName, "1", "-m", "comment", "--comment", comment, "-d", pod.ip, "-j", podFwChainName + "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// ensure there is rule in filter table and OUTPUT chain to jump to pod specific firewall chain
		// this rule applies to the traffic from a pod getting routed back to another pod on same node by service proxy
		args = []string{"-I", kubeOutputChainName, "1", "-m", "comment", "--comment", comment, "-d", pod.ip, "-j", podFwChainName + "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// ensure there is rule in filter table and forward chain to jump to pod specific firewall chain
		// this rule applies to the traffic getting switched (coming for same node pods)
		comment = "\"rule to jump traffic destined to POD name:" + pod.name + " namespace: " + pod.namespace +
			" to chain " + podFwChainName + "\""
		args = []string{"-I", kubeForwardChainName, "1", "-m", "physdev", "--physdev-is-bridged",
			"-m", "comment", "--comment", comment,
			"-d", pod.ip,
			"-j", podFwChainName, "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		err = dropUnmarkedTrafficRules(pod.name, pod.namespace, podFwChainName)
		if err != nil {
			return nil, err
		}
	}

	// loop through the pods running on the node which egress network policies to be applied
	egressNetworkPolicyEnabledPods, err := npc.getEgressNetworkPolicyEnabledPods(networkPoliciesInfo, npc.nodeIP.String())
	if err != nil {
		return nil, err
	}
	for _, pod := range *egressNetworkPolicyEnabledPods {

		// below condition occurs when we get trasient update while removing or adding pod
		// subseqent update will do the correct action
		if len(pod.ip) == 0 || pod.ip == "" {
			continue
		}

		// ensure pod specific firewall chain exist for all the pods that need egress firewall
		podFwChainName := podFirewallChainName(pod.namespace, pod.name, version)
		if !activePodFwChains[podFwChainName] {
			npc.filterTableRules.WriteString(":" + podFwChainName + "\n")
		}
		activePodFwChains[podFwChainName] = true

		// add entries in pod firewall to run through required network policies
		for _, policy := range networkPoliciesInfo {
			if _, ok := policy.targetPods[pod.ip]; ok {
				comment := "\"run through nw policy " + policy.name + "\""
				policyChainName := networkPolicyChainName(policy.namespace, policy.name, version)
				args := []string{"-I", podFwChainName, "1", "-m", "comment", "--comment", comment, "-j", policyChainName, "\n"}
				npc.filterTableRules.WriteString(strings.Join(args, " "))
			}
		}

		// ensure statefull firewall, that permits return traffic for the traffic originated by the pod
		comment := "\"rule for stateful firewall for pod\""
		args := []string{"-I", podFwChainName, "1", "-m", "comment", "--comment", comment, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT", "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		egressFilterChains := []string{kubeInputChainName, kubeForwardChainName, kubeOutputChainName}
		for _, chain := range egressFilterChains {
			// ensure there is rule in filter table and FORWARD chain to jump to pod specific firewall chain
			// this rule applies to the traffic getting forwarded/routed (traffic from the pod destinted
			// to pod on a different node)
			comment = "\"rule to jump traffic from POD name:" + pod.name + " namespace: " + pod.namespace +
				" to chain " + podFwChainName + "\""
			args = []string{"-A", chain, "-m", "comment", "--comment", comment, "-s", pod.ip, "-j", podFwChainName, "\n"}
			npc.filterTableRules.WriteString(strings.Join(args, " "))
		}

		// ensure there is rule in filter table and forward chain to jump to pod specific firewall chain
		// this rule applies to the traffic getting switched (coming for same node pods)
		comment = "\"rule to jump traffic from POD name:" + pod.name + " namespace: " + pod.namespace +
			" to chain " + podFwChainName + "\""
		args = []string{"-I", kubeForwardChainName, "1", "-m", "physdev", "--physdev-is-bridged",
			"-m", "comment", "--comment", comment,
			"-s", pod.ip,
			"-j", podFwChainName, "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		err = dropUnmarkedTrafficRules(pod.name, pod.namespace, podFwChainName)
		if err != nil {
			return nil, err
		}
	}

	return activePodFwChains, nil
}

func (npc *NetworkPolicyController) getIngressNetworkPolicyEnabledPods(networkPoliciesInfo []networkPolicyInfo, nodeIP string) (*map[string]podInfo, error) {
	nodePods := make(map[string]podInfo)

	for _, obj := range npc.podLister.List() {
		pod := obj.(*api.Pod)

		// ignore the pods running on the different node and pods that are not actionable
		if strings.Compare(pod.Status.HostIP, nodeIP) != 0 || !isNetPolActionable(pod) {
			continue
		}

		for _, policy := range networkPoliciesInfo {
			if policy.namespace != pod.ObjectMeta.Namespace {
				continue
			}
			_, ok := policy.targetPods[pod.Status.PodIP]
			if ok && (policy.policyType == "both" || policy.policyType == "ingress") {
				klog.V(2).Infof("Found pod name: " + pod.ObjectMeta.Name + " namespace: " + pod.ObjectMeta.Namespace + " for which network policies need to be applied.")
				nodePods[pod.Status.PodIP] = podInfo{ip: pod.Status.PodIP,
					name:      pod.ObjectMeta.Name,
					namespace: pod.ObjectMeta.Namespace,
					labels:    pod.ObjectMeta.Labels}
				break
			}
		}
	}

	return &nodePods, nil
}

func (npc *NetworkPolicyController) getEgressNetworkPolicyEnabledPods(networkPoliciesInfo []networkPolicyInfo, nodeIP string) (*map[string]podInfo, error) {

	nodePods := make(map[string]podInfo)

	for _, obj := range npc.podLister.List() {
		pod := obj.(*api.Pod)

		// ignore the pods running on the different node and pods that are not actionable
		if strings.Compare(pod.Status.HostIP, nodeIP) != 0 || !isNetPolActionable(pod) {
			continue
		}

		for _, policy := range networkPoliciesInfo {
			if policy.namespace != pod.ObjectMeta.Namespace {
				continue
			}
			_, ok := policy.targetPods[pod.Status.PodIP]
			if ok && (policy.policyType == "both" || policy.policyType == "egress") {
				klog.V(2).Infof("Found pod name: " + pod.ObjectMeta.Name + " namespace: " + pod.ObjectMeta.Namespace + " for which network policies need to be applied.")
				nodePods[pod.Status.PodIP] = podInfo{ip: pod.Status.PodIP,
					name:      pod.ObjectMeta.Name,
					namespace: pod.ObjectMeta.Namespace,
					labels:    pod.ObjectMeta.Labels}
				break
			}
		}
	}

	return &nodePods, nil
}

func podFirewallChainName(namespace, podName string, version string) string {
	hash := sha256.Sum256([]byte(namespace + podName + version))
	encoded := base32.StdEncoding.EncodeToString(hash[:])
	return kubePodFirewallChainPrefix + encoded[:16]
}
<commit_msg>fix(NPC): remove HostNetwork check from OnPodUpdate<commit_after>package netpol

import (
	"crypto/sha256"
	"encoding/base32"
	"reflect"
	"strings"

	api "k8s.io/api/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/klog/v2"
)

func (npc *NetworkPolicyController) newPodEventHandler() cache.ResourceEventHandler {
	return cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			npc.OnPodUpdate(obj)

		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			newPodObj := newObj.(*api.Pod)
			oldPodObj := oldObj.(*api.Pod)
			if newPodObj.Status.Phase != oldPodObj.Status.Phase ||
				newPodObj.Status.PodIP != oldPodObj.Status.PodIP ||
				!reflect.DeepEqual(newPodObj.Labels, oldPodObj.Labels) {
				// for the network policies, we are only interested in pod status phase change or IP change
				npc.OnPodUpdate(newObj)
			}
		},
		DeleteFunc: func(obj interface{}) {
			npc.handlePodDelete(obj)
		},
	}
}

// OnPodUpdate handles updates to pods from the Kubernetes api server
func (npc *NetworkPolicyController) OnPodUpdate(obj interface{}) {
	pod := obj.(*api.Pod)
	klog.V(2).Infof("Received update to pod: %s/%s", pod.Namespace, pod.Name)

	npc.RequestFullSync()
}

func (npc *NetworkPolicyController) handlePodDelete(obj interface{}) {
	pod, ok := obj.(*api.Pod)
	if !ok {
		tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
		if !ok {
			klog.Errorf("unexpected object type: %v", obj)
			return
		}
		if pod, ok = tombstone.Obj.(*api.Pod); !ok {
			klog.Errorf("unexpected object type: %v", obj)
			return
		}
	}
	klog.V(2).Infof("Received pod: %s/%s delete event", pod.Namespace, pod.Name)

	npc.RequestFullSync()
}

func (npc *NetworkPolicyController) syncPodFirewallChains(networkPoliciesInfo []networkPolicyInfo, version string) (map[string]bool, error) {

	activePodFwChains := make(map[string]bool)

	dropUnmarkedTrafficRules := func(podName, podNamespace, podFwChainName string) error {
		// add rule to log the packets that will be dropped due to network policy enforcement
		comment := "\"rule to log dropped traffic POD name:" + podName + " namespace: " + podNamespace + "\""
		args := []string{"-A", podFwChainName, "-m", "comment", "--comment", comment, "-m", "mark", "!", "--mark", "0x10000/0x10000", "-j", "NFLOG", "--nflog-group", "100", "-m", "limit", "--limit", "10/minute", "--limit-burst", "10", "\n"}
		// This used to be AppendUnique when we were using iptables directly, this checks to make sure we didn't drop unmarked for this chain already
		if strings.Contains(npc.filterTableRules.String(), strings.Join(args, " ")) {
			return nil
		}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// add rule to DROP if no applicable network policy permits the traffic
		comment = "\"rule to REJECT traffic destined for POD name:" + podName + " namespace: " + podNamespace + "\""
		args = []string{"-A", podFwChainName, "-m", "comment", "--comment", comment, "-m", "mark", "!", "--mark", "0x10000/0x10000", "-j", "REJECT", "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// reset mark to let traffic pass through rest of the chains
		args = []string{"-A", podFwChainName, "-j", "MARK", "--set-mark", "0/0x10000", "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		return nil
	}

	// loop through the pods running on the node which to which ingress network policies to be applied
	ingressNetworkPolicyEnabledPods, err := npc.getIngressNetworkPolicyEnabledPods(networkPoliciesInfo, npc.nodeIP.String())
	if err != nil {
		return nil, err
	}
	for _, pod := range *ingressNetworkPolicyEnabledPods {

		// below condition occurs when we get trasient update while removing or adding pod
		// subseqent update will do the correct action
		if len(pod.ip) == 0 || pod.ip == "" {
			continue
		}

		// ensure pod specific firewall chain exist for all the pods that need ingress firewall
		podFwChainName := podFirewallChainName(pod.namespace, pod.name, version)
		npc.filterTableRules.WriteString(":" + podFwChainName + "\n")

		activePodFwChains[podFwChainName] = true

		// add entries in pod firewall to run through required network policies
		for _, policy := range networkPoliciesInfo {
			if _, ok := policy.targetPods[pod.ip]; ok {
				comment := "\"run through nw policy " + policy.name + "\""
				policyChainName := networkPolicyChainName(policy.namespace, policy.name, version)
				args := []string{"-I", podFwChainName, "1", "-m", "comment", "--comment", comment, "-j", policyChainName, "\n"}
				npc.filterTableRules.WriteString(strings.Join(args, " "))
			}
		}

		comment := "\"rule to permit the traffic traffic to pods when source is the pod's local node\""
		args := []string{"-I", podFwChainName, "1", "-m", "comment", "--comment", comment, "-m", "addrtype", "--src-type", "LOCAL", "-d", pod.ip, "-j", "ACCEPT", "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// ensure statefull firewall, that permits return traffic for the traffic originated by the pod
		comment = "\"rule for stateful firewall for pod\""
		args = []string{"-I", podFwChainName, "1", "-m", "comment", "--comment", comment, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT", "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// ensure there is rule in filter table and FORWARD chain to jump to pod specific firewall chain
		// this rule applies to the traffic getting routed (coming for other node pods)
		comment = "\"rule to jump traffic destined to POD name:" + pod.name + " namespace: " + pod.namespace +
			" to chain " + podFwChainName + "\""
		args = []string{"-I", kubeForwardChainName, "1", "-m", "comment", "--comment", comment, "-d", pod.ip, "-j", podFwChainName + "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// ensure there is rule in filter table and OUTPUT chain to jump to pod specific firewall chain
		// this rule applies to the traffic from a pod getting routed back to another pod on same node by service proxy
		args = []string{"-I", kubeOutputChainName, "1", "-m", "comment", "--comment", comment, "-d", pod.ip, "-j", podFwChainName + "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		// ensure there is rule in filter table and forward chain to jump to pod specific firewall chain
		// this rule applies to the traffic getting switched (coming for same node pods)
		comment = "\"rule to jump traffic destined to POD name:" + pod.name + " namespace: " + pod.namespace +
			" to chain " + podFwChainName + "\""
		args = []string{"-I", kubeForwardChainName, "1", "-m", "physdev", "--physdev-is-bridged",
			"-m", "comment", "--comment", comment,
			"-d", pod.ip,
			"-j", podFwChainName, "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		err = dropUnmarkedTrafficRules(pod.name, pod.namespace, podFwChainName)
		if err != nil {
			return nil, err
		}
	}

	// loop through the pods running on the node which egress network policies to be applied
	egressNetworkPolicyEnabledPods, err := npc.getEgressNetworkPolicyEnabledPods(networkPoliciesInfo, npc.nodeIP.String())
	if err != nil {
		return nil, err
	}
	for _, pod := range *egressNetworkPolicyEnabledPods {

		// below condition occurs when we get trasient update while removing or adding pod
		// subseqent update will do the correct action
		if len(pod.ip) == 0 || pod.ip == "" {
			continue
		}

		// ensure pod specific firewall chain exist for all the pods that need egress firewall
		podFwChainName := podFirewallChainName(pod.namespace, pod.name, version)
		if !activePodFwChains[podFwChainName] {
			npc.filterTableRules.WriteString(":" + podFwChainName + "\n")
		}
		activePodFwChains[podFwChainName] = true

		// add entries in pod firewall to run through required network policies
		for _, policy := range networkPoliciesInfo {
			if _, ok := policy.targetPods[pod.ip]; ok {
				comment := "\"run through nw policy " + policy.name + "\""
				policyChainName := networkPolicyChainName(policy.namespace, policy.name, version)
				args := []string{"-I", podFwChainName, "1", "-m", "comment", "--comment", comment, "-j", policyChainName, "\n"}
				npc.filterTableRules.WriteString(strings.Join(args, " "))
			}
		}

		// ensure statefull firewall, that permits return traffic for the traffic originated by the pod
		comment := "\"rule for stateful firewall for pod\""
		args := []string{"-I", podFwChainName, "1", "-m", "comment", "--comment", comment, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT", "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		egressFilterChains := []string{kubeInputChainName, kubeForwardChainName, kubeOutputChainName}
		for _, chain := range egressFilterChains {
			// ensure there is rule in filter table and FORWARD chain to jump to pod specific firewall chain
			// this rule applies to the traffic getting forwarded/routed (traffic from the pod destinted
			// to pod on a different node)
			comment = "\"rule to jump traffic from POD name:" + pod.name + " namespace: " + pod.namespace +
				" to chain " + podFwChainName + "\""
			args = []string{"-A", chain, "-m", "comment", "--comment", comment, "-s", pod.ip, "-j", podFwChainName, "\n"}
			npc.filterTableRules.WriteString(strings.Join(args, " "))
		}

		// ensure there is rule in filter table and forward chain to jump to pod specific firewall chain
		// this rule applies to the traffic getting switched (coming for same node pods)
		comment = "\"rule to jump traffic from POD name:" + pod.name + " namespace: " + pod.namespace +
			" to chain " + podFwChainName + "\""
		args = []string{"-I", kubeForwardChainName, "1", "-m", "physdev", "--physdev-is-bridged",
			"-m", "comment", "--comment", comment,
			"-s", pod.ip,
			"-j", podFwChainName, "\n"}
		npc.filterTableRules.WriteString(strings.Join(args, " "))

		err = dropUnmarkedTrafficRules(pod.name, pod.namespace, podFwChainName)
		if err != nil {
			return nil, err
		}
	}

	return activePodFwChains, nil
}

func (npc *NetworkPolicyController) getIngressNetworkPolicyEnabledPods(networkPoliciesInfo []networkPolicyInfo, nodeIP string) (*map[string]podInfo, error) {
	nodePods := make(map[string]podInfo)

	for _, obj := range npc.podLister.List() {
		pod := obj.(*api.Pod)

		// ignore the pods running on the different node and pods that are not actionable
		if strings.Compare(pod.Status.HostIP, nodeIP) != 0 || !isNetPolActionable(pod) {
			continue
		}

		for _, policy := range networkPoliciesInfo {
			if policy.namespace != pod.ObjectMeta.Namespace {
				continue
			}
			_, ok := policy.targetPods[pod.Status.PodIP]
			if ok && (policy.policyType == "both" || policy.policyType == "ingress") {
				klog.V(2).Infof("Found pod name: " + pod.ObjectMeta.Name + " namespace: " + pod.ObjectMeta.Namespace + " for which network policies need to be applied.")
				nodePods[pod.Status.PodIP] = podInfo{ip: pod.Status.PodIP,
					name:      pod.ObjectMeta.Name,
					namespace: pod.ObjectMeta.Namespace,
					labels:    pod.ObjectMeta.Labels}
				break
			}
		}
	}

	return &nodePods, nil
}

func (npc *NetworkPolicyController) getEgressNetworkPolicyEnabledPods(networkPoliciesInfo []networkPolicyInfo, nodeIP string) (*map[string]podInfo, error) {

	nodePods := make(map[string]podInfo)

	for _, obj := range npc.podLister.List() {
		pod := obj.(*api.Pod)

		// ignore the pods running on the different node and pods that are not actionable
		if strings.Compare(pod.Status.HostIP, nodeIP) != 0 || !isNetPolActionable(pod) {
			continue
		}

		for _, policy := range networkPoliciesInfo {
			if policy.namespace != pod.ObjectMeta.Namespace {
				continue
			}
			_, ok := policy.targetPods[pod.Status.PodIP]
			if ok && (policy.policyType == "both" || policy.policyType == "egress") {
				klog.V(2).Infof("Found pod name: " + pod.ObjectMeta.Name + " namespace: " + pod.ObjectMeta.Namespace + " for which network policies need to be applied.")
				nodePods[pod.Status.PodIP] = podInfo{ip: pod.Status.PodIP,
					name:      pod.ObjectMeta.Name,
					namespace: pod.ObjectMeta.Namespace,
					labels:    pod.ObjectMeta.Labels}
				break
			}
		}
	}

	return &nodePods, nil
}

func podFirewallChainName(namespace, podName string, version string) string {
	hash := sha256.Sum256([]byte(namespace + podName + version))
	encoded := base32.StdEncoding.EncodeToString(hash[:])
	return kubePodFirewallChainPrefix + encoded[:16]
}
<|endoftext|>


<commit_before>package exporter

import (
	"fmt"
	"strings"

	proto "github.com/anz-bank/sysl/pkg/sysl"
	"github.com/go-openapi/spec"
	"github.com/sirupsen/logrus"
)

type protoType struct {
	Format string
	Type   string
}

type TypeExporter struct {
	primitiveTypesMap map[proto.Type_Primitive]protoType
	log               *logrus.Logger
}

func makeTypeExporter(logger *logrus.Logger) *TypeExporter {
	return &TypeExporter{
		primitiveTypesMap: map[proto.Type_Primitive]protoType{
			proto.Type_NO_Primitive: {Format: "", Type: "object"},
			proto.Type_BOOL:         {Format: "", Type: "boolean"},
			proto.Type_INT:          {Format: "integer", Type: "number"},
			proto.Type_FLOAT:        {Format: "double", Type: "number"},
			proto.Type_DECIMAL:      {Format: "double", Type: "number"},
			proto.Type_STRING:       {Format: "string", Type: "string"},
			proto.Type_BYTES:        {Format: "string", Type: "string"},
			proto.Type_STRING_8:     {Format: "string", Type: "string"},
			proto.Type_DATE:         {Format: "string", Type: "string"},
			proto.Type_DATETIME:     {Format: "string", Type: "string"},
			proto.Type_XML:          {Format: "string", Type: "string"},
		},
		log: logger,
	}
}

func (t *TypeExporter) populateTypes(syslTypes map[string]*proto.Type, swaggerTypes spec.Definitions) error {
	for typeName, dataType := range syslTypes {
		typeSchema := spec.Schema{}
		if t.isComposite(dataType) {
			t.parseComposite(dataType, &typeSchema)
			swaggerTypes[typeName] = typeSchema
			continue
		}
		valueMap, err := t.findSwaggerType(dataType)
		if err != nil {
			t.log.Warnf("Type matching failed %s", err)
			return err
		}
		typeSchema.Format = valueMap.Format
		typeSchema.Type = append(typeSchema.Type, valueMap.Type)
		memberTypes := map[string]*proto.Type{}
		if typeSchema.Properties == nil {
			typeSchema.Properties = map[string]spec.Schema{}
		}
		if valueMap.Format == "tuple" {
			memberTypes = dataType.GetTuple().GetAttrDefs()
		} else if valueMap.Format == "relation" {
			memberTypes = dataType.GetRelation().GetAttrDefs()
		}
		for attK, attV := range memberTypes {
			elementSchema := spec.Schema{}
			if t.isComposite(attV) {
				t.parseComposite(attV, &elementSchema)
				swaggerTypes[attK] = elementSchema
			} else {
				attrValueMap, err := t.findSwaggerType(attV)
				if err != nil {
					t.log.Warnf("Type matching failed %s", err)
					return err
				}
				elementSchema.Format = attrValueMap.Format
				elementSchema.Type = []string{attrValueMap.Type}
			}
			typeSchema.Properties[attK] = elementSchema
		}
		swaggerTypes[typeName] = typeSchema
	}
	return nil
}

func (t *TypeExporter) findSwaggerType(syslType *proto.Type) (protoType, error) {
	switch s := syslType.Type.(type) {
	case *proto.Type_Primitive_:
		return protoType{
			Format: t.primitiveTypesMap[syslType.GetPrimitive()].Format,
			Type:   t.primitiveTypesMap[syslType.GetPrimitive()].Type,
		}, nil
	case *proto.Type_Enum_:
		return protoType{Format: "integer", Type: "number"}, nil
	case *proto.Type_Tuple_:
		return protoType{Format: "tuple", Type: "object"}, nil
	case *proto.Type_Relation_:
		return protoType{Format: "relation", Type: "object"}, nil
	case *proto.Type_TypeRef:
		if s.TypeRef.GetRef().GetAppname() == nil {
			return protoType{Format: s.TypeRef.GetRef().Path[0], Type: "object"}, nil
		}
		return protoType{Format: s.TypeRef.GetRef().GetAppname().Part[0], Type: "object"}, nil
	default:
		return protoType{}, fmt.Errorf("none of the Swagger Types match")
	}
}

func (t *TypeExporter) isComposite(containerType *proto.Type) bool {
	if _, isSet := containerType.Type.(*proto.Type_Set); isSet {
		return true
	} else if _, isSeq := containerType.Type.(*proto.Type_Sequence); isSeq {
		return true
	}
	return false
}

func (t *TypeExporter) isCompositeString(containerType string) bool {
	typeTokens := strings.Split(strings.ToLower(containerType), " ")
	switch typeTokens[0] {
	case "set", "sequence", "map":
		return true
	default:
		return false
	}
}

func (t *TypeExporter) parseComposite(containerType *proto.Type, schema *spec.Schema) {
	schema.Items = &spec.SchemaOrArray{}
	schema.Items.Schema = &spec.Schema{}
	if cType, isSet := containerType.Type.(*proto.Type_Set); isSet {
		schema.Type = []string{"array"}
		retMap, err := t.findSwaggerType(cType.Set)
		if err != nil {
			t.log.Warnf("Type matching failed %s", err)
			return
		}
		schema.Items.Schema.Format = retMap.Format
		schema.Items.Schema.Type = append(schema.Items.Schema.Type, retMap.Type)
	} else if cType, isSeq := containerType.Type.(*proto.Type_Sequence); isSeq {
		schema.Type = []string{"array"}
		retMap, err := t.findSwaggerType(cType.Sequence)
		if err != nil {
			t.log.Warnf("Type matching failed %s", err)
			return
		}
		schema.Items.Schema.Format = retMap.Format
		schema.Items.Schema.Type = append(schema.Items.Schema.Type, retMap.Type)
	}
}
<commit_msg>Add support for empty types in sysl export<commit_after>package exporter

import (
	"fmt"
	"strings"

	proto "github.com/anz-bank/sysl/pkg/sysl"
	"github.com/go-openapi/spec"
	"github.com/sirupsen/logrus"
)

type protoType struct {
	Format string
	Type   string
}

type TypeExporter struct {
	primitiveTypesMap map[proto.Type_Primitive]protoType
	log               *logrus.Logger
}

func makeTypeExporter(logger *logrus.Logger) *TypeExporter {
	return &TypeExporter{
		primitiveTypesMap: map[proto.Type_Primitive]protoType{
			proto.Type_NO_Primitive: {Format: "", Type: "object"},
			proto.Type_BOOL:         {Format: "", Type: "boolean"},
			proto.Type_INT:          {Format: "integer", Type: "number"},
			proto.Type_FLOAT:        {Format: "double", Type: "number"},
			proto.Type_DECIMAL:      {Format: "double", Type: "number"},
			proto.Type_STRING:       {Format: "string", Type: "string"},
			proto.Type_BYTES:        {Format: "string", Type: "string"},
			proto.Type_STRING_8:     {Format: "string", Type: "string"},
			proto.Type_DATE:         {Format: "string", Type: "string"},
			proto.Type_DATETIME:     {Format: "string", Type: "string"},
			proto.Type_XML:          {Format: "string", Type: "string"},
		},
		log: logger,
	}
}

func (t *TypeExporter) populateTypes(syslTypes map[string]*proto.Type, swaggerTypes spec.Definitions) error {
	for typeName, dataType := range syslTypes {
		typeSchema := spec.Schema{}
		if t.isComposite(dataType) {
			t.parseComposite(dataType, &typeSchema)
			swaggerTypes[typeName] = typeSchema
			continue
		}
		valueMap, err := t.findSwaggerType(dataType)
		if err != nil {
			t.log.Warnf("Type matching failed %s", err)
			return err
		}
		// Handles empty responses
		if valueMap.Format == "" && valueMap.Type == "" {
			continue
		}
		typeSchema.Format = valueMap.Format
		typeSchema.Type = append(typeSchema.Type, valueMap.Type)
		memberTypes := map[string]*proto.Type{}
		if typeSchema.Properties == nil {
			typeSchema.Properties = map[string]spec.Schema{}
		}
		if valueMap.Format == "tuple" {
			memberTypes = dataType.GetTuple().GetAttrDefs()
		} else if valueMap.Format == "relation" {
			memberTypes = dataType.GetRelation().GetAttrDefs()
		}
		for attK, attV := range memberTypes {
			elementSchema := spec.Schema{}
			if t.isComposite(attV) {
				t.parseComposite(attV, &elementSchema)
				swaggerTypes[attK] = elementSchema
			} else {
				attrValueMap, err := t.findSwaggerType(attV)
				if err != nil {
					t.log.Warnf("Type matching failed %s", err)
					return err
				}
				elementSchema.Format = attrValueMap.Format
				elementSchema.Type = []string{attrValueMap.Type}
			}
			typeSchema.Properties[attK] = elementSchema
		}
		swaggerTypes[typeName] = typeSchema
	}
	return nil
}

func (t *TypeExporter) findSwaggerType(syslType *proto.Type) (protoType, error) {
	if syslType.Type == nil {
		return protoType{}, nil
	}
	switch s := syslType.Type.(type) {
	case *proto.Type_Primitive_:
		return protoType{
			Format: t.primitiveTypesMap[syslType.GetPrimitive()].Format,
			Type:   t.primitiveTypesMap[syslType.GetPrimitive()].Type,
		}, nil
	case *proto.Type_Enum_:
		return protoType{Format: "integer", Type: "number"}, nil
	case *proto.Type_Tuple_:
		return protoType{Format: "tuple", Type: "object"}, nil
	case *proto.Type_Relation_:
		return protoType{Format: "relation", Type: "object"}, nil
	case *proto.Type_TypeRef:
		if s.TypeRef.GetRef().GetAppname() == nil {
			return protoType{Format: s.TypeRef.GetRef().Path[0], Type: "object"}, nil
		}
		return protoType{Format: s.TypeRef.GetRef().GetAppname().Part[0], Type: "object"}, nil
	default:
		return protoType{}, fmt.Errorf("none of the Swagger Types match for %s", syslType.String())
	}
}

func (t *TypeExporter) isComposite(containerType *proto.Type) bool {
	if _, isSet := containerType.Type.(*proto.Type_Set); isSet {
		return true
	} else if _, isSeq := containerType.Type.(*proto.Type_Sequence); isSeq {
		return true
	}
	return false
}

func (t *TypeExporter) isCompositeString(containerType string) bool {
	typeTokens := strings.Split(strings.ToLower(containerType), " ")
	switch typeTokens[0] {
	case "set", "sequence", "map":
		return true
	default:
		return false
	}
}

func (t *TypeExporter) parseComposite(containerType *proto.Type, schema *spec.Schema) {
	schema.Items = &spec.SchemaOrArray{}
	schema.Items.Schema = &spec.Schema{}
	if cType, isSet := containerType.Type.(*proto.Type_Set); isSet {
		schema.Type = []string{"array"}
		retMap, err := t.findSwaggerType(cType.Set)
		if err != nil {
			t.log.Warnf("Type matching failed %s", err)
			return
		}
		schema.Items.Schema.Format = retMap.Format
		schema.Items.Schema.Type = append(schema.Items.Schema.Type, retMap.Type)
	} else if cType, isSeq := containerType.Type.(*proto.Type_Sequence); isSeq {
		schema.Type = []string{"array"}
		retMap, err := t.findSwaggerType(cType.Sequence)
		if err != nil {
			t.log.Warnf("Type matching failed %s", err)
			return
		}
		schema.Items.Schema.Format = retMap.Format
		schema.Items.Schema.Type = append(schema.Items.Schema.Type, retMap.Type)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 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 compose

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"k8s.io/kubernetes/pkg/api"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libcompose/config"
	"github.com/docker/libcompose/lookup"
	"github.com/docker/libcompose/project"
	"github.com/kubernetes-incubator/kompose/pkg/kobject"
)

type Compose struct {
}

// load environment variables from compose file
func loadEnvVars(envars []string) []kobject.EnvVar {
	envs := []kobject.EnvVar{}
	for _, e := range envars {
		character := ""
		equalPos := strings.Index(e, "=")
		colonPos := strings.Index(e, ":")
		switch {
		case equalPos == -1 && colonPos == -1:
			character = ""
		case equalPos == -1 && colonPos != -1:
			character = ":"
		case equalPos != -1 && colonPos == -1:
			character = "="
		case equalPos != -1 && colonPos != -1:
			if equalPos > colonPos {
				character = ":"
			} else {
				character = "="
			}
		}

		if character == "" {
			envs = append(envs, kobject.EnvVar{
				Name: e,
			})
		} else {
			values := strings.SplitN(e, character, 2)
			envs = append(envs, kobject.EnvVar{
				Name:  values[0],
				Value: values[1],
			})
		}
	}

	return envs
}

// Load ports from compose file
func loadPorts(composePorts []string) ([]kobject.Ports, error) {
	ports := []kobject.Ports{}
	character := ":"
	for _, port := range composePorts {
		proto := api.ProtocolTCP
		// get protocol
		p := strings.Split(port, "/")
		if len(p) == 2 {
			if strings.EqualFold("tcp", p[1]) {
				proto = api.ProtocolTCP
			} else if strings.EqualFold("udp", p[1]) {
				proto = api.ProtocolUDP
			}
		}
		// port mappings without protocol part
		portNoProto := p[0]
		if strings.Contains(portNoProto, character) {
			hostPort := portNoProto[0:strings.Index(portNoProto, character)]
			hostPort = strings.TrimSpace(hostPort)
			hostPortInt, err := strconv.Atoi(hostPort)
			if err != nil {
				return nil, fmt.Errorf("invalid host port %q", port)
			}
			containerPort := portNoProto[strings.Index(portNoProto, character)+1:]
			containerPort = strings.TrimSpace(containerPort)
			containerPortInt, err := strconv.Atoi(containerPort)
			if err != nil {
				return nil, fmt.Errorf("invalid container port %q", port)
			}
			ports = append(ports, kobject.Ports{
				HostPort:      int32(hostPortInt),
				ContainerPort: int32(containerPortInt),
				Protocol:      proto,
			})
		} else {
			containerPortInt, err := strconv.Atoi(portNoProto)
			if err != nil {
				return nil, fmt.Errorf("invalid container port %q", port)
			}
			ports = append(ports, kobject.Ports{
				ContainerPort: int32(containerPortInt),
				Protocol:      proto,
			})
		}

	}
	return ports, nil
}

// load compose file into KomposeObject
func (c *Compose) LoadFile(file string) kobject.KomposeObject {
	komposeObject := kobject.KomposeObject{
		ServiceConfigs: make(map[string]kobject.ServiceConfig),
	}
	context := &project.Context{}
	if file == "" {
		file = "docker-compose.yml"
	}
	context.ComposeFiles = []string{file}

	if context.ResourceLookup == nil {
		context.ResourceLookup = &lookup.FileResourceLookup{}
	}

	if context.EnvironmentLookup == nil {
		cwd, err := os.Getwd()
		if err != nil {
			return kobject.KomposeObject{}
		}
		context.EnvironmentLookup = &lookup.ComposableEnvLookup{
			Lookups: []config.EnvironmentLookup{
				&lookup.EnvfileLookup{
					Path: filepath.Join(cwd, ".env"),
				},
				&lookup.OsEnvLookup{},
			},
		}
	}

	// load compose file into composeObject
	composeObject := project.NewProject(context, nil, nil)
	err := composeObject.Parse()
	if err != nil {
		logrus.Fatalf("Failed to load compose file: %v", err)
	}

	// transform composeObject into komposeObject
	composeServiceNames := composeObject.ServiceConfigs.Keys()

	// volume config and network config are not supported
	if len(composeObject.NetworkConfigs) > 0 {
		logrus.Warningf("Unsupported network configuration of compose v2 - ignoring")
	}
	if len(composeObject.VolumeConfigs) > 0 {
		logrus.Warningf("Unsupported volume configuration of compose v2 - ignoring")
	}

	networksWarningFound := false

	for _, name := range composeServiceNames {
		if composeServiceConfig, ok := composeObject.ServiceConfigs.Get(name); ok {
			//FIXME: networks always contains one default element, even it isn't declared in compose v2.
			if composeServiceConfig.Networks != nil && len(composeServiceConfig.Networks.Networks) > 0 &&
				composeServiceConfig.Networks.Networks[0].Name != "default" &&
				!networksWarningFound {
				logrus.Warningf("Unsupported key networks - ignoring")
				networksWarningFound = true
			}
			kobject.CheckUnsupportedKey(composeServiceConfig)
			serviceConfig := kobject.ServiceConfig{}
			serviceConfig.Image = composeServiceConfig.Image
			serviceConfig.ContainerName = composeServiceConfig.ContainerName
			serviceConfig.Command = composeServiceConfig.Entrypoint
			serviceConfig.Args = composeServiceConfig.Command

			envs := loadEnvVars(composeServiceConfig.Environment)
			serviceConfig.Environment = envs

			// load ports
			ports, err := loadPorts(composeServiceConfig.Ports)
			if err != nil {
				logrus.Fatalf("%q failed to load ports from compose file: %v", name, err)
			}
			serviceConfig.Port = ports

			serviceConfig.WorkingDir = composeServiceConfig.WorkingDir

			if composeServiceConfig.Volumes != nil {
				for _, volume := range composeServiceConfig.Volumes.Volumes {
					serviceConfig.Volumes = append(serviceConfig.Volumes, volume.String())
				}
			}

			// canonical "Custom Labels" handler
			// Labels used to influence conversion of kompose will be handled
			// from here for docker-compose. Each loader will have such handler.
			for key, value := range composeServiceConfig.Labels {
				switch key {
				case "kompose.service.type":
					serviceConfig.ServiceType = handleServiceType(value)
				}
			}

			// convert compose labels to annotations
			serviceConfig.Annotations = map[string]string(composeServiceConfig.Labels)

			serviceConfig.CPUSet = composeServiceConfig.CPUSet
			serviceConfig.CPUShares = int64(composeServiceConfig.CPUShares)
			serviceConfig.CPUQuota = int64(composeServiceConfig.CPUQuota)
			serviceConfig.CapAdd = composeServiceConfig.CapAdd
			serviceConfig.CapDrop = composeServiceConfig.CapDrop
			serviceConfig.Expose = composeServiceConfig.Expose
			serviceConfig.Privileged = composeServiceConfig.Privileged
			serviceConfig.Restart = composeServiceConfig.Restart
			serviceConfig.User = composeServiceConfig.User
			serviceConfig.VolumesFrom = composeServiceConfig.VolumesFrom

			komposeObject.ServiceConfigs[name] = serviceConfig
		}
	}

	return komposeObject
}

func handleServiceType(ServiceType string) string {
	switch strings.ToLower(ServiceType) {
	case "", "clusterip":
		return string(api.ServiceTypeClusterIP)
	case "nodeport":
		return string(api.ServiceTypeNodePort)
	case "loadbalancer":
		return string(api.ServiceTypeLoadBalancer)
	default:
		logrus.Fatalf("Unknown value '%s', supported values are 'NodePort, ClusterIP or LoadBalancer'", ServiceType)
		return ""
	}
}
<commit_msg>support parse key-only environment variable<commit_after>/*
Copyright 2016 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 compose

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"k8s.io/kubernetes/pkg/api"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libcompose/config"
	"github.com/docker/libcompose/lookup"
	"github.com/docker/libcompose/project"
	"github.com/kubernetes-incubator/kompose/pkg/kobject"
)

type Compose struct {
}

// load environment variables from compose file
func loadEnvVars(envars []string) []kobject.EnvVar {
	envs := []kobject.EnvVar{}
	for _, e := range envars {
		character := ""
		equalPos := strings.Index(e, "=")
		colonPos := strings.Index(e, ":")
		switch {
		case equalPos == -1 && colonPos == -1:
			character = ""
		case equalPos == -1 && colonPos != -1:
			character = ":"
		case equalPos != -1 && colonPos == -1:
			character = "="
		case equalPos != -1 && colonPos != -1:
			if equalPos > colonPos {
				character = ":"
			} else {
				character = "="
			}
		}

		if character == "" {
			envs = append(envs, kobject.EnvVar{
				Name:  e,
				Value: os.Getenv(e),
			})
		} else {
			values := strings.SplitN(e, character, 2)
			// try to get value from os env
			if values[1] == "" {
				values[1] = os.Getenv(values[0])
			}
			envs = append(envs, kobject.EnvVar{
				Name:  values[0],
				Value: values[1],
			})
		}
	}

	return envs
}

// Load ports from compose file
func loadPorts(composePorts []string) ([]kobject.Ports, error) {
	ports := []kobject.Ports{}
	character := ":"
	for _, port := range composePorts {
		proto := api.ProtocolTCP
		// get protocol
		p := strings.Split(port, "/")
		if len(p) == 2 {
			if strings.EqualFold("tcp", p[1]) {
				proto = api.ProtocolTCP
			} else if strings.EqualFold("udp", p[1]) {
				proto = api.ProtocolUDP
			}
		}
		// port mappings without protocol part
		portNoProto := p[0]
		if strings.Contains(portNoProto, character) {
			hostPort := portNoProto[0:strings.Index(portNoProto, character)]
			hostPort = strings.TrimSpace(hostPort)
			hostPortInt, err := strconv.Atoi(hostPort)
			if err != nil {
				return nil, fmt.Errorf("invalid host port %q", port)
			}
			containerPort := portNoProto[strings.Index(portNoProto, character)+1:]
			containerPort = strings.TrimSpace(containerPort)
			containerPortInt, err := strconv.Atoi(containerPort)
			if err != nil {
				return nil, fmt.Errorf("invalid container port %q", port)
			}
			ports = append(ports, kobject.Ports{
				HostPort:      int32(hostPortInt),
				ContainerPort: int32(containerPortInt),
				Protocol:      proto,
			})
		} else {
			containerPortInt, err := strconv.Atoi(portNoProto)
			if err != nil {
				return nil, fmt.Errorf("invalid container port %q", port)
			}
			ports = append(ports, kobject.Ports{
				ContainerPort: int32(containerPortInt),
				Protocol:      proto,
			})
		}

	}
	return ports, nil
}

// load compose file into KomposeObject
func (c *Compose) LoadFile(file string) kobject.KomposeObject {
	komposeObject := kobject.KomposeObject{
		ServiceConfigs: make(map[string]kobject.ServiceConfig),
	}
	context := &project.Context{}
	if file == "" {
		file = "docker-compose.yml"
	}
	context.ComposeFiles = []string{file}

	if context.ResourceLookup == nil {
		context.ResourceLookup = &lookup.FileResourceLookup{}
	}

	if context.EnvironmentLookup == nil {
		cwd, err := os.Getwd()
		if err != nil {
			return kobject.KomposeObject{}
		}
		context.EnvironmentLookup = &lookup.ComposableEnvLookup{
			Lookups: []config.EnvironmentLookup{
				&lookup.EnvfileLookup{
					Path: filepath.Join(cwd, ".env"),
				},
				&lookup.OsEnvLookup{},
			},
		}
	}

	// load compose file into composeObject
	composeObject := project.NewProject(context, nil, nil)
	err := composeObject.Parse()
	if err != nil {
		logrus.Fatalf("Failed to load compose file: %v", err)
	}

	// transform composeObject into komposeObject
	composeServiceNames := composeObject.ServiceConfigs.Keys()

	// volume config and network config are not supported
	if len(composeObject.NetworkConfigs) > 0 {
		logrus.Warningf("Unsupported network configuration of compose v2 - ignoring")
	}
	if len(composeObject.VolumeConfigs) > 0 {
		logrus.Warningf("Unsupported volume configuration of compose v2 - ignoring")
	}

	networksWarningFound := false

	for _, name := range composeServiceNames {
		if composeServiceConfig, ok := composeObject.ServiceConfigs.Get(name); ok {
			//FIXME: networks always contains one default element, even it isn't declared in compose v2.
			if composeServiceConfig.Networks != nil && len(composeServiceConfig.Networks.Networks) > 0 &&
				composeServiceConfig.Networks.Networks[0].Name != "default" &&
				!networksWarningFound {
				logrus.Warningf("Unsupported key networks - ignoring")
				networksWarningFound = true
			}
			kobject.CheckUnsupportedKey(composeServiceConfig)
			serviceConfig := kobject.ServiceConfig{}
			serviceConfig.Image = composeServiceConfig.Image
			serviceConfig.ContainerName = composeServiceConfig.ContainerName
			serviceConfig.Command = composeServiceConfig.Entrypoint
			serviceConfig.Args = composeServiceConfig.Command

			envs := loadEnvVars(composeServiceConfig.Environment)
			serviceConfig.Environment = envs

			// load ports
			ports, err := loadPorts(composeServiceConfig.Ports)
			if err != nil {
				logrus.Fatalf("%q failed to load ports from compose file: %v", name, err)
			}
			serviceConfig.Port = ports

			serviceConfig.WorkingDir = composeServiceConfig.WorkingDir

			if composeServiceConfig.Volumes != nil {
				for _, volume := range composeServiceConfig.Volumes.Volumes {
					serviceConfig.Volumes = append(serviceConfig.Volumes, volume.String())
				}
			}

			// canonical "Custom Labels" handler
			// Labels used to influence conversion of kompose will be handled
			// from here for docker-compose. Each loader will have such handler.
			for key, value := range composeServiceConfig.Labels {
				switch key {
				case "kompose.service.type":
					serviceConfig.ServiceType = handleServiceType(value)
				}
			}

			// convert compose labels to annotations
			serviceConfig.Annotations = map[string]string(composeServiceConfig.Labels)

			serviceConfig.CPUSet = composeServiceConfig.CPUSet
			serviceConfig.CPUShares = int64(composeServiceConfig.CPUShares)
			serviceConfig.CPUQuota = int64(composeServiceConfig.CPUQuota)
			serviceConfig.CapAdd = composeServiceConfig.CapAdd
			serviceConfig.CapDrop = composeServiceConfig.CapDrop
			serviceConfig.Expose = composeServiceConfig.Expose
			serviceConfig.Privileged = composeServiceConfig.Privileged
			serviceConfig.Restart = composeServiceConfig.Restart
			serviceConfig.User = composeServiceConfig.User
			serviceConfig.VolumesFrom = composeServiceConfig.VolumesFrom

			komposeObject.ServiceConfigs[name] = serviceConfig
		}
	}

	return komposeObject
}

func handleServiceType(ServiceType string) string {
	switch strings.ToLower(ServiceType) {
	case "", "clusterip":
		return string(api.ServiceTypeClusterIP)
	case "nodeport":
		return string(api.ServiceTypeNodePort)
	case "loadbalancer":
		return string(api.ServiceTypeLoadBalancer)
	default:
		logrus.Fatalf("Unknown value '%s', supported values are 'NodePort, ClusterIP or LoadBalancer'", ServiceType)
		return ""
	}
}
<|endoftext|>