<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"log/syslog"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"code.google.com/p/go.net/websocket"
	"github.com/fsouza/go-dockerclient"
	"github.com/go-martini/martini"
)

var debugMode bool

func debug(v ...interface{}) {
	if debugMode {
		log.Println(v...)
	}
}

func assert(err error, context string) {
	if err != nil {
		log.Fatal(context+": ", err)
	}
}

func getopt(name, dfault string) string {
	value := os.Getenv(name)
	if value == "" {
		value = dfault
	}
	return value
}

type Colorizer map[string]int

// returns up to 14 color escape codes (then repeats) for each unique key
func (c Colorizer) Get(key string) string {
	i, exists := c[key]
	if !exists {
		c[key] = len(c)
		i = c[key]
	}
	bright := "1;"
	if i%14 > 6 {
		bright = ""
	}
	return "\x1b[" + bright + "3" + strconv.Itoa(7-(i%7)) + "m"
}

func logDataToString(data interface{}) string {
	switch d := data.(type) {
	case string:
		return d
	default:
		jsonData, err := json.Marshal(d)
		if err != nil {
			debug("httpPostStreamer - Error encoding JSON for data: ", jsonData)
		}
		return string(jsonData)
	}
}

func httpPostStreamer(target Target, types []string, logstream chan *Log) {

	url := target.Type + "://" + target.Addr + target.Path
	debug("httpPostStreamer - URL:", url)

	typestr := "," + strings.Join(types, ",") + ","
	debug("httpPostStreamer - typestr:", typestr)

	hostname, err := os.Hostname()
	if err != nil {
		debug("httpPostStreamer - error getting hostname:", err)
		hostname = "<unknown>"
	}

	tr := &http.Transport{}
	client := &http.Client{Transport: tr}

	timeout := 60000 * time.Millisecond
	capacity := 128
	timer := time.NewTimer(timeout)
	buffer := make([]*Log, 0, capacity)
	counter := 0
	for {
		select {
		case logline := <-logstream:
			if typestr != ",," && !strings.Contains(typestr, logline.Type) {
				continue
			}
			logline.Hostname = hostname
			buffer = append(buffer, logline)
			counter++
			if counter >= cap(buffer) {
				debug("httpPostStreamer - buffer full")
				flushHttp(buffer, client, url)
				buffer = make([]*Log, 0, capacity)
				timer.Stop()
				select {
				case <-timer.C:
				default:
				}
				timer.Reset(timeout)
			}
		case <-timer.C:
			debug("httpPostStreamer - timer expired, buffer length:", len(buffer))
			flushHttp(buffer, client, url)
			counter = 0
			buffer = make([]*Log, 0, capacity)
			timer.Stop()
			select {
			case <-timer.C:
			default:
			}
			timer.Reset(timeout)
		}
	}
}

func flushHttp(buffer []*Log, client *http.Client, url string) {
	messages := make([]byte, 8*1024)
	for l := range buffer {
		logline := buffer[l]
		message, err := json.Marshal(logline)
		if err != nil {
			debug("httpPostStreamer - Error encoding JSON: ", err)
			continue
		}
		messages = append(messages, message...)
		messages = append(messages, "\n"...)
	}

	debug("messages:\n", string(messages))
	req, err := http.NewRequest("POST", url, bytes.NewReader(messages))
	if err != nil {
		debug("httpPostStreamer - Error on http.NewRequest: ", err, url)
	}

	resp, err := client.Do(req)
	if err != nil {
		debug("httpPostStreamer - Error on client.Do: ", err, url)
	}
	io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()
}

func syslogStreamer(target Target, types []string, logstream chan *Log) {
	typestr := "," + strings.Join(types, ",") + ","
	for logline := range logstream {
		if typestr != ",," && !strings.Contains(typestr, logline.Type) {
			continue
		}
		tag := logline.Name + target.AppendTag
		remote, err := syslog.Dial("udp", target.Addr, syslog.LOG_USER|syslog.LOG_INFO, tag)
		assert(err, "syslog")
		io.WriteString(remote, logDataToString(logline.Data))
	}
}

func udpStreamer(target Target, types []string, logstream chan *Log) {
	typestr := "," + strings.Join(types, ",") + ","
	addr, err := net.ResolveUDPAddr("udp", target.Addr)
	assert(err, "resolve udp failed")
	conn, err := net.DialUDP("udp", nil, addr)
	assert(err, "connect udp failed")
	encoder := json.NewEncoder(conn)
	defer conn.Close()
	for logline := range logstream {
		if typestr != ",," && !strings.Contains(typestr, logline.Type) {
			continue
		}
		encoder.Encode(logline)
	}
}

func websocketStreamer(w http.ResponseWriter, req *http.Request, logstream chan *Log, closer chan bool) {
	websocket.Handler(func(conn *websocket.Conn) {
		for logline := range logstream {
			if req.URL.Query().Get("type") != "" && logline.Type != req.URL.Query().Get("type") {
				continue
			}
			_, err := conn.Write(append(marshal(logline), '\n'))
			if err != nil {
				closer <- true
				return
			}
		}
	}).ServeHTTP(w, req)
}

func httpStreamer(w http.ResponseWriter, req *http.Request, logstream chan *Log, multi bool) {
	var colors Colorizer
	var usecolor, usejson bool
	nameWidth := 16
	if req.URL.Query().Get("colors") != "off" {
		colors = make(Colorizer)
		usecolor = true
	}
	if req.Header.Get("Accept") == "application/json" {
		w.Header().Add("Content-Type", "application/json")
		usejson = true
	} else {
		w.Header().Add("Content-Type", "text/plain")
	}
	for logline := range logstream {
		if req.URL.Query().Get("types") != "" && logline.Type != req.URL.Query().Get("types") {
			continue
		}
		if usejson {
			w.Write(append(marshal(logline), '\n'))
		} else {
			if multi {
				if len(logline.Name) > nameWidth {
					nameWidth = len(logline.Name)
				}
				if usecolor {
					w.Write([]byte(fmt.Sprintf(
						"%s%"+strconv.Itoa(nameWidth)+"s|%s\x1b[0m\n",
						colors.Get(logline.Name), logline.Name, logline.Data,
					)))
				} else {
					w.Write([]byte(fmt.Sprintf(
						"%"+strconv.Itoa(nameWidth)+"s|%s\n", logline.Name,
						logDataToString(logline.Data),
					)))
				}
			} else {
				w.Write(append([]byte(logDataToString(logline.Data)), '\n'))
			}
		}
		w.(http.Flusher).Flush()
	}
}

func main() {
	debugMode = getopt("DEBUG", "") != ""
	port := getopt("PORT", "8000")
	endpoint := getopt("DOCKER_HOST", "unix:///var/run/docker.sock")
	routespath := getopt("ROUTESPATH", "/var/lib/logspout")
	eventlog := getopt("EVENTLOG", "") != ""

	client, err := docker.NewClient(endpoint)
	assert(err, "docker")
	attacher := NewAttachManager(client)
	router := NewRouteManager(attacher)

	if eventlog {
		debug("event logging enabled")
	}

	if len(os.Args) > 1 {
		u, err := url.Parse(os.Args[1])
		assert(err, "url")
		log.Println("routing all to " + os.Args[1])
		router.Add(&Route{Target: Target{Type: u.Scheme, Addr: u.Host, Path: u.Path}})
	}

	if _, err := os.Stat(routespath); err == nil {
		log.Println("loading and persisting routes in " + routespath)
		assert(router.Load(RouteFileStore(routespath)), "persistor")
	}

	m := martini.Classic()

	m.Get("/logs(?:/(?P<predicate>[a-zA-Z]+):(?P<value>.+))?", func(w http.ResponseWriter, req *http.Request, params martini.Params) {
		source := new(Source)
		switch {
		case params["predicate"] == "id" && params["value"] != "":
			source.ID = params["value"][:12]
		case params["predicate"] == "name" && params["value"] != "":
			source.Name = params["value"]
		case params["predicate"] == "filter" && params["value"] != "":
			source.Filter = params["value"]
		}

		if source.ID != "" && attacher.Get(source.ID) == nil {
			http.NotFound(w, req)
			return
		}

		logstream := make(chan *Log)
		defer close(logstream)

		var closer <-chan bool
		if req.Header.Get("Upgrade") == "websocket" {
			closerBi := make(chan bool)
			go websocketStreamer(w, req, logstream, closerBi)
			closer = closerBi
		} else {
			go httpStreamer(w, req, logstream, source.All() || source.Filter != "")
			closer = w.(http.CloseNotifier).CloseNotify()
		}

		attacher.Listen(source, logstream, closer, logstream)
	})

	m.Get("/routes", func(w http.ResponseWriter, req *http.Request) {
		w.Header().Add("Content-Type", "application/json")
		routes, _ := router.GetAll()
		w.Write(append(marshal(routes), '\n'))
	})

	m.Post("/routes", func(w http.ResponseWriter, req *http.Request) (int, string) {
		route := new(Route)
		if err := unmarshal(req.Body, route); err != nil {
			return http.StatusBadRequest, "Bad request: " + err.Error()
		}

		// TODO: validate?
		router.Add(route)

		w.Header().Add("Content-Type", "application/json")
		return http.StatusCreated, string(append(marshal(route), '\n'))
	})

	m.Get("/routes/:id", func(w http.ResponseWriter, req *http.Request, params martini.Params) {
		route, _ := router.Get(params["id"])
		if route == nil {
			http.NotFound(w, req)
			return
		}
		w.Write(append(marshal(route), '\n'))
	})

	m.Delete("/routes/:id", func(w http.ResponseWriter, req *http.Request, params martini.Params) {
		if ok := router.Remove(params["id"]); !ok {
			http.NotFound(w, req)
		}
	})

	log.Println("logspout serving http on :" + port)
	log.Fatal(http.ListenAndServe(":"+port, m))
}
<commit_msg>DRYing up the spooler business a bit<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"log/syslog"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"code.google.com/p/go.net/websocket"
	"github.com/fsouza/go-dockerclient"
	"github.com/go-martini/martini"
)

var debugMode bool

func debug(v ...interface{}) {
	if debugMode {
		log.Println(v...)
	}
}

func assert(err error, context string) {
	if err != nil {
		log.Fatal(context+": ", err)
	}
}

func getopt(name, dfault string) string {
	value := os.Getenv(name)
	if value == "" {
		value = dfault
	}
	return value
}

type Colorizer map[string]int

// returns up to 14 color escape codes (then repeats) for each unique key
func (c Colorizer) Get(key string) string {
	i, exists := c[key]
	if !exists {
		c[key] = len(c)
		i = c[key]
	}
	bright := "1;"
	if i%14 > 6 {
		bright = ""
	}
	return "\x1b[" + bright + "3" + strconv.Itoa(7-(i%7)) + "m"
}

func logDataToString(data interface{}) string {
	switch d := data.(type) {
	case string:
		return d
	default:
		jsonData, err := json.Marshal(d)
		if err != nil {
			debug("httpPostStreamer - Error encoding JSON for data: ", jsonData)
		}
		return string(jsonData)
	}
}

func httpPostStreamer(target Target, types []string, logstream chan *Log) {

	url := target.Type + "://" + target.Addr + target.Path
	debug("httpPostStreamer - URL:", url)

	typestr := "," + strings.Join(types, ",") + ","
	debug("httpPostStreamer - typestr:", typestr)

	hostname, err := os.Hostname()
	if err != nil {
		debug("httpPostStreamer - error getting hostname:", err)
		hostname = "<unknown>"
	}

	tr := &http.Transport{}
	client := &http.Client{Transport: tr}

	timeout := 1000 * time.Millisecond
	timer := time.NewTimer(timeout)
	capacity := 128
	buffer := make([]*Log, 0, capacity)
	for {
		select {
		case logline := <-logstream:
			if typestr != ",," && !strings.Contains(typestr, logline.Type) {
				continue
			}
			logline.Hostname = hostname
			buffer = append(buffer, logline)
			if len(buffer) >= cap(buffer) {
				//debug("httpPostStreamer - buffer full")
				buffer = flushHttp(buffer, client, url, timer, timeout, capacity)
			}
		case <-timer.C:
			//debug("httpPostStreamer - timer expired, buffer length:", len(buffer))
			buffer = flushHttp(buffer, client, url, timer, timeout, capacity)
		}
	}
}

func flushHttp(buffer []*Log, client *http.Client, url string,
	timer *time.Timer, timeout time.Duration, capacity int) []*Log {

	if len(buffer) > 0 {

		messages := make([]string, len(buffer))
		for l := range buffer {
			logline := buffer[l]
			message, err := json.Marshal(logline)
			if err != nil {
				debug("httpPostStreamer - Error encoding JSON: ", err)
				continue
			}
			messages = append(messages, string(message))
		}

		payload := strings.Join(messages, "\n")
		debug("messages:", payload)
		req, err := http.NewRequest("POST", url, strings.NewReader(payload))
		if err != nil {
			debug("httpPostStreamer - Error on http.NewRequest: ", err, url)
		}

		resp, err := client.Do(req)
		if err != nil {
			debug("httpPostStreamer - Error on client.Do: ", err, url)
		}
		io.Copy(ioutil.Discard, resp.Body)
		resp.Body.Close()
	}

	buffer = make([]*Log, 0, capacity)
	timer.Stop()
	select {
	case <-timer.C:
	default:
	}
	timer.Reset(timeout)
	return buffer
}

func syslogStreamer(target Target, types []string, logstream chan *Log) {
	typestr := "," + strings.Join(types, ",") + ","
	for logline := range logstream {
		if typestr != ",," && !strings.Contains(typestr, logline.Type) {
			continue
		}
		tag := logline.Name + target.AppendTag
		remote, err := syslog.Dial("udp", target.Addr, syslog.LOG_USER|syslog.LOG_INFO, tag)
		assert(err, "syslog")
		io.WriteString(remote, logDataToString(logline.Data))
	}
}

func udpStreamer(target Target, types []string, logstream chan *Log) {
	typestr := "," + strings.Join(types, ",") + ","
	addr, err := net.ResolveUDPAddr("udp", target.Addr)
	assert(err, "resolve udp failed")
	conn, err := net.DialUDP("udp", nil, addr)
	assert(err, "connect udp failed")
	encoder := json.NewEncoder(conn)
	defer conn.Close()
	for logline := range logstream {
		if typestr != ",," && !strings.Contains(typestr, logline.Type) {
			continue
		}
		encoder.Encode(logline)
	}
}

func websocketStreamer(w http.ResponseWriter, req *http.Request, logstream chan *Log, closer chan bool) {
	websocket.Handler(func(conn *websocket.Conn) {
		for logline := range logstream {
			if req.URL.Query().Get("type") != "" && logline.Type != req.URL.Query().Get("type") {
				continue
			}
			_, err := conn.Write(append(marshal(logline), '\n'))
			if err != nil {
				closer <- true
				return
			}
		}
	}).ServeHTTP(w, req)
}

func httpStreamer(w http.ResponseWriter, req *http.Request, logstream chan *Log, multi bool) {
	var colors Colorizer
	var usecolor, usejson bool
	nameWidth := 16
	if req.URL.Query().Get("colors") != "off" {
		colors = make(Colorizer)
		usecolor = true
	}
	if req.Header.Get("Accept") == "application/json" {
		w.Header().Add("Content-Type", "application/json")
		usejson = true
	} else {
		w.Header().Add("Content-Type", "text/plain")
	}
	for logline := range logstream {
		if req.URL.Query().Get("types") != "" && logline.Type != req.URL.Query().Get("types") {
			continue
		}
		if usejson {
			w.Write(append(marshal(logline), '\n'))
		} else {
			if multi {
				if len(logline.Name) > nameWidth {
					nameWidth = len(logline.Name)
				}
				if usecolor {
					w.Write([]byte(fmt.Sprintf(
						"%s%"+strconv.Itoa(nameWidth)+"s|%s\x1b[0m\n",
						colors.Get(logline.Name), logline.Name, logline.Data,
					)))
				} else {
					w.Write([]byte(fmt.Sprintf(
						"%"+strconv.Itoa(nameWidth)+"s|%s\n", logline.Name,
						logDataToString(logline.Data),
					)))
				}
			} else {
				w.Write(append([]byte(logDataToString(logline.Data)), '\n'))
			}
		}
		w.(http.Flusher).Flush()
	}
}

func main() {
	debugMode = getopt("DEBUG", "") != ""
	port := getopt("PORT", "8000")
	endpoint := getopt("DOCKER_HOST", "unix:///var/run/docker.sock")
	routespath := getopt("ROUTESPATH", "/var/lib/logspout")
	eventlog := getopt("EVENTLOG", "") != ""

	client, err := docker.NewClient(endpoint)
	assert(err, "docker")
	attacher := NewAttachManager(client)
	router := NewRouteManager(attacher)

	if eventlog {
		debug("event logging enabled")
	}

	if len(os.Args) > 1 {
		u, err := url.Parse(os.Args[1])
		assert(err, "url")
		log.Println("routing all to " + os.Args[1])
		router.Add(&Route{Target: Target{Type: u.Scheme, Addr: u.Host, Path: u.Path}})
	}

	if _, err := os.Stat(routespath); err == nil {
		log.Println("loading and persisting routes in " + routespath)
		assert(router.Load(RouteFileStore(routespath)), "persistor")
	}

	m := martini.Classic()

	m.Get("/logs(?:/(?P<predicate>[a-zA-Z]+):(?P<value>.+))?", func(w http.ResponseWriter, req *http.Request, params martini.Params) {
		source := new(Source)
		switch {
		case params["predicate"] == "id" && params["value"] != "":
			source.ID = params["value"][:12]
		case params["predicate"] == "name" && params["value"] != "":
			source.Name = params["value"]
		case params["predicate"] == "filter" && params["value"] != "":
			source.Filter = params["value"]
		}

		if source.ID != "" && attacher.Get(source.ID) == nil {
			http.NotFound(w, req)
			return
		}

		logstream := make(chan *Log)
		defer close(logstream)

		var closer <-chan bool
		if req.Header.Get("Upgrade") == "websocket" {
			closerBi := make(chan bool)
			go websocketStreamer(w, req, logstream, closerBi)
			closer = closerBi
		} else {
			go httpStreamer(w, req, logstream, source.All() || source.Filter != "")
			closer = w.(http.CloseNotifier).CloseNotify()
		}

		attacher.Listen(source, logstream, closer, logstream)
	})

	m.Get("/routes", func(w http.ResponseWriter, req *http.Request) {
		w.Header().Add("Content-Type", "application/json")
		routes, _ := router.GetAll()
		w.Write(append(marshal(routes), '\n'))
	})

	m.Post("/routes", func(w http.ResponseWriter, req *http.Request) (int, string) {
		route := new(Route)
		if err := unmarshal(req.Body, route); err != nil {
			return http.StatusBadRequest, "Bad request: " + err.Error()
		}

		// TODO: validate?
		router.Add(route)

		w.Header().Add("Content-Type", "application/json")
		return http.StatusCreated, string(append(marshal(route), '\n'))
	})

	m.Get("/routes/:id", func(w http.ResponseWriter, req *http.Request, params martini.Params) {
		route, _ := router.Get(params["id"])
		if route == nil {
			http.NotFound(w, req)
			return
		}
		w.Write(append(marshal(route), '\n'))
	})

	m.Delete("/routes/:id", func(w http.ResponseWriter, req *http.Request, params martini.Params) {
		if ok := router.Remove(params["id"]); !ok {
			http.NotFound(w, req)
		}
	})

	log.Println("logspout serving http on :" + port)
	log.Fatal(http.ListenAndServe(":"+port, m))
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 flow

import (
	"fmt"
	"sync"
	"time"

	"github.com/redhat-cip/skydive/logging"
)

type FlowTable struct {
	lock    sync.RWMutex
	table   map[string]*Flow
	manager FlowTableManager
}

type FlowTableAsyncNotificationUpdate interface {
	AsyncNotificationUpdate(every time.Duration)
}

func NewFlowTable() *FlowTable {
	return &FlowTable{table: make(map[string]*Flow)}
}

func (ft *FlowTable) String() string {
	ft.lock.RLock()
	defer ft.lock.RUnlock()
	return fmt.Sprintf("%d flows", len(ft.table))
}

func (ft *FlowTable) Update(flows []*Flow) {
	ft.lock.Lock()
	for _, f := range flows {
		if _, found := ft.table[f.UUID]; !found {
			ft.table[f.UUID] = f
		}
	}
	ft.lock.Unlock()
}

func (ft *FlowTable) GetFlows() []*Flow {
	ft.lock.RLock()
	defer ft.lock.RUnlock()

	flows := []*Flow{}
	for _, f := range ft.table {
		flows = append(flows, &*f)
	}
	return flows
}

func (ft *FlowTable) GetFlow(key string) *Flow {
	ft.lock.RLock()
	defer ft.lock.RUnlock()
	if flow, found := ft.table[key]; found {
		return flow
	}

	return nil
}

func (ft *FlowTable) GetOrCreateFlow(key string) (*Flow, bool) {
	ft.lock.Lock()
	defer ft.lock.Unlock()
	if flow, found := ft.table[key]; found {
		return flow, false
	}

	new := &Flow{}
	ft.table[key] = new

	return new, true
}

func (ft *FlowTable) NewFlowTableFromFlows(flows []*Flow) *FlowTable {
	nft := NewFlowTable()
	nft.Update(flows)
	return nft
}

/* Return a new FlowTable that contain <last> active flows */
func (ft *FlowTable) FilterLast(last time.Duration) []*Flow {
	var flows []*Flow
	selected := time.Now().Unix() - int64((last).Seconds())
	ft.lock.RLock()
	for _, f := range ft.table {
		fs := f.GetStatistics()
		if fs.Last >= selected {
			flows = append(flows, f)
		}
	}
	ft.lock.RUnlock()
	return flows
}

func (ft *FlowTable) SelectLayer(endpointType FlowEndpointType, list []string) []*Flow {
	meth := make(map[string][]*Flow)
	ft.lock.RLock()
	for _, f := range ft.table {
		layerFlow := f.GetStatistics().Endpoints[endpointType.Value()]
		if layerFlow.AB.Value == "ff:ff:ff:ff:ff:ff" || layerFlow.BA.Value == "ff:ff:ff:ff:ff:ff" {
			continue
		}
		meth[layerFlow.AB.Value] = append(meth[layerFlow.AB.Value], f)
		meth[layerFlow.BA.Value] = append(meth[layerFlow.BA.Value], f)
	}
	ft.lock.RUnlock()

	mflows := make(map[*Flow]struct{})
	var flows []*Flow
	for _, eth := range list {
		if flist, ok := meth[eth]; ok {
			for _, f := range flist {
				if _, found := mflows[f]; !found {
					mflows[f] = struct{}{}
					flows = append(flows, f)
				}
			}
		}
	}
	return flows
}

/*
 * Following function are FlowTable manager helpers
 */
func (ft *FlowTable) Expire(now time.Time) {
	timepoint := now.Unix() - int64((ft.manager.expire.duration).Seconds())
	ft.lock.Lock()
	ft.expire(ft.manager.expire.callback, timepoint)
	ft.lock.Unlock()
}

/* Internal call only, Must be called under ft.lock.Lock() */
func (ft *FlowTable) expire(fn ExpireUpdateFunc, expireBefore int64) {
	var expiredFlows []*Flow
	flowTableSzBefore := len(ft.table)
	for _, f := range ft.table {
		fs := f.GetStatistics()
		if fs.Last < expireBefore {
			duration := time.Duration(fs.Last - fs.Start)
			logging.GetLogger().Debugf("Expire flow %s Duration %v", f.UUID, duration)
			expiredFlows = append(expiredFlows, f)
		}
	}
	/* Advise Clients */
	fn(expiredFlows)
	for _, f := range expiredFlows {
		delete(ft.table, f.UUID)
	}
	flowTableSz := len(ft.table)
	logging.GetLogger().Debugf("Expire Flow : removed %v ; new size %v", flowTableSzBefore-flowTableSz, flowTableSz)
}

func (ft *FlowTable) Updated(now time.Time) {
	timepoint := now.Unix() - int64((ft.manager.updated.duration).Seconds())
	ft.lock.RLock()
	ft.updated(ft.manager.updated.callback, timepoint)
	ft.lock.RUnlock()
}

/* Internal call only, Must be called under ft.lock.RLock() */
func (ft *FlowTable) updated(fn ExpireUpdateFunc, updateFrom int64) {
	var updatedFlows []*Flow
	for _, f := range ft.table {
		fs := f.GetStatistics()
		if fs.Last > updateFrom {
			updatedFlows = append(updatedFlows, f)
		}
	}
	/* Advise Clients */
	fn(updatedFlows)
	logging.GetLogger().Debugf("Send updated Flow %d", len(updatedFlows))
}

func (ft *FlowTable) ExpireNow() {
	const Now = int64(^uint64(0) >> 1)
	ft.lock.Lock()
	ft.expire(ft.manager.expire.callback, Now)
	ft.lock.Unlock()
}

/* Asynchrnously Register an expire callback fn with last updated flow 'since', each 'since' tick  */
func (ft *FlowTable) RegisterExpire(fn ExpireUpdateFunc, every time.Duration) {
	ft.lock.Lock()
	ft.manager.expire.Register(&FlowTableManagerAsyncParam{ft.expire, fn, every, every})
	ft.lock.Unlock()
}

/* Asynchrnously call the callback fn with last updated flow 'since', each 'since' tick  */
func (ft *FlowTable) RegisterUpdated(fn ExpireUpdateFunc, since time.Duration) {
	ft.lock.Lock()
	ft.manager.updated.Register(&FlowTableManagerAsyncParam{ft.updated, fn, since, since + 2})
	ft.lock.Unlock()
}

func (ft *FlowTable) UnregisterAll() {
	ft.lock.Lock()
	if ft.manager.updated.running {
		ft.manager.updated.Unregister()
	}
	if ft.manager.expire.running {
		ft.manager.expire.Unregister()
	}
	ft.lock.Unlock()

	ft.ExpireNow()
}

func (ft *FlowTable) GetExpireTicker() <-chan time.Time {
	return ft.manager.expire.ticker.C
}

func (ft *FlowTable) GetUpdatedTicker() <-chan time.Time {
	return ft.manager.updated.ticker.C
}
<commit_msg>flow: Update flow table even if flow uuid already present<commit_after>/*
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 flow

import (
	"fmt"
	"sync"
	"time"

	"github.com/redhat-cip/skydive/logging"
)

type FlowTable struct {
	lock    sync.RWMutex
	table   map[string]*Flow
	manager FlowTableManager
}

type FlowTableAsyncNotificationUpdate interface {
	AsyncNotificationUpdate(every time.Duration)
}

func NewFlowTable() *FlowTable {
	return &FlowTable{table: make(map[string]*Flow)}
}

func (ft *FlowTable) String() string {
	ft.lock.RLock()
	defer ft.lock.RUnlock()
	return fmt.Sprintf("%d flows", len(ft.table))
}

func (ft *FlowTable) Update(flows []*Flow) {
	ft.lock.Lock()
	for _, f := range flows {
		ft.table[f.UUID] = f
	}
	ft.lock.Unlock()
}

func (ft *FlowTable) GetFlows() []*Flow {
	ft.lock.RLock()
	defer ft.lock.RUnlock()

	flows := []*Flow{}
	for _, f := range ft.table {
		flows = append(flows, &*f)
	}
	return flows
}

func (ft *FlowTable) GetFlow(key string) *Flow {
	ft.lock.RLock()
	defer ft.lock.RUnlock()
	if flow, found := ft.table[key]; found {
		return flow
	}

	return nil
}

func (ft *FlowTable) GetOrCreateFlow(key string) (*Flow, bool) {
	ft.lock.Lock()
	defer ft.lock.Unlock()
	if flow, found := ft.table[key]; found {
		return flow, false
	}

	new := &Flow{}
	ft.table[key] = new

	return new, true
}

func (ft *FlowTable) NewFlowTableFromFlows(flows []*Flow) *FlowTable {
	nft := NewFlowTable()
	nft.Update(flows)
	return nft
}

/* Return a new FlowTable that contain <last> active flows */
func (ft *FlowTable) FilterLast(last time.Duration) []*Flow {
	var flows []*Flow
	selected := time.Now().Unix() - int64((last).Seconds())
	ft.lock.RLock()
	for _, f := range ft.table {
		fs := f.GetStatistics()
		if fs.Last >= selected {
			flows = append(flows, f)
		}
	}
	ft.lock.RUnlock()
	return flows
}

func (ft *FlowTable) SelectLayer(endpointType FlowEndpointType, list []string) []*Flow {
	meth := make(map[string][]*Flow)
	ft.lock.RLock()
	for _, f := range ft.table {
		layerFlow := f.GetStatistics().Endpoints[endpointType.Value()]
		if layerFlow.AB.Value == "ff:ff:ff:ff:ff:ff" || layerFlow.BA.Value == "ff:ff:ff:ff:ff:ff" {
			continue
		}
		meth[layerFlow.AB.Value] = append(meth[layerFlow.AB.Value], f)
		meth[layerFlow.BA.Value] = append(meth[layerFlow.BA.Value], f)
	}
	ft.lock.RUnlock()

	mflows := make(map[*Flow]struct{})
	var flows []*Flow
	for _, eth := range list {
		if flist, ok := meth[eth]; ok {
			for _, f := range flist {
				if _, found := mflows[f]; !found {
					mflows[f] = struct{}{}
					flows = append(flows, f)
				}
			}
		}
	}
	return flows
}

/*
 * Following function are FlowTable manager helpers
 */
func (ft *FlowTable) Expire(now time.Time) {
	timepoint := now.Unix() - int64((ft.manager.expire.duration).Seconds())
	ft.lock.Lock()
	ft.expire(ft.manager.expire.callback, timepoint)
	ft.lock.Unlock()
}

/* Internal call only, Must be called under ft.lock.Lock() */
func (ft *FlowTable) expire(fn ExpireUpdateFunc, expireBefore int64) {
	var expiredFlows []*Flow
	flowTableSzBefore := len(ft.table)
	for _, f := range ft.table {
		fs := f.GetStatistics()
		if fs.Last < expireBefore {
			duration := time.Duration(fs.Last - fs.Start)
			logging.GetLogger().Debugf("Expire flow %s Duration %v", f.UUID, duration)
			expiredFlows = append(expiredFlows, f)
		}
	}
	/* Advise Clients */
	fn(expiredFlows)
	for _, f := range expiredFlows {
		delete(ft.table, f.UUID)
	}
	flowTableSz := len(ft.table)
	logging.GetLogger().Debugf("Expire Flow : removed %v ; new size %v", flowTableSzBefore-flowTableSz, flowTableSz)
}

func (ft *FlowTable) Updated(now time.Time) {
	timepoint := now.Unix() - int64((ft.manager.updated.duration).Seconds())
	ft.lock.RLock()
	ft.updated(ft.manager.updated.callback, timepoint)
	ft.lock.RUnlock()
}

/* Internal call only, Must be called under ft.lock.RLock() */
func (ft *FlowTable) updated(fn ExpireUpdateFunc, updateFrom int64) {
	var updatedFlows []*Flow
	for _, f := range ft.table {
		fs := f.GetStatistics()
		if fs.Last > updateFrom {
			updatedFlows = append(updatedFlows, f)
		}
	}
	/* Advise Clients */
	fn(updatedFlows)
	logging.GetLogger().Debugf("Send updated Flow %d", len(updatedFlows))
}

func (ft *FlowTable) ExpireNow() {
	const Now = int64(^uint64(0) >> 1)
	ft.lock.Lock()
	ft.expire(ft.manager.expire.callback, Now)
	ft.lock.Unlock()
}

/* Asynchrnously Register an expire callback fn with last updated flow 'since', each 'since' tick  */
func (ft *FlowTable) RegisterExpire(fn ExpireUpdateFunc, every time.Duration) {
	ft.lock.Lock()
	ft.manager.expire.Register(&FlowTableManagerAsyncParam{ft.expire, fn, every, every})
	ft.lock.Unlock()
}

/* Asynchrnously call the callback fn with last updated flow 'since', each 'since' tick  */
func (ft *FlowTable) RegisterUpdated(fn ExpireUpdateFunc, since time.Duration) {
	ft.lock.Lock()
	ft.manager.updated.Register(&FlowTableManagerAsyncParam{ft.updated, fn, since, since + 2})
	ft.lock.Unlock()
}

func (ft *FlowTable) UnregisterAll() {
	ft.lock.Lock()
	if ft.manager.updated.running {
		ft.manager.updated.Unregister()
	}
	if ft.manager.expire.running {
		ft.manager.expire.Unregister()
	}
	ft.lock.Unlock()

	ft.ExpireNow()
}

func (ft *FlowTable) GetExpireTicker() <-chan time.Time {
	return ft.manager.expire.ticker.C
}

func (ft *FlowTable) GetUpdatedTicker() <-chan time.Time {
	return ft.manager.updated.ticker.C
}
<|endoftext|>


<commit_before>package lua

import (
	"fmt"
	"os"
	"reflect"
	"syscall"
	"unsafe"
)

var lua_pushnil = luaDLL.NewProc("lua_pushnil")

func (this Lua) PushNil() {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushNil()\n", this)
	}
	lua_pushnil.Call(this.State())
}

var lua_pushboolean = luaDLL.NewProc("lua_pushboolean")

func (this Lua) PushBool(value bool) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushBool(%v)\n", this, value)
	}
	if value {
		lua_pushboolean.Call(this.State(), 1)
	} else {
		lua_pushboolean.Call(this.State(), 0)
	}
}

var lua_pushinteger = luaDLL.NewProc("lua_pushinteger")

func (this Lua) PushInteger(value Integer) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushInteger(%v)\n", this, value)
	}
	params := make([]uintptr, 0, 4)
	params = append(params, this.State())
	params = value.Expand(params)
	lua_pushinteger.Call(params...)
}

var lua_pushlstring = luaDLL.NewProc("lua_pushlstring")

func (this Lua) PushBytes(data []byte) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushBytes(len=%v)\n", this, len(data))
	}
	if data != nil && len(data) >= 1 {
		lua_pushlstring.Call(this.State(),
			uintptr(unsafe.Pointer(&data[0])),
			uintptr(len(data)))
	} else {
		zerobyte := []byte{'\000'}
		lua_pushlstring.Call(this.State(),
			uintptr(unsafe.Pointer(&zerobyte[0])),
			0)
	}
}

var lua_pushstring = luaDLL.NewProc("lua_pushstring")

func (this Lua) PushString(str string) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushString(%v)\n", this, str)
	}
	// BytePtrFromString can not use the string which contains NUL
	array := make([]byte, len(str)+1)
	copy(array, str)
	lua_pushlstring.Call(this.State(),
		uintptr(unsafe.Pointer(&array[0])),
		uintptr(len(str)))
}

var lua_pushlightuserdata = luaDLL.NewProc("lua_pushlightuserdata")

func (this Lua) PushLightUserData(p unsafe.Pointer) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushLightUserData(%v)", this, p)
	}
	lua_pushlightuserdata.Call(this.State(), uintptr(p))
}

var lua_pushvalue = luaDLL.NewProc("lua_pushvalue")

func (this Lua) PushValue(index int) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushValue(%v)\n", this, index)
	}
	lua_pushvalue.Call(this.State(), uintptr(index))
}

var lua_pushcclosure = luaDLL.NewProc("lua_pushcclosure")

func (this Lua) PushGoClosure(fn func(Lua) int, n uintptr) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushGoClosure(%v,%v)\n", this, fn, n)
	}
	lua_pushcclosure.Call(this.State(), syscall.NewCallbackCDecl(fn), n)
}

func (this Lua) PushGoFunction(fn func(Lua) int) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushGoFunction(%v)\n", this, fn)
	}
	this.PushGoClosure(fn, 0)
}

func UpValueIndex(i int) int {
	return LUA_REGISTRYINDEX - i
}

type TGoFunction func(Lua) int

func (this TGoFunction) Push(L Lua) int {
	if trace {
		fmt.Fprintf(os.Stderr, "lua.TGoFunction(%v).Push(%v)\n", this, L)
	}
	L.PushGoFunction(this)
	return 1
}

func (this Lua) PushCFunction(fn uintptr) {
	if trace {
		fmt.Fprintf(os.Stderr, "lua(%v).PushCFunction(%v)\n", this, fn)
	}
	lua_pushcclosure.Call(this.State(), fn, 0)
}

type Object interface {
	Push(Lua) int
}

func (this Lua) Push(values ...interface{}) int {
	if trace {
		fmt.Fprintf(os.Stderr, "lua(%v).Push(%v...)\n", this, values)
	}
	for _, value := range values {
		if value == nil {
			this.PushNil()
			continue
		}
		switch t := value.(type) {
		case bool:
			this.PushBool(t)
		case int:
			this.PushInteger(Integer(t))
		case int64:
			this.PushInteger(Integer(t))
		case string:
			this.PushString(t)
		case func(L Lua) int:
			this.PushGoFunction(t)
		case []byte:
			this.PushBytes(t)
		case error:
			this.PushString(t.Error())
		case TTable:
			this.NewTable()
			for key, val := range t.Dict {
				this.PushString(key)
				this.Push(val)
				this.SetTable(-3)
			}
			for key, val := range t.Array {
				this.Push(key)
				this.Push(val)
				this.SetTable(-3)
			}
		case unsafe.Pointer:
			this.PushLightUserData(t)
		case Object:
			t.Push(this)
		default:
			if !this.PushReflect(value) {
				panic(fmt.Sprintf(
					"lua.Lua.Push(%T): value is not supported type", t))
			}
		}
	}
	return len(values)
}

func (this Lua) PushReflect(value interface{}) bool {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushReflect(%v)\n", this, value)
	}
	if value == nil {
		this.PushNil()
	}
	return this.pushReflect(reflect.ValueOf(value))
}

func (this Lua) pushReflect(value reflect.Value) bool {
	switch value.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		this.PushInteger(Integer(value.Int()))
	case reflect.Uint, reflect.Uint16, reflect.Uint32,
		reflect.Uint64, reflect.Uintptr:
		this.PushInteger(Integer(value.Uint()))
	case reflect.Bool:
		this.PushBool(value.Bool())
	case reflect.String:
		this.PushString(value.String())
	case reflect.Interface:
		this.Push(value.Interface())
	case reflect.Slice, reflect.Array:
		elem := value.Type().Elem()
		if elem.Kind() == reflect.Uint8 {
			buffer := make([]byte, 0, value.Len())
			for i, end := 0, value.Len(); i < end; i++ {
				buffer = append(buffer, byte(value.Index(i).Uint()))
			}
			this.PushBytes(buffer)
		} else {
			this.NewTable()
			for i, end := 0, value.Len(); i < end; i++ {
				val := value.Index(i)
				this.PushInteger(Integer(i + 1))
				this.pushReflect(val)
				this.SetTable(-3)
			}
		}
	case reflect.Map:
		this.NewTable()
		for _, key := range value.MapKeys() {
			this.pushReflect(key)
			val := value.MapIndex(key)
			this.pushReflect(val)
			this.SetTable(-3)
		}
	default:
		return false
	}
	return true
}
<commit_msg>Fix: go vet: error: arg fn in printf call is a function value, not a function call<commit_after>package lua

import (
	"fmt"
	"os"
	"reflect"
	"syscall"
	"unsafe"
)

var lua_pushnil = luaDLL.NewProc("lua_pushnil")

func (this Lua) PushNil() {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushNil()\n", this)
	}
	lua_pushnil.Call(this.State())
}

var lua_pushboolean = luaDLL.NewProc("lua_pushboolean")

func (this Lua) PushBool(value bool) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushBool(%v)\n", this, value)
	}
	if value {
		lua_pushboolean.Call(this.State(), 1)
	} else {
		lua_pushboolean.Call(this.State(), 0)
	}
}

var lua_pushinteger = luaDLL.NewProc("lua_pushinteger")

func (this Lua) PushInteger(value Integer) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushInteger(%v)\n", this, value)
	}
	params := make([]uintptr, 0, 4)
	params = append(params, this.State())
	params = value.Expand(params)
	lua_pushinteger.Call(params...)
}

var lua_pushlstring = luaDLL.NewProc("lua_pushlstring")

func (this Lua) PushBytes(data []byte) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushBytes(len=%v)\n", this, len(data))
	}
	if data != nil && len(data) >= 1 {
		lua_pushlstring.Call(this.State(),
			uintptr(unsafe.Pointer(&data[0])),
			uintptr(len(data)))
	} else {
		zerobyte := []byte{'\000'}
		lua_pushlstring.Call(this.State(),
			uintptr(unsafe.Pointer(&zerobyte[0])),
			0)
	}
}

var lua_pushstring = luaDLL.NewProc("lua_pushstring")

func (this Lua) PushString(str string) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushString(%v)\n", this, str)
	}
	// BytePtrFromString can not use the string which contains NUL
	array := make([]byte, len(str)+1)
	copy(array, str)
	lua_pushlstring.Call(this.State(),
		uintptr(unsafe.Pointer(&array[0])),
		uintptr(len(str)))
}

var lua_pushlightuserdata = luaDLL.NewProc("lua_pushlightuserdata")

func (this Lua) PushLightUserData(p unsafe.Pointer) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushLightUserData(%v)", this, p)
	}
	lua_pushlightuserdata.Call(this.State(), uintptr(p))
}

var lua_pushvalue = luaDLL.NewProc("lua_pushvalue")

func (this Lua) PushValue(index int) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushValue(%v)\n", this, index)
	}
	lua_pushvalue.Call(this.State(), uintptr(index))
}

var lua_pushcclosure = luaDLL.NewProc("lua_pushcclosure")

func (this Lua) PushGoClosure(fn func(Lua) int, n uintptr) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushGoClosure(%v)\n", this, n)
	}
	lua_pushcclosure.Call(this.State(), syscall.NewCallbackCDecl(fn), n)
}

func (this Lua) PushGoFunction(fn func(Lua) int) {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushGoFunction\n", this)
	}
	this.PushGoClosure(fn, 0)
}

func UpValueIndex(i int) int {
	return LUA_REGISTRYINDEX - i
}

type TGoFunction func(Lua) int

func (this TGoFunction) Push(L Lua) int {
	if trace {
		fmt.Fprintf(os.Stderr, "lua.TGoFunction(%v).Push(%v)\n", this, L)
	}
	L.PushGoFunction(this)
	return 1
}

func (this Lua) PushCFunction(fn uintptr) {
	if trace {
		fmt.Fprintf(os.Stderr, "lua(%v).PushCFunction(%v)\n", this, fn)
	}
	lua_pushcclosure.Call(this.State(), fn, 0)
}

type Object interface {
	Push(Lua) int
}

func (this Lua) Push(values ...interface{}) int {
	if trace {
		fmt.Fprintf(os.Stderr, "lua(%v).Push(%v...)\n", this, values)
	}
	for _, value := range values {
		if value == nil {
			this.PushNil()
			continue
		}
		switch t := value.(type) {
		case bool:
			this.PushBool(t)
		case int:
			this.PushInteger(Integer(t))
		case int64:
			this.PushInteger(Integer(t))
		case string:
			this.PushString(t)
		case func(L Lua) int:
			this.PushGoFunction(t)
		case []byte:
			this.PushBytes(t)
		case error:
			this.PushString(t.Error())
		case TTable:
			this.NewTable()
			for key, val := range t.Dict {
				this.PushString(key)
				this.Push(val)
				this.SetTable(-3)
			}
			for key, val := range t.Array {
				this.Push(key)
				this.Push(val)
				this.SetTable(-3)
			}
		case unsafe.Pointer:
			this.PushLightUserData(t)
		case Object:
			t.Push(this)
		default:
			if !this.PushReflect(value) {
				panic(fmt.Sprintf(
					"lua.Lua.Push(%T): value is not supported type", t))
			}
		}
	}
	return len(values)
}

func (this Lua) PushReflect(value interface{}) bool {
	if trace {
		fmt.Fprintf(os.Stderr, "Lua(%v).PushReflect(%v)\n", this, value)
	}
	if value == nil {
		this.PushNil()
	}
	return this.pushReflect(reflect.ValueOf(value))
}

func (this Lua) pushReflect(value reflect.Value) bool {
	switch value.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		this.PushInteger(Integer(value.Int()))
	case reflect.Uint, reflect.Uint16, reflect.Uint32,
		reflect.Uint64, reflect.Uintptr:
		this.PushInteger(Integer(value.Uint()))
	case reflect.Bool:
		this.PushBool(value.Bool())
	case reflect.String:
		this.PushString(value.String())
	case reflect.Interface:
		this.Push(value.Interface())
	case reflect.Slice, reflect.Array:
		elem := value.Type().Elem()
		if elem.Kind() == reflect.Uint8 {
			buffer := make([]byte, 0, value.Len())
			for i, end := 0, value.Len(); i < end; i++ {
				buffer = append(buffer, byte(value.Index(i).Uint()))
			}
			this.PushBytes(buffer)
		} else {
			this.NewTable()
			for i, end := 0, value.Len(); i < end; i++ {
				val := value.Index(i)
				this.PushInteger(Integer(i + 1))
				this.pushReflect(val)
				this.SetTable(-3)
			}
		}
	case reflect.Map:
		this.NewTable()
		for _, key := range value.MapKeys() {
			this.pushReflect(key)
			val := value.MapIndex(key)
			this.pushReflect(val)
			this.SetTable(-3)
		}
	default:
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"path"
	"strings"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/i18n"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/gnuflag"
)

type copyCmd struct {
	ephem bool
}

func (c *copyCmd) showByDefault() bool {
	return true
}

func (c *copyCmd) usage() string {
	return i18n.G(
		`Copy containers within or in between lxd instances.

lxc copy [remote:]<source container> [remote:]<destination container> [--ephemeral|e]`)
}

func (c *copyCmd) flags() {
	gnuflag.BoolVar(&c.ephem, "ephemeral", false, i18n.G("Ephemeral container"))
	gnuflag.BoolVar(&c.ephem, "e", false, i18n.G("Ephemeral container"))
}

func copyContainer(config *lxd.Config, sourceResource string, destResource string, keepVolatile bool, ephemeral int) error {
	sourceRemote, sourceName := config.ParseRemoteAndContainer(sourceResource)
	destRemote, destName := config.ParseRemoteAndContainer(destResource)

	if sourceName == "" {
		return fmt.Errorf(i18n.G("you must specify a source container name"))
	}

	if destName == "" {
		destName = sourceName
	}

	source, err := lxd.NewClient(config, sourceRemote)
	if err != nil {
		return err
	}

	status := &shared.ContainerState{}

	// TODO: presumably we want to do this for copying snapshots too? We
	// need to think a bit more about how we track the baseImage in the
	// face of LVM and snapshots in general; this will probably make more
	// sense once that work is done.
	baseImage := ""

	if !shared.IsSnapshot(sourceName) {
		status, err = source.ContainerStatus(sourceName)
		if err != nil {
			return err
		}

		baseImage = status.Config["volatile.base_image"]

		if !keepVolatile {
			for k := range status.Config {
				if strings.HasPrefix(k, "volatile") {
					delete(status.Config, k)
				}
			}
		}
	}

	// Do a local copy if the remotes are the same, otherwise do a migration
	if sourceRemote == destRemote {
		if sourceName == destName {
			return fmt.Errorf(i18n.G("can't copy to the same container name"))
		}

		cp, err := source.LocalCopy(sourceName, destName, status.Config, status.Profiles, ephemeral == 1)
		if err != nil {
			return err
		}

		return source.WaitForSuccess(cp.Operation)
	} else {
		dest, err := lxd.NewClient(config, destRemote)
		if err != nil {
			return err
		}

		sourceProfs := shared.NewStringSet(status.Profiles)
		destProfs, err := dest.ListProfiles()
		if err != nil {
			return err
		}

		if !sourceProfs.IsSubset(shared.NewStringSet(destProfs)) {
			return fmt.Errorf(i18n.G("not all the profiles from the source exist on the target"))
		}

		if ephemeral == -1 {
			ct, err := source.ContainerStatus(sourceName)
			if err != nil {
				return err
			}

			if ct.Ephemeral {
				ephemeral = 1
			} else {
				ephemeral = 0
			}
		}

		sourceWSResponse, err := source.GetMigrationSourceWS(sourceName)
		if err != nil {
			return err
		}

		secrets := map[string]string{}

		op, err := sourceWSResponse.MetadataAsOperation()
		if err == nil && op.Metadata != nil {
			for k, v := range *op.Metadata {
				secrets[k] = v.(string)
			}
		} else {
			// FIXME: This is a backward compatibility codepath
			if err := json.Unmarshal(sourceWSResponse.Metadata, &secrets); err != nil {
				return err
			}
		}

		addresses, err := source.Addresses()
		if err != nil {
			return err
		}

		for _, addr := range addresses {
			sourceWSUrl := "wss://" + addr + path.Join(sourceWSResponse.Operation, "websocket")

			var migration *lxd.Response
			migration, err = dest.MigrateFrom(destName, sourceWSUrl, secrets, status.Architecture, status.Config, status.Devices, status.Profiles, baseImage, ephemeral == 1)
			if err != nil {
				continue
			}

			if err = dest.WaitForSuccess(migration.Operation); err != nil {
				continue
			}

			return nil
		}

		return err
	}
}

func (c *copyCmd) run(config *lxd.Config, args []string) error {
	if len(args) != 2 {
		return errArgs
	}

	ephem := 0
	if c.ephem {
		ephem = 1
	}

	return copyContainer(config, args[0], args[1], false, ephem)
}
<commit_msg>Update client to respect spec wrt migration<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/i18n"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/gnuflag"
)

type copyCmd struct {
	ephem bool
}

func (c *copyCmd) showByDefault() bool {
	return true
}

func (c *copyCmd) usage() string {
	return i18n.G(
		`Copy containers within or in between lxd instances.

lxc copy [remote:]<source container> [remote:]<destination container> [--ephemeral|e]`)
}

func (c *copyCmd) flags() {
	gnuflag.BoolVar(&c.ephem, "ephemeral", false, i18n.G("Ephemeral container"))
	gnuflag.BoolVar(&c.ephem, "e", false, i18n.G("Ephemeral container"))
}

func copyContainer(config *lxd.Config, sourceResource string, destResource string, keepVolatile bool, ephemeral int) error {
	sourceRemote, sourceName := config.ParseRemoteAndContainer(sourceResource)
	destRemote, destName := config.ParseRemoteAndContainer(destResource)

	if sourceName == "" {
		return fmt.Errorf(i18n.G("you must specify a source container name"))
	}

	if destName == "" {
		destName = sourceName
	}

	source, err := lxd.NewClient(config, sourceRemote)
	if err != nil {
		return err
	}

	status := &shared.ContainerState{}

	// TODO: presumably we want to do this for copying snapshots too? We
	// need to think a bit more about how we track the baseImage in the
	// face of LVM and snapshots in general; this will probably make more
	// sense once that work is done.
	baseImage := ""

	if !shared.IsSnapshot(sourceName) {
		status, err = source.ContainerStatus(sourceName)
		if err != nil {
			return err
		}

		baseImage = status.Config["volatile.base_image"]

		if !keepVolatile {
			for k := range status.Config {
				if strings.HasPrefix(k, "volatile") {
					delete(status.Config, k)
				}
			}
		}
	}

	// Do a local copy if the remotes are the same, otherwise do a migration
	if sourceRemote == destRemote {
		if sourceName == destName {
			return fmt.Errorf(i18n.G("can't copy to the same container name"))
		}

		cp, err := source.LocalCopy(sourceName, destName, status.Config, status.Profiles, ephemeral == 1)
		if err != nil {
			return err
		}

		return source.WaitForSuccess(cp.Operation)
	} else {
		dest, err := lxd.NewClient(config, destRemote)
		if err != nil {
			return err
		}

		sourceProfs := shared.NewStringSet(status.Profiles)
		destProfs, err := dest.ListProfiles()
		if err != nil {
			return err
		}

		if !sourceProfs.IsSubset(shared.NewStringSet(destProfs)) {
			return fmt.Errorf(i18n.G("not all the profiles from the source exist on the target"))
		}

		if ephemeral == -1 {
			ct, err := source.ContainerStatus(sourceName)
			if err != nil {
				return err
			}

			if ct.Ephemeral {
				ephemeral = 1
			} else {
				ephemeral = 0
			}
		}

		sourceWSResponse, err := source.GetMigrationSourceWS(sourceName)
		if err != nil {
			return err
		}

		secrets := map[string]string{}

		op, err := sourceWSResponse.MetadataAsOperation()
		if err == nil && op.Metadata != nil {
			for k, v := range *op.Metadata {
				secrets[k] = v.(string)
			}
		} else {
			// FIXME: This is a backward compatibility codepath
			if err := json.Unmarshal(sourceWSResponse.Metadata, &secrets); err != nil {
				return err
			}
		}

		addresses, err := source.Addresses()
		if err != nil {
			return err
		}

		for _, addr := range addresses {
			sourceWSUrl := "https://" + addr + sourceWSResponse.Operation

			var migration *lxd.Response
			migration, err = dest.MigrateFrom(destName, sourceWSUrl, secrets, status.Architecture, status.Config, status.Devices, status.Profiles, baseImage, ephemeral == 1)
			if err != nil {
				continue
			}

			if err = dest.WaitForSuccess(migration.Operation); err != nil {
				continue
			}

			return nil
		}

		return err
	}
}

func (c *copyCmd) run(config *lxd.Config, args []string) error {
	if len(args) != 2 {
		return errArgs
	}

	ephem := 0
	if c.ephem {
		ephem = 1
	}

	return copyContainer(config, args[0], args[1], false, ephem)
}
<|endoftext|>


<commit_before>// Copyright 2017-2018 Authors of Cilium
//
// 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 utils

import (
	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/policy/api"

	"github.com/sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
)

const (
	// subsysK8s is the value for logfields.LogSubsys
	subsysK8s = "k8s"
	// podPrefixLbl is the value the prefix used in the label selector to
	// represent pods on the default namespace.
	podPrefixLbl = labels.LabelSourceK8sKeyPrefix + k8sConst.PodNamespaceLabel

	// podAnyPrefixLbl is the value of the prefix used in the label selector to
	// represent pods in the default namespace for any source type.
	podAnyPrefixLbl = labels.LabelSourceAnyKeyPrefix + k8sConst.PodNamespaceLabel

	// podInitLbl is the label used in a label selector to match on
	// initializing pods.
	podInitLbl = labels.LabelSourceReservedKeyPrefix + labels.IDNameInit

	// ResourceTypeCiliumNetworkPolicy is the resource type used for the
	// PolicyLabelDerivedFrom label
	ResourceTypeCiliumNetworkPolicy = "CiliumNetworkPolicy"
)

var (
	// log is the k8s package logger object.
	log = logging.DefaultLogger.WithField(logfields.LogSubsys, subsysK8s)
)

// GetPolicyLabels returns a LabelArray for the given namespace and name.
func GetPolicyLabels(ns, name string, uid types.UID, derivedFrom string) labels.LabelArray {
	return labels.LabelArray{
		labels.NewLabel(k8sConst.PolicyLabelName, name, labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelUID, string(uid), labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelNamespace, ns, labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelDerivedFrom, derivedFrom, labels.LabelSourceK8s),
	}
}

// getEndpointSelector converts the provided labelSelector into an EndpointSelector,
// adding the relevant matches for namespaces based on the provided options.
func getEndpointSelector(namespace string, labelSelector *metav1.LabelSelector, addK8sPrefix, matchesInit bool) api.EndpointSelector {
	es := api.NewESFromK8sLabelSelector("", labelSelector)

	// There's no need to prefixed K8s
	// prefix for reserved labels
	if addK8sPrefix && es.HasKeyPrefix(labels.LabelSourceReservedKeyPrefix) {
		return es
	}

	// The user can explicitly specify the namespace in the
	// FromEndpoints selector. If omitted, we limit the
	// scope to the namespace the policy lives in.
	//
	// Policies applying on initializing pods are a special case.
	// Those pods don't have any labels, so they don't have a namespace label either.
	// Don't add a namespace label to those endpoint selectors, or we wouldn't be
	// able to match on those pods.
	if !matchesInit && !es.HasKey(podPrefixLbl) && !es.HasKey(podAnyPrefixLbl) {
		es.AddMatch(podPrefixLbl, namespace)
	}

	return es
}

func parseToCiliumIngressRule(namespace string, inRule, retRule *api.Rule) {
	matchesInit := retRule.EndpointSelector.HasKey(podInitLbl)

	if inRule.Ingress != nil {
		retRule.Ingress = make([]api.IngressRule, len(inRule.Ingress))
		for i, ing := range inRule.Ingress {
			if ing.FromEndpoints != nil {
				retRule.Ingress[i].FromEndpoints = make([]api.EndpointSelector, len(ing.FromEndpoints))
				for j, ep := range ing.FromEndpoints {
					retRule.Ingress[i].FromEndpoints[j] = getEndpointSelector(namespace, ep.LabelSelector, true, matchesInit)
				}
			}

			if ing.ToPorts != nil {
				retRule.Ingress[i].ToPorts = make([]api.PortRule, len(ing.ToPorts))
				copy(retRule.Ingress[i].ToPorts, ing.ToPorts)
			}
			if ing.FromCIDR != nil {
				retRule.Ingress[i].FromCIDR = make([]api.CIDR, len(ing.FromCIDR))
				copy(retRule.Ingress[i].FromCIDR, ing.FromCIDR)
			}

			if ing.FromCIDRSet != nil {
				retRule.Ingress[i].FromCIDRSet = make([]api.CIDRRule, len(ing.FromCIDRSet))
				copy(retRule.Ingress[i].FromCIDRSet, ing.FromCIDRSet)
			}

			if ing.FromRequires != nil {
				retRule.Ingress[i].FromRequires = make([]api.EndpointSelector, len(ing.FromRequires))
				for j, ep := range ing.FromRequires {
					retRule.Ingress[i].FromRequires[j] = getEndpointSelector(namespace, ep.LabelSelector, false, matchesInit)
				}
			}

			if ing.FromEntities != nil {
				retRule.Ingress[i].FromEntities = make([]api.Entity, len(ing.FromEntities))
				copy(retRule.Ingress[i].FromEntities, ing.FromEntities)
			}
		}
	}
}

func parseToCiliumEgressRule(namespace string, inRule, retRule *api.Rule) {
	matchesInit := retRule.EndpointSelector.HasKey(podInitLbl)

	if inRule.Egress != nil {
		retRule.Egress = make([]api.EgressRule, len(inRule.Egress))

		for i, egr := range inRule.Egress {
			if egr.ToEndpoints != nil {
				retRule.Egress[i].ToEndpoints = make([]api.EndpointSelector, len(egr.ToEndpoints))
				for j, ep := range egr.ToEndpoints {
					retRule.Egress[i].ToEndpoints[j] = getEndpointSelector(namespace, ep.LabelSelector, true, matchesInit)
				}
			}

			if egr.ToPorts != nil {
				retRule.Egress[i].ToPorts = make([]api.PortRule, len(egr.ToPorts))
				copy(retRule.Egress[i].ToPorts, egr.ToPorts)
			}
			if egr.ToCIDR != nil {
				retRule.Egress[i].ToCIDR = make([]api.CIDR, len(egr.ToCIDR))
				copy(retRule.Egress[i].ToCIDR, egr.ToCIDR)
			}

			if egr.ToCIDRSet != nil {
				retRule.Egress[i].ToCIDRSet = make(api.CIDRRuleSlice, len(egr.ToCIDRSet))
				copy(retRule.Egress[i].ToCIDRSet, egr.ToCIDRSet)
			}

			if egr.ToRequires != nil {
				retRule.Egress[i].ToRequires = make([]api.EndpointSelector, len(egr.ToRequires))
				for j, ep := range egr.ToRequires {
					retRule.Egress[i].ToRequires[j] = getEndpointSelector(namespace, ep.LabelSelector, false, matchesInit)
				}
			}

			if egr.ToServices != nil {
				retRule.Egress[i].ToServices = make([]api.Service, len(egr.ToServices))
				copy(retRule.Egress[i].ToServices, egr.ToServices)
			}

			if egr.ToEntities != nil {
				retRule.Egress[i].ToEntities = make([]api.Entity, len(egr.ToEntities))
				copy(retRule.Egress[i].ToEntities, egr.ToEntities)
			}

			if egr.ToFQDNs != nil {
				retRule.Egress[i].ToFQDNs = make([]api.FQDNSelector, len(egr.ToFQDNs))
				copy(retRule.Egress[i].ToFQDNs, egr.ToFQDNs)
			}
		}
	}
}

// namespacesAreValid checks the set of namespaces from a rule returns true if
// they are not specified, or if they are specified and match the namespace
// where the rule is being inserted.
func namespacesAreValid(namespace string, userNamespaces []string) bool {
	return len(userNamespaces) == 0 ||
		(len(userNamespaces) == 1 && userNamespaces[0] == namespace)
}

// ParseToCiliumRule returns an api.Rule with all the labels parsed into cilium
// labels.
func ParseToCiliumRule(namespace, name string, uid types.UID, r *api.Rule) *api.Rule {
	retRule := &api.Rule{}
	if r.EndpointSelector.LabelSelector != nil {
		retRule.EndpointSelector = api.NewESFromK8sLabelSelector("", r.EndpointSelector.LabelSelector)
		// The PodSelector should only reflect to the same namespace
		// the policy is being stored, thus we add the namespace to
		// the MatchLabels map.
		//
		// Policies applying on initializing pods are a special case.
		// Those pods don't have any labels, so they don't have a namespace label either.
		// Don't add a namespace label to those endpoint selectors, or we wouldn't be
		// able to match on those pods.
		if !retRule.EndpointSelector.HasKey(podInitLbl) {
			userNamespace, present := r.EndpointSelector.GetMatch(podPrefixLbl)
			if present && !namespacesAreValid(namespace, userNamespace) {
				log.WithFields(logrus.Fields{
					logfields.K8sNamespace:              namespace,
					logfields.CiliumNetworkPolicyName:   name,
					logfields.K8sNamespace + ".illegal": userNamespace,
				}).Warn("CiliumNetworkPolicy contains illegal namespace match in EndpointSelector." +
					" EndpointSelector always applies in namespace of the policy resource, removing illegal namespace match'.")
			}
			retRule.EndpointSelector.AddMatch(podPrefixLbl, namespace)
		}
	}

	parseToCiliumIngressRule(namespace, r, retRule)
	parseToCiliumEgressRule(namespace, r, retRule)

	retRule.Labels = ParseToCiliumLabels(namespace, name, uid, r.Labels)

	retRule.Description = r.Description

	return retRule
}

// ParseToCiliumLabels returns all ruleLbls appended with a specific label that
// represents the given namespace and name along with a label that specifies
// these labels were derived from a CiliumNetworkPolicy.
func ParseToCiliumLabels(namespace, name string, uid types.UID, ruleLbs labels.LabelArray) labels.LabelArray {
	policyLbls := GetPolicyLabels(namespace, name, uid, ResourceTypeCiliumNetworkPolicy)
	return append(policyLbls, ruleLbs...)
}
<commit_msg>CNP: Added ToGroups into parser<commit_after>// Copyright 2017-2018 Authors of Cilium
//
// 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 utils

import (
	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/policy/api"

	"github.com/sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
)

const (
	// subsysK8s is the value for logfields.LogSubsys
	subsysK8s = "k8s"
	// podPrefixLbl is the value the prefix used in the label selector to
	// represent pods on the default namespace.
	podPrefixLbl = labels.LabelSourceK8sKeyPrefix + k8sConst.PodNamespaceLabel

	// podAnyPrefixLbl is the value of the prefix used in the label selector to
	// represent pods in the default namespace for any source type.
	podAnyPrefixLbl = labels.LabelSourceAnyKeyPrefix + k8sConst.PodNamespaceLabel

	// podInitLbl is the label used in a label selector to match on
	// initializing pods.
	podInitLbl = labels.LabelSourceReservedKeyPrefix + labels.IDNameInit

	// ResourceTypeCiliumNetworkPolicy is the resource type used for the
	// PolicyLabelDerivedFrom label
	ResourceTypeCiliumNetworkPolicy = "CiliumNetworkPolicy"
)

var (
	// log is the k8s package logger object.
	log = logging.DefaultLogger.WithField(logfields.LogSubsys, subsysK8s)
)

// GetPolicyLabels returns a LabelArray for the given namespace and name.
func GetPolicyLabels(ns, name string, uid types.UID, derivedFrom string) labels.LabelArray {
	return labels.LabelArray{
		labels.NewLabel(k8sConst.PolicyLabelName, name, labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelUID, string(uid), labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelNamespace, ns, labels.LabelSourceK8s),
		labels.NewLabel(k8sConst.PolicyLabelDerivedFrom, derivedFrom, labels.LabelSourceK8s),
	}
}

// getEndpointSelector converts the provided labelSelector into an EndpointSelector,
// adding the relevant matches for namespaces based on the provided options.
func getEndpointSelector(namespace string, labelSelector *metav1.LabelSelector, addK8sPrefix, matchesInit bool) api.EndpointSelector {
	es := api.NewESFromK8sLabelSelector("", labelSelector)

	// There's no need to prefixed K8s
	// prefix for reserved labels
	if addK8sPrefix && es.HasKeyPrefix(labels.LabelSourceReservedKeyPrefix) {
		return es
	}

	// The user can explicitly specify the namespace in the
	// FromEndpoints selector. If omitted, we limit the
	// scope to the namespace the policy lives in.
	//
	// Policies applying on initializing pods are a special case.
	// Those pods don't have any labels, so they don't have a namespace label either.
	// Don't add a namespace label to those endpoint selectors, or we wouldn't be
	// able to match on those pods.
	if !matchesInit && !es.HasKey(podPrefixLbl) && !es.HasKey(podAnyPrefixLbl) {
		es.AddMatch(podPrefixLbl, namespace)
	}

	return es
}

func parseToCiliumIngressRule(namespace string, inRule, retRule *api.Rule) {
	matchesInit := retRule.EndpointSelector.HasKey(podInitLbl)

	if inRule.Ingress != nil {
		retRule.Ingress = make([]api.IngressRule, len(inRule.Ingress))
		for i, ing := range inRule.Ingress {
			if ing.FromEndpoints != nil {
				retRule.Ingress[i].FromEndpoints = make([]api.EndpointSelector, len(ing.FromEndpoints))
				for j, ep := range ing.FromEndpoints {
					retRule.Ingress[i].FromEndpoints[j] = getEndpointSelector(namespace, ep.LabelSelector, true, matchesInit)
				}
			}

			if ing.ToPorts != nil {
				retRule.Ingress[i].ToPorts = make([]api.PortRule, len(ing.ToPorts))
				copy(retRule.Ingress[i].ToPorts, ing.ToPorts)
			}
			if ing.FromCIDR != nil {
				retRule.Ingress[i].FromCIDR = make([]api.CIDR, len(ing.FromCIDR))
				copy(retRule.Ingress[i].FromCIDR, ing.FromCIDR)
			}

			if ing.FromCIDRSet != nil {
				retRule.Ingress[i].FromCIDRSet = make([]api.CIDRRule, len(ing.FromCIDRSet))
				copy(retRule.Ingress[i].FromCIDRSet, ing.FromCIDRSet)
			}

			if ing.FromRequires != nil {
				retRule.Ingress[i].FromRequires = make([]api.EndpointSelector, len(ing.FromRequires))
				for j, ep := range ing.FromRequires {
					retRule.Ingress[i].FromRequires[j] = getEndpointSelector(namespace, ep.LabelSelector, false, matchesInit)
				}
			}

			if ing.FromEntities != nil {
				retRule.Ingress[i].FromEntities = make([]api.Entity, len(ing.FromEntities))
				copy(retRule.Ingress[i].FromEntities, ing.FromEntities)
			}
		}
	}
}

func parseToCiliumEgressRule(namespace string, inRule, retRule *api.Rule) {
	matchesInit := retRule.EndpointSelector.HasKey(podInitLbl)

	if inRule.Egress != nil {
		retRule.Egress = make([]api.EgressRule, len(inRule.Egress))

		for i, egr := range inRule.Egress {
			if egr.ToEndpoints != nil {
				retRule.Egress[i].ToEndpoints = make([]api.EndpointSelector, len(egr.ToEndpoints))
				for j, ep := range egr.ToEndpoints {
					retRule.Egress[i].ToEndpoints[j] = getEndpointSelector(namespace, ep.LabelSelector, true, matchesInit)
				}
			}

			if egr.ToPorts != nil {
				retRule.Egress[i].ToPorts = make([]api.PortRule, len(egr.ToPorts))
				copy(retRule.Egress[i].ToPorts, egr.ToPorts)
			}
			if egr.ToCIDR != nil {
				retRule.Egress[i].ToCIDR = make([]api.CIDR, len(egr.ToCIDR))
				copy(retRule.Egress[i].ToCIDR, egr.ToCIDR)
			}

			if egr.ToCIDRSet != nil {
				retRule.Egress[i].ToCIDRSet = make(api.CIDRRuleSlice, len(egr.ToCIDRSet))
				copy(retRule.Egress[i].ToCIDRSet, egr.ToCIDRSet)
			}

			if egr.ToRequires != nil {
				retRule.Egress[i].ToRequires = make([]api.EndpointSelector, len(egr.ToRequires))
				for j, ep := range egr.ToRequires {
					retRule.Egress[i].ToRequires[j] = getEndpointSelector(namespace, ep.LabelSelector, false, matchesInit)
				}
			}

			if egr.ToServices != nil {
				retRule.Egress[i].ToServices = make([]api.Service, len(egr.ToServices))
				copy(retRule.Egress[i].ToServices, egr.ToServices)
			}

			if egr.ToEntities != nil {
				retRule.Egress[i].ToEntities = make([]api.Entity, len(egr.ToEntities))
				copy(retRule.Egress[i].ToEntities, egr.ToEntities)
			}

			if egr.ToFQDNs != nil {
				retRule.Egress[i].ToFQDNs = make([]api.FQDNSelector, len(egr.ToFQDNs))
				copy(retRule.Egress[i].ToFQDNs, egr.ToFQDNs)
			}

			if egr.ToGroups != nil {
				retRule.Egress[i].ToGroups = make([]api.ToGroups, len(egr.ToGroups))
				copy(retRule.Egress[i].ToGroups, egr.ToGroups)
			}
		}
	}
}

// namespacesAreValid checks the set of namespaces from a rule returns true if
// they are not specified, or if they are specified and match the namespace
// where the rule is being inserted.
func namespacesAreValid(namespace string, userNamespaces []string) bool {
	return len(userNamespaces) == 0 ||
		(len(userNamespaces) == 1 && userNamespaces[0] == namespace)
}

// ParseToCiliumRule returns an api.Rule with all the labels parsed into cilium
// labels.
func ParseToCiliumRule(namespace, name string, uid types.UID, r *api.Rule) *api.Rule {
	retRule := &api.Rule{}
	if r.EndpointSelector.LabelSelector != nil {
		retRule.EndpointSelector = api.NewESFromK8sLabelSelector("", r.EndpointSelector.LabelSelector)
		// The PodSelector should only reflect to the same namespace
		// the policy is being stored, thus we add the namespace to
		// the MatchLabels map.
		//
		// Policies applying on initializing pods are a special case.
		// Those pods don't have any labels, so they don't have a namespace label either.
		// Don't add a namespace label to those endpoint selectors, or we wouldn't be
		// able to match on those pods.
		if !retRule.EndpointSelector.HasKey(podInitLbl) {
			userNamespace, present := r.EndpointSelector.GetMatch(podPrefixLbl)
			if present && !namespacesAreValid(namespace, userNamespace) {
				log.WithFields(logrus.Fields{
					logfields.K8sNamespace:              namespace,
					logfields.CiliumNetworkPolicyName:   name,
					logfields.K8sNamespace + ".illegal": userNamespace,
				}).Warn("CiliumNetworkPolicy contains illegal namespace match in EndpointSelector." +
					" EndpointSelector always applies in namespace of the policy resource, removing illegal namespace match'.")
			}
			retRule.EndpointSelector.AddMatch(podPrefixLbl, namespace)
		}
	}

	parseToCiliumIngressRule(namespace, r, retRule)
	parseToCiliumEgressRule(namespace, r, retRule)

	retRule.Labels = ParseToCiliumLabels(namespace, name, uid, r.Labels)

	retRule.Description = r.Description

	return retRule
}

// ParseToCiliumLabels returns all ruleLbls appended with a specific label that
// represents the given namespace and name along with a label that specifies
// these labels were derived from a CiliumNetworkPolicy.
func ParseToCiliumLabels(namespace, name string, uid types.UID, ruleLbs labels.LabelArray) labels.LabelArray {
	policyLbls := GetPolicyLabels(namespace, name, uid, ResourceTypeCiliumNetworkPolicy)
	return append(policyLbls, ruleLbs...)
}
<|endoftext|>


<commit_before>/*
(c) Copyright [2021] Hewlett Packard Enterprise Development LP

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 ov
package ov

import (
	"encoding/json"
	"fmt"
	"path"

	"github.com/HewlettPackard/oneview-golang/rest"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/docker/machine/libmachine/log"
)

// RackManager object for OV
type RackManager struct {
	Category                   string        `json:"category,omitempty"`
	Created                    string        `json:"created,omitempty"` // "created": "20150831T154835.250Z",
	ETAG                       string        `json:"eTag,omitempty"`    // "eTag": "1441036118675/8"
	Hostname                   utils.Nstring `json:"hostname,omitempty"`
	Id                         string        `json:"id,omitempty"`               // "43A48BDB-5FAC-42F5-9D1C-A280F48246AD"
	LicensingIntent            string        `json:"licensingIntent,omitempty"`  //OneViewNoiLO"
	Location                   string        `json:"location,omitempty"`         // null
	Model                      string        `json:"model,omitempty"`            // null
	Modified                   string        `json:"modified,omitempty"`         // "modified": "20150902T175611.657Z",
	Name                       string        `json:"name,omitempty"`             //
	PartNumber                 string        `json:"partNumber,omitempty"`       // "affinity": "Bay",
	RefreshState               string        `json:"refreshState,omitempty"`     //Current refresh State of this Server Profile
	RemoteSupportUri           utils.Nstring `json:"remoteSupportUri,omitempty"` // "
	Password                   utils.Nstring `json:"password,omitempty"`
	ScopesUri                  utils.Nstring `json:"scopesUri,omitempty"`                  // "scopesUri":
	SerialNumber               utils.Nstring `json:"serialNumber,omitempty"`               // "serialNumber": "2M25090RMW",
	State                      string        `json:"state,omitempty"`                      // "state": "Normal",
	Status                     string        `json:"status,omitempty"`                     // "status": "Critical",
	SubResources               *SubResource  `json:"subResources,omitempty"`               // "subResources":[]
	SupportDataCollectionState string        `json:"supportDataCollectionState,omitempty"` //supportDataCollectionState
	SupportDataCollectionType  string        `json:"supportDataCollectionType,omitempty"`
	SupportDataCollectionsUri  string        `json:"supportDataCollectionsUri,omitempty"`
	SupportState               string        `json:"supportState,omitempty"`
	Type                       string        `json:"type,omitempty"` // "type": "ServerProfileV4",
	URI                        utils.Nstring `json:"uri,omitempty"`  // "uri": "/rest/server-profiles/9979b3a4-
	UserName                   string        `json:"username,omitempty"`
}

type SubResource struct {
	Chassis               subresourceDat
	Partition             subresourceDat
	Managers              subresourceDat
	RemoteSupportSettings subresourceDat
	FwInventories         subresourceDat
}

type subresourceDat struct {
	Type     string        `json:"type,omitempty"`
	URI      string        `json:"uri,omitempty"`
	Modified string        `json:"modified,omitempty"`
	State    string        `json:"state,omitempty"`
	Etag     string        `json:"etag,omitempty"`
	Count    int           `json:"count,omitempty"`
	Data     []interface{} `json:"data,omitempty"`
}

type RackManagerList struct {
	Type        string        `json:"type,omitempty"`        // "type": "server-hardware-list-3",
	Category    string        `json:"category,omitempty"`    // "category": "server-hardware",
	Count       int           `json:"count,omitempty"`       // "count": 15,
	Created     string        `json:"created,omitempty"`     // "created": "2015-09-08T04:58:21.489Z",
	ETAG        string        `json:"eTag,omitempty"`        // "eTag": "1441688301489",
	Modified    string        `json:"modified,omitempty"`    // "modified": "2015-09-08T04:58:21.489Z",
	NextPageURI utils.Nstring `json:"nextPageUri,omitempty"` // "nextPageUri": null,
	PrevPageURI utils.Nstring `json:"prevPageUri,omitempty"` // "prevPageUri": null,
	Start       int           `json:"start,omitempty"`       // "start": 0,
	Total       int           `json:"total,omitempty"`       // "total": 15,
	URI         string        `json:"uri,omitempty"`         // "uri": "/rest/server-hardware/*/firmware?filter=serverModel='ProLiant DL380 Gen10'"
	Members     []RackManager `json:"members,omitempty"`     // "members":[]
}

// GetRack Manager By Name
func (c *OVClient) GetRackManagerByName(name string) (RackManager, error) {
	var (
		rm RackManager
	)
	rmList, err := c.GetRackManagerList("", "", fmt.Sprintf("name matches '%s'", name), "name:asc", "")
	if rmList.Total > 0 {
		return rmList.Members[0], err
	} else {
		return rm, err
	}
}

func (c *OVClient) GetRackManagerById(Id string) (RackManager, error) {
	var (
		rm  RackManager
		uri = "/rest/rack-managers/" + Id
	)
	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	data, err := c.RestAPICall(rest.GET, uri, nil)
	if err != nil {
		return rm, err
	}
	log.Debugf("GetRackManager %s", data)
	if err := json.Unmarshal([]byte(data), &rm); err != nil {
		return rm, err
	}
	return rm, nil
}

// GetRackMansgers - get all the rack managers
func (c *OVClient) GetRackManagerList(start string, count string, filter string, sort string, scopeUris string) (RackManagerList, error) {
	var (
		uri = "/rest/rack-managers"
		q   map[string]interface{}
		rms RackManagerList
	)
	q = make(map[string]interface{})
	if len(filter) > 0 {
		q["filter"] = filter
	}

	if sort != "" {
		q["sort"] = sort
	}

	if start != "" {
		q["start"] = start
	}

	if count != "" {
		q["count"] = count
	}

	if scopeUris != "" {
		q["scopeUris"] = scopeUris
	}

	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	// Setup query
	if len(q) > 0 {
		c.SetQueryString(q)
	}
	data, err := c.RestAPICall(rest.GET, uri, nil)
	if err != nil {
		return rms, err
	}

	log.Debugf("Get Rack Managers %s", data)
	if err := json.Unmarshal([]byte(data), &rms); err != nil {
		return rms, err
	}
	return rms, nil
}

// Add rack manager- Add  new rack manager
func (c *OVClient) AddRackManager(rm RackManager) (resourceId string, err error) {

	log.Infof("Initializing adding of RackManager %s.", rm.Hostname)
	var (
		uri  = "/rest/rack-managers"
		rmId = ""
		t    *Task
	)

	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())

	t = t.NewProfileTask(c)
	t.ResetTask()
	// log.Infof("REST : %s \n %+v\n", uri, rm)
	log.Debugf("task -> %+v", t)
	data, err := c.RestAPICall(rest.POST, uri, rm)
	if err != nil {
		t.TaskIsDone = true
		log.Errorf("Error submitting new add RackManager request: %s", err)
		return rmId, err
	}

	log.Debugf("Response New RackrManager %s", data)
	if err := json.Unmarshal([]byte(data), &t); err != nil {
		t.TaskIsDone = true
		log.Errorf("Error with task un-marshal: %s", err)
		return rmId, err
	}

	err = t.Wait()
	if err != nil {
		return rmId, err
	}
	rmUri := string(t.AssociatedRes.ResourceURI)
	rmId = path.Base(rmUri)
	return rmId, nil
}

// delete a profile, assign the server and remove the profile from the system
func (c *OVClient) DeleteRackManager(name string) error {
	var (
		rm  RackManager
		err error
		t   *Task
		uri string
	)

	rm, err = c.GetRackManagerByName(name)
	if err != nil {
		return err
	}
	if rm.Name != "" {
		t = t.NewProfileTask(c)
		t.ResetTask()
		log.Debugf("REST : %s \n %+v\n", rm.URI, rm)
		log.Debugf("task -> %+v", t)
		uri = rm.URI.String()
		if uri == "" {
			log.Warn("Unable to post delete, no uri found.")
			t.TaskIsDone = true
			return err
		}
		data, err := c.RestAPICall(rest.DELETE, uri, nil)
		if err != nil {
			log.Errorf("Error submitting delete rack manager request: %s", err)
			t.TaskIsDone = true
			return err
		}

		log.Debugf("Response delete rack manager %s", data)
		if err := json.Unmarshal([]byte(data), &t); err != nil {
			t.TaskIsDone = true
			log.Errorf("Error with task un-marshal: %s", err)
			return err
		}
		err = t.Wait()
		if err != nil {
			return err
		}
		return nil
	} else {
		log.Infof("Rack Manager could not be found to delete, %s, skipping delete ...", name)
	}
	return nil
}
<commit_msg>changes for  rack manager<commit_after>/*
(c) Copyright [2021] Hewlett Packard Enterprise Development LP

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 ov
package ov

import (
	"encoding/json"
	"fmt"
	"path"

	"github.com/HewlettPackard/oneview-golang/rest"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/docker/machine/libmachine/log"
)

// RackManager object for OV
type RackManager struct {
	Category                   string          `json:"category,omitempty"`
	Created                    string          `json:"created,omitempty"` // "created": "20150831T154835.250Z",
	ETAG                       string          `json:"eTag,omitempty"`    // "eTag": "1441036118675/8"
	Force                      bool            `json:"force,omitempty"`
	Hostname                   utils.Nstring   `json:"hostname,omitempty"`
	Id                         string          `json:"id,omitempty"` // "43A48BDB-5FAC-42F5-9D1C-A280F48246AD"
	InitialScopeUris           []utils.Nstring `json:"initialScopeUris,omitempty"`
	LicensingIntent            string          `json:"licensingIntent,omitempty"`  //OneViewNoiLO"
	Location                   string          `json:"location,omitempty"`         // null
	Model                      string          `json:"model,omitempty"`            // null
	Modified                   string          `json:"modified,omitempty"`         // "modified": "20150902T175611.657Z",
	Name                       string          `json:"name,omitempty"`             //
	PartNumber                 string          `json:"partNumber,omitempty"`       // "affinity": "Bay",
	RefreshState               string          `json:"refreshState,omitempty"`     //Current refresh State of this Server Profile
	RemoteSupportUri           utils.Nstring   `json:"remoteSupportUri,omitempty"` // "
	Password                   utils.Nstring   `json:"password,omitempty"`
	ScopesUri                  utils.Nstring   `json:"scopesUri,omitempty"`                  // "scopesUri":
	SerialNumber               utils.Nstring   `json:"serialNumber,omitempty"`               // "serialNumber": "2M25090RMW",
	State                      string          `json:"state,omitempty"`                      // "state": "Normal",
	Status                     string          `json:"status,omitempty"`                     // "status": "Critical",
	SubResources               *SubResource    `json:"subResources,omitempty"`               // "subResources":[]
	SupportDataCollectionState string          `json:"supportDataCollectionState,omitempty"` //supportDataCollectionState
	SupportDataCollectionType  string          `json:"supportDataCollectionType,omitempty"`
	SupportDataCollectionsUri  string          `json:"supportDataCollectionsUri,omitempty"`
	SupportState               string          `json:"supportState,omitempty"`
	Type                       string          `json:"type,omitempty"` // "type": "ServerProfileV4",
	URI                        utils.Nstring   `json:"uri,omitempty"`  // "uri": "/rest/server-profiles/9979b3a4-
	UserName                   string          `json:"username,omitempty"`
}

type SubResource struct {
	Chassis               subresourceDat
	Partition             subresourceDat
	Managers              subresourceDat
	RemoteSupportSettings subresourceDat
	FwInventories         subresourceDat
}

type subresourceDat struct {
	Type     string        `json:"type,omitempty"`
	URI      string        `json:"uri,omitempty"`
	Modified string        `json:"modified,omitempty"`
	State    string        `json:"state,omitempty"`
	Etag     string        `json:"etag,omitempty"`
	Count    int           `json:"count,omitempty"`
	Data     []interface{} `json:"data,omitempty"`
}

type RackManagerList struct {
	Type        string        `json:"type,omitempty"`        // "type": "server-hardware-list-3",
	Category    string        `json:"category,omitempty"`    // "category": "server-hardware",
	Count       int           `json:"count,omitempty"`       // "count": 15,
	Created     string        `json:"created,omitempty"`     // "created": "2015-09-08T04:58:21.489Z",
	ETAG        string        `json:"eTag,omitempty"`        // "eTag": "1441688301489",
	Modified    string        `json:"modified,omitempty"`    // "modified": "2015-09-08T04:58:21.489Z",
	NextPageURI utils.Nstring `json:"nextPageUri,omitempty"` // "nextPageUri": null,
	PrevPageURI utils.Nstring `json:"prevPageUri,omitempty"` // "prevPageUri": null,
	Start       int           `json:"start,omitempty"`       // "start": 0,
	Total       int           `json:"total,omitempty"`       // "total": 15,
	URI         string        `json:"uri,omitempty"`         // "uri": "/rest/server-hardware/*/firmware?filter=serverModel='ProLiant DL380 Gen10'"
	Members     []RackManager `json:"members,omitempty"`     // "members":[]
}

// GetRack Manager By Name
func (c *OVClient) GetRackManagerByName(name string) (RackManager, error) {
	var (
		rm RackManager
	)
	rmList, err := c.GetRackManagerList("", "", fmt.Sprintf("name matches '%s'", name), "name:asc", "")
	if rmList.Total > 0 {
		return rmList.Members[0], err
	} else {
		return rm, err
	}
}

func (c *OVClient) GetRackManagerById(Id string) (RackManager, error) {
	var (
		rm  RackManager
		uri = "/rest/rack-managers/" + Id
	)
	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	data, err := c.RestAPICall(rest.GET, uri, nil)
	if err != nil {
		return rm, err
	}
	log.Debugf("GetRackManager %s", data)
	if err := json.Unmarshal([]byte(data), &rm); err != nil {
		return rm, err
	}
	return rm, nil
}

// GetRackMansgers - get all the rack managers
func (c *OVClient) GetRackManagerList(start string, count string, filter string, sort string, scopeUris string) (RackManagerList, error) {
	var (
		uri = "/rest/rack-managers"
		q   map[string]interface{}
		rms RackManagerList
	)
	q = make(map[string]interface{})
	if len(filter) > 0 {
		q["filter"] = filter
	}

	if sort != "" {
		q["sort"] = sort
	}

	if start != "" {
		q["start"] = start
	}

	if count != "" {
		q["count"] = count
	}

	if scopeUris != "" {
		q["scopeUris"] = scopeUris
	}

	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	// Setup query
	if len(q) > 0 {
		c.SetQueryString(q)
	}
	data, err := c.RestAPICall(rest.GET, uri, nil)
	if err != nil {
		return rms, err
	}

	log.Debugf("Get Rack Managers %s", data)
	if err := json.Unmarshal([]byte(data), &rms); err != nil {
		return rms, err
	}
	return rms, nil
}

// Add rack manager- Add  new rack manager
func (c *OVClient) AddRackManager(rm RackManager) (resourceId string, err error) {

	log.Infof("Initializing adding of RackManager %s.", rm.Hostname)
	var (
		uri  = "/rest/rack-managers"
		rmId = ""
		t    *Task
	)

	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())

	t = t.NewProfileTask(c)
	t.ResetTask()
	// log.Infof("REST : %s \n %+v\n", uri, rm)
	log.Debugf("task -> %+v", t)
	data, err := c.RestAPICall(rest.POST, uri, rm)
	if err != nil {
		t.TaskIsDone = true
		log.Errorf("Error submitting new add RackManager request: %s", err)
		return rmId, err
	}

	log.Debugf("Response New RackrManager %s", data)
	if err := json.Unmarshal([]byte(data), &t); err != nil {
		t.TaskIsDone = true
		log.Errorf("Error with task un-marshal: %s", err)
		return rmId, err
	}

	err = t.Wait()
	if err != nil {
		return rmId, err
	}
	rmUri := string(t.AssociatedRes.ResourceURI)
	rmId = path.Base(rmUri)
	return rmId, nil
}

// delete a profile, assign the server and remove the profile from the system
func (c *OVClient) DeleteRackManager(name string) error {
	var (
		rm  RackManager
		err error
		t   *Task
		uri string
	)

	rm, err = c.GetRackManagerByName(name)
	if err != nil {
		return err
	}
	if rm.Name != "" {
		t = t.NewProfileTask(c)
		t.ResetTask()
		log.Debugf("REST : %s \n %+v\n", rm.URI, rm)
		log.Debugf("task -> %+v", t)
		uri = rm.URI.String()
		if uri == "" {
			log.Warn("Unable to post delete, no uri found.")
			t.TaskIsDone = true
			return err
		}
		data, err := c.RestAPICall(rest.DELETE, uri, nil)
		if err != nil {
			log.Errorf("Error submitting delete rack manager request: %s", err)
			t.TaskIsDone = true
			return err
		}

		log.Debugf("Response delete rack manager %s", data)
		if err := json.Unmarshal([]byte(data), &t); err != nil {
			t.TaskIsDone = true
			log.Errorf("Error with task un-marshal: %s", err)
			return err
		}
		err = t.Wait()
		if err != nil {
			return err
		}
		return nil
	} else {
		log.Infof("Rack Manager could not be found to delete, %s, skipping delete ...", name)
	}
	return nil
}
<|endoftext|>


<commit_before>// +build mage

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"text/template"

	"github.com/magefile/mage/mg"
	"github.com/magefile/mage/sh"
)

const (
	dockerImage      = "jbub/docker-hugo"
	dockerBaseImage  = "alpine:latest"
	dockerMaintainer = "Juraj Bubniak <juraj.bubniak@gmail.com>"
)

type versionInfo struct {
	Name       string
	Image      string
	Version    string
	Maintainer string
}

func (v versionInfo) tag(latest bool) string {
	if latest {
		return fmt.Sprintf("%v:latest", dockerImage)
	}
	return fmt.Sprintf("%v:%v", dockerImage, v.Name)
}

var versions = []versionInfo{
	{Name: "0.28", Version: "0.28", Image: dockerBaseImage, Maintainer: dockerMaintainer},
	{Name: "0.29", Version: "0.29", Image: dockerBaseImage, Maintainer: dockerMaintainer},
	{Name: "0.30", Version: "0.30.2", Image: dockerBaseImage, Maintainer: dockerMaintainer},
	{Name: "0.31", Version: "0.31.1", Image: dockerBaseImage, Maintainer: dockerMaintainer},
	{Name: "0.32", Version: "0.32.4", Image: dockerBaseImage, Maintainer: dockerMaintainer},
	{Name: "0.33", Version: "0.33", Image: dockerBaseImage, Maintainer: dockerMaintainer},
}

var dockerfileTmplString = `FROM {{ .Image }}
MAINTAINER {{ .Maintainer }}

ENV HUGO_VERSION={{ .Version }}

RUN apk --no-cache add wget ca-certificates && \
  cd /tmp && \
  wget https://github.com/spf13/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.tar.gz && \
  tar xzf hugo_${HUGO_VERSION}_Linux-64bit.tar.gz && \
  rm hugo_${HUGO_VERSION}_Linux-64bit.tar.gz && \
  mv hugo /usr/bin/hugo && \
  apk del wget ca-certificates

ENTRYPOINT ["/usr/bin/hugo"]`

var dockerfileTmpl = template.Must(template.New("dockerfile").Parse(dockerfileTmplString))

var docker = sh.RunCmd("docker")

// Generate generates dockerfiles for all versions.
func Generate() error {
	wd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("could not get wd: %v", err)
	}

	for _, info := range versions {
		dir := filepath.Join(wd, info.Name)
		if err := ensureDir(dir); err != nil {
			return err
		}

		if err := genDockerfile(dir, info); err != nil {
			return err
		}
	}

	latest := versions[len(versions)-1]
	if err := genDockerfile(wd, latest); err != nil {
		return err
	}

	return nil
}

// Docker builds and runs docker images for all versions.
func Docker() error {
	mg.Deps(Generate)

	wd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("could not get wd: %v", err)
	}

	for _, info := range versions {
		dir := filepath.Join(wd, info.Name)
		if err := buildAndRunDocker(dir, info, false); err != nil {
			return fmt.Errorf("could not build/run docker: %v", err)
		}
	}

	latest := versions[len(versions)-1]
	if err := buildAndRunDocker(wd, latest, true); err != nil {
		return fmt.Errorf("could not build/run docker: %v", err)
	}

	return nil
}

// Push pushes built docker images do docker hub.
func Push() error {
	mg.Deps(Docker)

	for _, info := range versions {
		if err := pushDocker(info, false); err != nil {
			return fmt.Errorf("could not push docker image: %v", err)
		}
	}

	latest := versions[len(versions)-1]
	if err := pushDocker(latest, true); err != nil {
		return fmt.Errorf("could not push docker image: %v", err)
	}

	return nil
}

func pushDocker(info versionInfo, latest bool) error {
	return docker("push", info.tag(latest))
}

func buildAndRunDocker(dir string, info versionInfo, latest bool) error {
	if err := docker("build", "-t", info.tag(latest), dir); err != nil {
		return err
	}
	if err := docker("run", "--interactive", "--tty", "--rm", info.tag(latest), "version"); err != nil {
		return err
	}
	return nil
}

func genDockerfile(dir string, info versionInfo) error {
	fpath := filepath.Join(dir, "Dockerfile")
	fp, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
	if err != nil {
		return fmt.Errorf("could not open file: %v", err)
	}
	defer fp.Close()

	if err := dockerfileTmpl.Execute(fp, info); err != nil {
		return fmt.Errorf("could not execute template: %v", err)
	}
	return nil
}

func ensureDir(dir string) error {
	if _, err := os.Stat(dir); err != nil {
		if !os.IsNotExist(err) {
			return fmt.Errorf("could not stat path: %v", err)
		}

		if err := os.Mkdir(dir, os.ModePerm); err != nil {
			return fmt.Errorf("could not create dir: %v", err)
		}
	}
	return nil
}
<commit_msg>Use BaseImage instead of Image.<commit_after>// +build mage

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"text/template"

	"github.com/magefile/mage/mg"
	"github.com/magefile/mage/sh"
)

const (
	dockerImage      = "jbub/docker-hugo"
	dockerBaseImage  = "alpine:latest"
	dockerMaintainer = "Juraj Bubniak <juraj.bubniak@gmail.com>"
)

type versionInfo struct {
	Name       string
	BaseImage  string
	Version    string
	Maintainer string
}

func (v versionInfo) tag(latest bool) string {
	if latest {
		return fmt.Sprintf("%v:latest", dockerImage)
	}
	return fmt.Sprintf("%v:%v", dockerImage, v.Name)
}

var versions = []versionInfo{
	{Name: "0.28", Version: "0.28", BaseImage: dockerBaseImage, Maintainer: dockerMaintainer},
	{Name: "0.29", Version: "0.29", BaseImage: dockerBaseImage, Maintainer: dockerMaintainer},
	{Name: "0.30", Version: "0.30.2", BaseImage: dockerBaseImage, Maintainer: dockerMaintainer},
	{Name: "0.31", Version: "0.31.1", BaseImage: dockerBaseImage, Maintainer: dockerMaintainer},
	{Name: "0.32", Version: "0.32.4", BaseImage: dockerBaseImage, Maintainer: dockerMaintainer},
	{Name: "0.33", Version: "0.33", BaseImage: dockerBaseImage, Maintainer: dockerMaintainer},
}

var dockerfileTmplString = `FROM {{ .BaseImage }}
MAINTAINER {{ .Maintainer }}

ENV HUGO_VERSION={{ .Version }}

RUN apk --no-cache add wget ca-certificates && \
  cd /tmp && \
  wget https://github.com/spf13/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.tar.gz && \
  tar xzf hugo_${HUGO_VERSION}_Linux-64bit.tar.gz && \
  rm hugo_${HUGO_VERSION}_Linux-64bit.tar.gz && \
  mv hugo /usr/bin/hugo && \
  apk del wget ca-certificates

ENTRYPOINT ["/usr/bin/hugo"]`

var dockerfileTmpl = template.Must(template.New("dockerfile").Parse(dockerfileTmplString))

var docker = sh.RunCmd("docker")

// Generate generates dockerfiles for all versions.
func Generate() error {
	wd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("could not get wd: %v", err)
	}

	for _, info := range versions {
		dir := filepath.Join(wd, info.Name)
		if err := ensureDir(dir); err != nil {
			return err
		}

		if err := genDockerfile(dir, info); err != nil {
			return err
		}
	}

	latest := versions[len(versions)-1]
	if err := genDockerfile(wd, latest); err != nil {
		return err
	}

	return nil
}

// Docker builds and runs docker images for all versions.
func Docker() error {
	mg.Deps(Generate)

	wd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("could not get wd: %v", err)
	}

	for _, info := range versions {
		dir := filepath.Join(wd, info.Name)
		if err := buildAndRunDocker(dir, info, false); err != nil {
			return fmt.Errorf("could not build/run docker: %v", err)
		}
	}

	latest := versions[len(versions)-1]
	if err := buildAndRunDocker(wd, latest, true); err != nil {
		return fmt.Errorf("could not build/run docker: %v", err)
	}

	return nil
}

// Push pushes built docker images do docker hub.
func Push() error {
	mg.Deps(Docker)

	for _, info := range versions {
		if err := pushDocker(info, false); err != nil {
			return fmt.Errorf("could not push docker image: %v", err)
		}
	}

	latest := versions[len(versions)-1]
	if err := pushDocker(latest, true); err != nil {
		return fmt.Errorf("could not push docker image: %v", err)
	}

	return nil
}

func pushDocker(info versionInfo, latest bool) error {
	return docker("push", info.tag(latest))
}

func buildAndRunDocker(dir string, info versionInfo, latest bool) error {
	if err := docker("build", "-t", info.tag(latest), dir); err != nil {
		return err
	}
	if err := docker("run", "--interactive", "--tty", "--rm", info.tag(latest), "version"); err != nil {
		return err
	}
	return nil
}

func genDockerfile(dir string, info versionInfo) error {
	fpath := filepath.Join(dir, "Dockerfile")
	fp, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
	if err != nil {
		return fmt.Errorf("could not open file: %v", err)
	}
	defer fp.Close()

	if err := dockerfileTmpl.Execute(fp, info); err != nil {
		return fmt.Errorf("could not execute template: %v", err)
	}
	return nil
}

func ensureDir(dir string) error {
	if _, err := os.Stat(dir); err != nil {
		if !os.IsNotExist(err) {
			return fmt.Errorf("could not stat path: %v", err)
		}

		if err := os.Mkdir(dir, os.ModePerm); err != nil {
			return fmt.Errorf("could not create dir: %v", err)
		}
	}
	return nil
}
<|endoftext|>