<commit_before>/*
© Copyright IBM Corporation 2018

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 logger provides utility functions for logging purposes
package logger

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/user"
	"sync"
	"time"
)

// timestampFormat matches the format used by MQ messages (includes milliseconds)
const timestampFormat string = "2006-01-02T15:04:05.000Z07:00"
const debugLevel string = "DEBUG"
const infoLevel string = "INFO"
const errorLevel string = "ERROR"

// A Logger is used to log messages to stdout
type Logger struct {
	mutex       sync.Mutex
	writer      io.Writer
	debug       bool
	json        bool
	processName string
	pid         int
	serverName  string
	host        string
	user        *user.User
}

// NewLogger creates a new logger
func NewLogger(writer io.Writer, debug bool, json bool, serverName string) (*Logger, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, err
	}
	user, err := user.Current()
	if err != nil {
		return nil, err
	}
	return &Logger{
		mutex:       sync.Mutex{},
		writer:      writer,
		debug:       debug,
		json:        json,
		processName: os.Args[0],
		pid:         os.Getpid(),
		serverName:  serverName,
		host:        hostname,
		user:        user,
	}, nil
}

func (l *Logger) format(entry map[string]interface{}) (string, error) {
	if l.json {
		b, err := json.Marshal(entry)
		if err != nil {
			return "", err
		}
		return string(b), err
	}
	return fmt.Sprintf("%v %v\n", entry["ibm_datetime"], entry["message"]), nil
}

// log logs a message at the specified level.  The message is enriched with
// additional fields.
func (l *Logger) log(level string, msg string) {
	t := time.Now()
	entry := map[string]interface{}{
		"message":         fmt.Sprint(msg),
		"ibm_datetime":    t.Format(timestampFormat),
		"loglevel":        level,
		"host":            l.host,
		"ibm_serverName":  l.serverName,
		"ibm_processName": l.processName,
		"ibm_processId":   l.pid,
		"ibm_userName":    l.user.Username,
		"type":            "mq_containerlog",
	}
	s, err := l.format(entry)
	l.mutex.Lock()
	if err != nil {
		// TODO: Fix this
		fmt.Println(err)
	}
	if l.json {
		fmt.Fprintln(l.writer, s)
	} else {
		fmt.Fprint(l.writer, s)
	}
	l.mutex.Unlock()
}

// LogDirect logs a message directly to stdout
func (l *Logger) LogDirect(msg string) {
	fmt.Println(msg)
}

// Debug logs a line as debug
func (l *Logger) Debug(args ...interface{}) {
	if l.debug {
		l.log(debugLevel, fmt.Sprint(args...))
	}
}

// Debugf logs a line as debug using format specifiers
func (l *Logger) Debugf(format string, args ...interface{}) {
	if l.debug {
		l.log(debugLevel, fmt.Sprintf(format, args...))
	}
}

// Print logs a message as info
func (l *Logger) Print(args ...interface{}) {
	l.log(infoLevel, fmt.Sprint(args...))
}

// Println logs a message
func (l *Logger) Println(args ...interface{}) {
	l.Print(args...)
}

// Printf logs a message as info using format specifiers
func (l *Logger) Printf(format string, args ...interface{}) {
	l.log(infoLevel, fmt.Sprintf(format, args...))
}

// PrintString logs a string as info
func (l *Logger) PrintString(msg string) {
	l.log(infoLevel, msg)
}

// Errorf logs a message as error
func (l *Logger) Error(args ...interface{}) {
	l.log(errorLevel, fmt.Sprint(args...))
}

// Errorf logs a message as error using format specifiers
func (l *Logger) Errorf(format string, args ...interface{}) {
	l.log(errorLevel, fmt.Sprintf(format, args...))
}

// Fatalf logs a message as fatal using format specifiers
// TODO: Remove this
func (l *Logger) Fatalf(format string, args ...interface{}) {
	l.log("FATAL", fmt.Sprintf(format, args...))
}
<commit_msg>Use string value for pid in JSON log (#110)<commit_after>/*
© Copyright IBM Corporation 2018

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 logger provides utility functions for logging purposes
package logger

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/user"
	"strconv"
	"sync"
	"time"
)

// timestampFormat matches the format used by MQ messages (includes milliseconds)
const timestampFormat string = "2006-01-02T15:04:05.000Z07:00"
const debugLevel string = "DEBUG"
const infoLevel string = "INFO"
const errorLevel string = "ERROR"

// A Logger is used to log messages to stdout
type Logger struct {
	mutex       sync.Mutex
	writer      io.Writer
	debug       bool
	json        bool
	processName string
	pid         string
	serverName  string
	host        string
	user        *user.User
}

// NewLogger creates a new logger
func NewLogger(writer io.Writer, debug bool, json bool, serverName string) (*Logger, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, err
	}
	user, err := user.Current()
	if err != nil {
		return nil, err
	}
	return &Logger{
		mutex:       sync.Mutex{},
		writer:      writer,
		debug:       debug,
		json:        json,
		processName: os.Args[0],
		pid:         strconv.Itoa(os.Getpid()),
		serverName:  serverName,
		host:        hostname,
		user:        user,
	}, nil
}

func (l *Logger) format(entry map[string]interface{}) (string, error) {
	if l.json {
		b, err := json.Marshal(entry)
		if err != nil {
			return "", err
		}
		return string(b), err
	}
	return fmt.Sprintf("%v %v\n", entry["ibm_datetime"], entry["message"]), nil
}

// log logs a message at the specified level.  The message is enriched with
// additional fields.
func (l *Logger) log(level string, msg string) {
	t := time.Now()
	entry := map[string]interface{}{
		"message":         fmt.Sprint(msg),
		"ibm_datetime":    t.Format(timestampFormat),
		"loglevel":        level,
		"host":            l.host,
		"ibm_serverName":  l.serverName,
		"ibm_processName": l.processName,
		"ibm_processId":   l.pid,
		"ibm_userName":    l.user.Username,
		"type":            "mq_containerlog",
	}
	s, err := l.format(entry)
	l.mutex.Lock()
	if err != nil {
		// TODO: Fix this
		fmt.Println(err)
	}
	if l.json {
		fmt.Fprintln(l.writer, s)
	} else {
		fmt.Fprint(l.writer, s)
	}
	l.mutex.Unlock()
}

// LogDirect logs a message directly to stdout
func (l *Logger) LogDirect(msg string) {
	fmt.Println(msg)
}

// Debug logs a line as debug
func (l *Logger) Debug(args ...interface{}) {
	if l.debug {
		l.log(debugLevel, fmt.Sprint(args...))
	}
}

// Debugf logs a line as debug using format specifiers
func (l *Logger) Debugf(format string, args ...interface{}) {
	if l.debug {
		l.log(debugLevel, fmt.Sprintf(format, args...))
	}
}

// Print logs a message as info
func (l *Logger) Print(args ...interface{}) {
	l.log(infoLevel, fmt.Sprint(args...))
}

// Println logs a message
func (l *Logger) Println(args ...interface{}) {
	l.Print(args...)
}

// Printf logs a message as info using format specifiers
func (l *Logger) Printf(format string, args ...interface{}) {
	l.log(infoLevel, fmt.Sprintf(format, args...))
}

// PrintString logs a string as info
func (l *Logger) PrintString(msg string) {
	l.log(infoLevel, msg)
}

// Errorf logs a message as error
func (l *Logger) Error(args ...interface{}) {
	l.log(errorLevel, fmt.Sprint(args...))
}

// Errorf logs a message as error using format specifiers
func (l *Logger) Errorf(format string, args ...interface{}) {
	l.log(errorLevel, fmt.Sprintf(format, args...))
}

// Fatalf logs a message as fatal using format specifiers
// TODO: Remove this
func (l *Logger) Fatalf(format string, args ...interface{}) {
	l.log("FATAL", fmt.Sprintf(format, args...))
}
<|endoftext|>


<commit_before>package loraserver

import (
	"fmt"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/jmoiron/sqlx"
	// register postgresql driver
	_ "github.com/lib/pq"
)

const (
	redisMaxIdle        = 3
	redisIdleTimeoutSec = 240
)

// OpenDatabase opens the database and performs a ping to make sure the
// database is up.
func OpenDatabase(dsn string) (*sqlx.DB, error) {
	db, err := sqlx.Open("postgres", dsn)
	if err != nil {
		return nil, fmt.Errorf("database connection error: %s", err)
	}
	if err := db.Ping(); err != nil {
		return nil, fmt.Errorf("ping database error: %s", err)
	}
	return db, nil
}

// NewRedisPool returns a new Redis connection pool.
func NewRedisPool(redisURL string) *redis.Pool {
	return &redis.Pool{
		MaxIdle:     redisMaxIdle,
		IdleTimeout: redisIdleTimeoutSec * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.DialURL(redisURL)
			if err != nil {
				return nil, fmt.Errorf("redis connection error: %s", err)
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			if err != nil {
				return fmt.Errorf("ping redis error: %s")
			}
			return nil
		},
	}
}
<commit_msg>Fix missing err argument.<commit_after>package loraserver

import (
	"fmt"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/jmoiron/sqlx"
	// register postgresql driver
	_ "github.com/lib/pq"
)

const (
	redisMaxIdle        = 3
	redisIdleTimeoutSec = 240
)

// OpenDatabase opens the database and performs a ping to make sure the
// database is up.
func OpenDatabase(dsn string) (*sqlx.DB, error) {
	db, err := sqlx.Open("postgres", dsn)
	if err != nil {
		return nil, fmt.Errorf("database connection error: %s", err)
	}
	if err := db.Ping(); err != nil {
		return nil, fmt.Errorf("ping database error: %s", err)
	}
	return db, nil
}

// NewRedisPool returns a new Redis connection pool.
func NewRedisPool(redisURL string) *redis.Pool {
	return &redis.Pool{
		MaxIdle:     redisMaxIdle,
		IdleTimeout: redisIdleTimeoutSec * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.DialURL(redisURL)
			if err != nil {
				return nil, fmt.Errorf("redis connection error: %s", err)
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			if err != nil {
				return fmt.Errorf("ping redis error: %s", err)
			}
			return nil
		},
	}
}
<|endoftext|>


<commit_before>package library

import (
	"github.com/nytlabs/streamtools/st/blocks" // blocks
	"github.com/nytlabs/streamtools/st/util"
	"time"
)

// specify those channels we're going to use to communicate with streamtools
type Ticker struct {
	blocks.Block
	queryrule chan chan interface{}
	inrule    chan interface{}
	out       chan interface{}
	quit      chan interface{}
}

// we need to build a simple factory so that streamtools can make new blocks of this kind
func NewTicker() blocks.BlockInterface {
	return &Ticker{}
}

// Setup is called once before running the block. We build up the channels and specify what kind of block this is.
func (b *Ticker) Setup() {
	b.Kind = "Ticker"
	b.inrule = b.InRoute("rule")
	b.queryrule = b.QueryRoute("rule")
	b.quit = b.Quit()
	b.out = b.Broadcast()
}

// Run is the block's main loop. Here we listen on the different channels we set up.
func (b *Ticker) Run() {
	interval := time.Duration(1) * time.Second
	ticker := time.NewTicker(interval)
	for {
		select {
		case tick := <-ticker.C:
			b.out <- map[string]interface{}{
				"tick": tick,
			}
		case ruleI := <-b.inrule:
			// set a parameter of the block
			intervalS, err := util.ParseString(ruleI, "Interval")
			if err != nil {
				b.Error("bad input")
				break
			}

			dur, err := time.ParseDuration(intervalS)
			if err != nil {
				b.Error(err)
				break
			}

			interval = dur
			ticker.Stop()
			ticker = time.NewTicker(interval)
		case <-b.quit:
			return
		case c := <-b.queryrule:
			// deal with a query request
			c <- map[string]interface{}{
				"Interval": interval.String(),
			}
		}
	}
}
<commit_msg>ticker fix for negatrive values<commit_after>package library

import (
	"github.com/nytlabs/streamtools/st/blocks" // blocks
	"github.com/nytlabs/streamtools/st/util"
	"time"
)

// specify those channels we're going to use to communicate with streamtools
type Ticker struct {
	blocks.Block
	queryrule chan chan interface{}
	inrule    chan interface{}
	out       chan interface{}
	quit      chan interface{}
}

// we need to build a simple factory so that streamtools can make new blocks of this kind
func NewTicker() blocks.BlockInterface {
	return &Ticker{}
}

// Setup is called once before running the block. We build up the channels and specify what kind of block this is.
func (b *Ticker) Setup() {
	b.Kind = "Ticker"
	b.inrule = b.InRoute("rule")
	b.queryrule = b.QueryRoute("rule")
	b.quit = b.Quit()
	b.out = b.Broadcast()
}

// Run is the block's main loop. Here we listen on the different channels we set up.
func (b *Ticker) Run() {
	interval := time.Duration(1) * time.Second
	ticker := time.NewTicker(interval)
	for {
		select {
		case tick := <-ticker.C:
			b.out <- map[string]interface{}{
				"tick": tick,
			}
		case ruleI := <-b.inrule:
			// set a parameter of the block
			intervalS, err := util.ParseString(ruleI, "Interval")
			if err != nil {
				b.Error("bad input")
				break
			}

			dur, err := time.ParseDuration(intervalS)
			if err != nil {
				b.Error(err)
				break
			}

			if dur < 0 {
				b.Error("cannot assign negative interval to ticker")
				break
			}

			interval = dur
			ticker.Stop()
			ticker = time.NewTicker(interval)
		case <-b.quit:
			return
		case c := <-b.queryrule:
			// deal with a query request
			c <- map[string]interface{}{
				"Interval": interval.String(),
			}
		}
	}
}
<|endoftext|>


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

package resource

import (
	"errors"
	"io"
	"net"
	"net/http"
	"time"

	"github.com/coreos/ignition/config/types"
	"github.com/coreos/ignition/internal/log"
	"github.com/coreos/ignition/internal/version"

	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"
)

const (
	initialBackoff = 100 * time.Millisecond
	maxBackoff     = 5 * time.Second

	defaultHttpResponseHeaderTimeout = 10
	defaultHttpTotalTimeout          = 0
)

var (
	ErrTimeout = errors.New("unable to fetch resource in time")
)

// HttpClient is a simple wrapper around the Go HTTP client that standardizes
// the process and logging of fetching payloads.
type HttpClient struct {
	client  *http.Client
	logger  *log.Logger
	timeout time.Duration
}

// NewHttpClient creates a new client with the given logger and timeouts.
func NewHttpClient(logger *log.Logger, timeouts types.Timeouts) HttpClient {
	responseHeader := defaultHttpResponseHeaderTimeout
	total := defaultHttpTotalTimeout
	if timeouts.HTTPResponseHeaders != nil {
		responseHeader = *timeouts.HTTPResponseHeaders
	}
	if timeouts.HTTPTotal != nil {
		total = *timeouts.HTTPTotal
	}
	return HttpClient{
		client: &http.Client{
			Transport: &http.Transport{
				ResponseHeaderTimeout: time.Duration(responseHeader) * time.Second,
				Dial: (&net.Dialer{
					Timeout:   30 * time.Second,
					KeepAlive: 30 * time.Second,
				}).Dial,
				TLSHandshakeTimeout: 10 * time.Second,
			},
		},
		logger:  logger,
		timeout: time.Duration(total) * time.Second,
	}
}

// getReaderWithHeader performs an HTTP GET on the provided URL with the provided request header
// and returns the response body Reader, HTTP status code, and error (if any). By
// default, User-Agent is added to the header but this can be overridden.
func (c HttpClient) getReaderWithHeader(url string, header http.Header) (io.ReadCloser, int, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, 0, err
	}

	req.Header.Set("User-Agent", "Ignition/"+version.Raw)

	for key, values := range header {
		req.Header.Del(key)
		for _, value := range values {
			req.Header.Add(key, value)
		}
	}

	ctx := context.Background()
	if c.timeout != 0 {
		ctx, _ = context.WithTimeout(ctx, c.timeout)
	}

	duration := initialBackoff
	for attempt := 1; ; attempt++ {
		c.logger.Debug("GET %s: attempt #%d", url, attempt)
		resp, err := ctxhttp.Do(ctx, c.client, req)

		if err == nil {
			c.logger.Debug("GET result: %s", http.StatusText(resp.StatusCode))
			if resp.StatusCode < 500 {
				return resp.Body, resp.StatusCode, nil
			}
			resp.Body.Close()
		} else {
			c.logger.Debug("GET error: %v", err)
		}

		duration = duration * 2
		if duration > maxBackoff {
			duration = maxBackoff
		}

		// Wait before next attempt or exit if we timeout while waiting
		select {
		case <-time.After(duration):
		case <-ctx.Done():
			return nil, 0, ErrTimeout
		}
	}
}
<commit_msg>resource/http: prefer the go resolver over libc<commit_after>// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package resource

import (
	"errors"
	"io"
	"net"
	"net/http"
	"time"

	"github.com/coreos/ignition/config/types"
	"github.com/coreos/ignition/internal/log"
	"github.com/coreos/ignition/internal/version"

	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"
)

const (
	initialBackoff = 100 * time.Millisecond
	maxBackoff     = 5 * time.Second

	defaultHttpResponseHeaderTimeout = 10
	defaultHttpTotalTimeout          = 0
)

var (
	ErrTimeout = errors.New("unable to fetch resource in time")
)

// HttpClient is a simple wrapper around the Go HTTP client that standardizes
// the process and logging of fetching payloads.
type HttpClient struct {
	client  *http.Client
	logger  *log.Logger
	timeout time.Duration
}

// NewHttpClient creates a new client with the given logger and timeouts.
func NewHttpClient(logger *log.Logger, timeouts types.Timeouts) HttpClient {
	responseHeader := defaultHttpResponseHeaderTimeout
	total := defaultHttpTotalTimeout
	if timeouts.HTTPResponseHeaders != nil {
		responseHeader = *timeouts.HTTPResponseHeaders
	}
	if timeouts.HTTPTotal != nil {
		total = *timeouts.HTTPTotal
	}
	return HttpClient{
		client: &http.Client{
			Transport: &http.Transport{
				ResponseHeaderTimeout: time.Duration(responseHeader) * time.Second,
				Dial: (&net.Dialer{
					Timeout:   30 * time.Second,
					KeepAlive: 30 * time.Second,
					Resolver: &net.Resolver{
						PreferGo: true,
					},
				}).Dial,
				TLSHandshakeTimeout: 10 * time.Second,
			},
		},
		logger:  logger,
		timeout: time.Duration(total) * time.Second,
	}
}

// getReaderWithHeader performs an HTTP GET on the provided URL with the provided request header
// and returns the response body Reader, HTTP status code, and error (if any). By
// default, User-Agent is added to the header but this can be overridden.
func (c HttpClient) getReaderWithHeader(url string, header http.Header) (io.ReadCloser, int, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, 0, err
	}

	req.Header.Set("User-Agent", "Ignition/"+version.Raw)

	for key, values := range header {
		req.Header.Del(key)
		for _, value := range values {
			req.Header.Add(key, value)
		}
	}

	ctx := context.Background()
	if c.timeout != 0 {
		ctx, _ = context.WithTimeout(ctx, c.timeout)
	}

	duration := initialBackoff
	for attempt := 1; ; attempt++ {
		c.logger.Debug("GET %s: attempt #%d", url, attempt)
		resp, err := ctxhttp.Do(ctx, c.client, req)

		if err == nil {
			c.logger.Debug("GET result: %s", http.StatusText(resp.StatusCode))
			if resp.StatusCode < 500 {
				return resp.Body, resp.StatusCode, nil
			}
			resp.Body.Close()
		} else {
			c.logger.Debug("GET error: %v", err)
		}

		duration = duration * 2
		if duration > maxBackoff {
			duration = maxBackoff
		}

		// Wait before next attempt or exit if we timeout while waiting
		select {
		case <-time.After(duration):
		case <-ctx.Done():
			return nil, 0, ErrTimeout
		}
	}
}
<|endoftext|>


<commit_before>package slack

import (
	"fmt"
	"log"
	"net/url"
	"os"
)

const team = "monkeytacos"

var apiToken string

func init() {
	apiToken = os.Getenv("SLACK_API_TOKEN")
	if apiToken == "" {
		log.Fatal("SLACK_API_TOKEN not set")
	}
}

type channelResponse struct {
	Channel  Channel   `json:"channel"`
	Channels []Channel `json:"channels"`
	Ok       bool      `json:"ok"`
	Err      string    `json:"error"`
}

// A Channel contains the name, id, and member list of a Slack channel
// (e.g. #general).
type Channel struct {
	ID      string   `json:"id"`
	Name    string   `json:"name"`
	Members []string `json:"members"`
	Team    string   // set in NewChannel
}

// NewChannel takes a channel name (e.g. "general" for #general) and returns
// a Channel with ID and member list populated from the Slack API.
func NewChannel(name string) (Channel, error) {
	var emptyChannel Channel

	qsp := &url.Values{}
	qsp.Set("channel", name)
	listURL := NewURL("channels.list", qsp)

	cr := channelResponse{}
	err := apiCall(listURL, &cr)
	if err != nil {
		return emptyChannel, APIError{err.Error()}
	}

	if cr.Ok != true {
		return emptyChannel, APIError{cr.Err}
	}

	for _, ch := range cr.Channels {
		if ch.Name == name {
			ch.Team = team
			return ch, nil
		}
	}

	return emptyChannel, fmt.Errorf("no channel named %q on team %q", name, team)
}

// String returns a human-readable string representation of a Channel.
func (ch Channel) String() string {
	return fmt.Sprintf("%#v", ch)
}

// UpdateMembers updates a Channel's member list through the Slack API.
func (ch *Channel) UpdateMembers() error {
	qsp := &url.Values{}
	qsp.Set("channel", ch.ID)
	channelURL := NewURL("channels.info", qsp)

	cr := channelResponse{}
	err := apiCall(channelURL, &cr)
	if err != nil {
		return APIError{err.Error()}
	}

	if !cr.Ok {
		return APIError{cr.Err}
	}

	ch.Members = cr.Channel.Members
	return nil
}
<commit_msg>Change team name to Omaze<commit_after>package slack

import (
	"fmt"
	"log"
	"net/url"
	"os"
)

const team = "omaze"

var apiToken string

func init() {
	apiToken = os.Getenv("SLACK_API_TOKEN")
	if apiToken == "" {
		log.Fatal("SLACK_API_TOKEN not set")
	}
}

type channelResponse struct {
	Channel  Channel   `json:"channel"`
	Channels []Channel `json:"channels"`
	Ok       bool      `json:"ok"`
	Err      string    `json:"error"`
}

// A Channel contains the name, id, and member list of a Slack channel
// (e.g. #general).
type Channel struct {
	ID      string   `json:"id"`
	Name    string   `json:"name"`
	Members []string `json:"members"`
	Team    string   // set in NewChannel
}

// NewChannel takes a channel name (e.g. "general" for #general) and returns
// a Channel with ID and member list populated from the Slack API.
func NewChannel(name string) (Channel, error) {
	var emptyChannel Channel

	qsp := &url.Values{}
	qsp.Set("channel", name)
	listURL := NewURL("channels.list", qsp)

	cr := channelResponse{}
	err := apiCall(listURL, &cr)
	if err != nil {
		return emptyChannel, APIError{err.Error()}
	}

	if cr.Ok != true {
		return emptyChannel, APIError{cr.Err}
	}

	for _, ch := range cr.Channels {
		if ch.Name == name {
			ch.Team = team
			return ch, nil
		}
	}

	return emptyChannel, fmt.Errorf("no channel named %q on team %q", name, team)
}

// String returns a human-readable string representation of a Channel.
func (ch Channel) String() string {
	return fmt.Sprintf("%#v", ch)
}

// UpdateMembers updates a Channel's member list through the Slack API.
func (ch *Channel) UpdateMembers() error {
	qsp := &url.Values{}
	qsp.Set("channel", ch.ID)
	channelURL := NewURL("channels.info", qsp)

	cr := channelResponse{}
	err := apiCall(channelURL, &cr)
	if err != nil {
		return APIError{err.Error()}
	}

	if !cr.Ok {
		return APIError{cr.Err}
	}

	ch.Members = cr.Channel.Members
	return nil
}
<|endoftext|>


<commit_before>package source

import (
	"bytes"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"runtime"

	"github.com/pkg/errors"
)

const baseStackIndex = 1

// GetCondition returns the condition string by reading it from the file
// identified in the callstack. In golang 1.9 the line number changed from
// being the line where the statement ended to the line where the statement began.
func GetCondition(stackIndex int, argPos int) (string, error) {
	_, filename, lineNum, ok := runtime.Caller(baseStackIndex + stackIndex)
	if !ok {
		return "", errors.New("failed to get caller info")
	}

	node, err := getNodeAtLine(filename, lineNum)
	if err != nil {
		return "", err
	}
	return getArgSourceFromAST(node, argPos)
}

func getNodeAtLine(filename string, lineNum int) (ast.Node, error) {
	fileset := token.NewFileSet()
	astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to parse source file: %s", filename)
	}

	node := scanToLine(fileset, astFile, lineNum)
	if node == nil {
		return nil, errors.Wrapf(err,
			"failed to find an expression on line %d in %s", lineNum, filename)
	}
	return node, nil
}

func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node {
	v := &scanToLineVisitor{lineNum: lineNum, fileset: fileset}
	ast.Walk(v, node)
	return v.matchedNode
}

type scanToLineVisitor struct {
	lineNum     int
	matchedNode ast.Node
	fileset     *token.FileSet
}

func (v *scanToLineVisitor) Visit(node ast.Node) ast.Visitor {
	if node == nil || v.matchedNode != nil {
		return nil
	}

	var position token.Position
	switch {
	case runtime.Version() < "go1.9":
		position = v.fileset.Position(node.End())
	default:
		position = v.fileset.Position(node.Pos())
	}

	if position.Line == v.lineNum {
		v.matchedNode = node
		return nil
	}
	return v
}

func getArgSourceFromAST(node ast.Node, argPos int) (string, error) {
	visitor := &callExprVisitor{}
	ast.Walk(visitor, node)
	if visitor.expr == nil {
		return "", errors.Errorf("unexpected ast")
	}

	buf := new(bytes.Buffer)
	err := format.Node(buf, token.NewFileSet(), visitor.expr.Args[argPos])
	return buf.String(), err
}

type callExprVisitor struct {
	expr *ast.CallExpr
}

func (v *callExprVisitor) Visit(node ast.Node) ast.Visitor {
	switch typed := node.(type) {
	case nil:
		return nil
	case *ast.IfStmt:
		ast.Walk(v, typed.Cond)
	case *ast.CallExpr:
		v.expr = typed
	}

	if v.expr != nil {
		return nil
	}
	return v
}
<commit_msg>Compare golang version as int, not string<commit_after>package source

import (
	"bytes"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"runtime"
	"strconv"
	"strings"

	"github.com/pkg/errors"
)

const baseStackIndex = 1

// GetCondition returns the condition string by reading it from the file
// identified in the callstack. In golang 1.9 the line number changed from
// being the line where the statement ended to the line where the statement began.
func GetCondition(stackIndex int, argPos int) (string, error) {
	_, filename, lineNum, ok := runtime.Caller(baseStackIndex + stackIndex)
	if !ok {
		return "", errors.New("failed to get caller info")
	}

	node, err := getNodeAtLine(filename, lineNum)
	if err != nil {
		return "", err
	}
	return getArgSourceFromAST(node, argPos)
}

func getNodeAtLine(filename string, lineNum int) (ast.Node, error) {
	fileset := token.NewFileSet()
	astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to parse source file: %s", filename)
	}

	node := scanToLine(fileset, astFile, lineNum)
	if node == nil {
		return nil, errors.Errorf(
			"failed to find an expression on line %d in %s", lineNum, filename)
	}
	return node, nil
}

func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node {
	v := &scanToLineVisitor{lineNum: lineNum, fileset: fileset}
	ast.Walk(v, node)
	return v.matchedNode
}

type scanToLineVisitor struct {
	lineNum     int
	matchedNode ast.Node
	fileset     *token.FileSet
}

func (v *scanToLineVisitor) Visit(node ast.Node) ast.Visitor {
	if node == nil || v.matchedNode != nil {
		return nil
	}
	if v.nodePosition(node).Line == v.lineNum {
		v.matchedNode = node
		return nil
	}
	return v
}

func (v *scanToLineVisitor) nodePosition(node ast.Node) token.Position {
	if isGOVersionBefore19() {
		return v.fileset.Position(node.End())
	}
	return v.fileset.Position(node.Pos())
}

func isGOVersionBefore19() bool {
	version := runtime.Version()
	// not a release version
	if !strings.HasPrefix(version, "go") {
		return false
	}
	version = strings.TrimPrefix(version, "go")
	parts := strings.Split(version, ".")
	if len(parts) < 2 {
		return false
	}
	minor, err := strconv.ParseInt(parts[1], 10, 32)
	return err == nil && parts[0] == "1" && minor < 9
}

func getArgSourceFromAST(node ast.Node, argPos int) (string, error) {
	visitor := &callExprVisitor{}
	ast.Walk(visitor, node)
	if visitor.expr == nil {
		return "", errors.New("unexpected ast")
	}

	buf := new(bytes.Buffer)
	err := format.Node(buf, token.NewFileSet(), visitor.expr.Args[argPos])
	return buf.String(), err
}

type callExprVisitor struct {
	expr *ast.CallExpr
}

func (v *callExprVisitor) Visit(node ast.Node) ast.Visitor {
	if v.expr != nil || node == nil {
		return nil
	}
	switch typed := node.(type) {
	case *ast.CallExpr:
		v.expr = typed
		return nil
	default:
		return v
	}
}
<|endoftext|>


<commit_before>package kontrol

import (
	"database/sql"
	"errors"
	"fmt"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/hashicorp/go-version"
	_ "github.com/lib/pq"

	"github.com/koding/kite"
	kontrolprotocol "github.com/koding/kite/kontrol/protocol"
	"github.com/koding/kite/protocol"
)

// Postgres holds Postgresql database related configuration
type PostgresConfig struct {
	Host     string
	Port     int
	Username string
	Password string
	DBName   string
}

type Postgres struct {
	DB  *sql.DB
	Log kite.Logger
}

func NewPostgres(conf *PostgresConfig, log kite.Logger) *Postgres {
	if conf == nil {
		conf = &PostgresConfig{}
	}

	if conf.Port == 0 {
		conf.Port = 5432
	}

	if conf.Host == "" {
		conf.Host = "localhost"
	}

	if conf.DBName == "" {
		conf.DBName = os.Getenv("KONTROL_POSTGRES_DBNAME")
		if conf.DBName == "" {
			panic("db name is not set for postgres kontrol storage")
		}
	}

	connString := fmt.Sprintf(
		"host=%s port=%d dbname=%s sslmode=disable",
		conf.Host, conf.Port, conf.DBName,
	)

	if conf.Password != "" {
		connString += " password=" + conf.Password
	}

	if conf.Username == "" {
		conf.Username = os.Getenv("KONTROL_POSTGRES_USERNAME")
		if conf.Username == "" {
			panic("username is not set for postgres kontrol storage")
		}
	}

	connString += " user=" + conf.Username

	db, err := sql.Open("postgres", connString)
	if err != nil {
		panic(err)
	}

	// enable the ltree module which we are going to use, any error means it's
	// failed so there is no sense to continue, panic!
	enableTree := `CREATE EXTENSION IF NOT EXISTS ltree`
	if _, err := db.Exec(enableTree); err != nil {
		panic(err)
	}

	// create our initial kites table
	// * kite is going to be our ltree
	// * url is containing the kite's register url
	// * id is going to be kites' unique id (which also exists in the ltree
	//   path). We are adding it as a primary key so each kite with the full
	//   path can only exist once.
	// * created_at and updated_at are updated at creation and updating (like
	//  if the URL has changed)
	// Some notes:
	// * path label can only contain a sequence of alphanumeric characters
	//   and underscores. So for example a version string of "1.0.4" needs to
	//   be converted to "1_0_4" or uuid of 1111-2222-3333-4444 needs to be
	//   converted to 1111_2222_3333_4444.
	table := `CREATE TABLE IF NOT EXISTS kites (
		kite ltree NOT NULL,
		url text NOT NULL,
		id uuid PRIMARY KEY,
		created_at timestamptz NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
		updated_at timestamptz NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC')
	);`

	if _, err := db.Exec(table); err != nil {
		panic(err)
	}

	// We enable index on the kite field. We don't return on errors because the
	// operator `IF NOT EXISTS` doesn't work for index creation, therefore we
	// assume the indexes might be already created.
	enableGistIndex := `CREATE INDEX kite_path_gist_idx ON kites USING GIST(kite)`
	if _, err := db.Exec(enableGistIndex); err != nil {
		log.Warning("postgres: enable gist index: %s", err)
	}

	p := &Postgres{
		DB:  db,
		Log: log,
	}

	cleanInterval := 30 * time.Second  // clean every 30 second
	expireInterval := 10 * time.Second // clean rows that are 10 second old
	go p.RunCleaner(cleanInterval, expireInterval)

	return p
}

// RunCleaner delets every "interval" duration rows which are older than
// "expire" duration based on the "updated_at" field. For more info check
// CleanExpireRows which is used to delete old rows.
func (p *Postgres) RunCleaner(interval, expire time.Duration) {
	cleanFunc := func() {
		affectedRows, err := p.CleanExpiredRows(expire)
		if err != nil {
			p.Log.Warning("postgres: cleaning old rows failed: %s", err)
		} else if affectedRows != 0 {
			p.Log.Info("postgres: cleaned up %d rows", affectedRows)
		}
	}

	cleanFunc() // run for the first time
	for _ = range time.Tick(interval) {
		cleanFunc()
	}
}

// CleanExpiredRows deletes rows that are at least "expire" duration old. So if
// say an expire duration of 10 second is given, it will delete all rows that
// were updated 10 seconds ago
func (p *Postgres) CleanExpiredRows(expire time.Duration) (int64, error) {
	// See: http://stackoverflow.com/questions/14465727/how-to-insert-things-like-now-interval-2-minutes-into-php-pdo-query
	// basically by passing an integer to INTERVAL is not possible, we need to
	// cast it. However there is a more simpler way, we can multiply INTERVAL
	// with an integer so we just declare a one second INTERVAL and multiply it
	// with the amount we want.
	cleanOldRows := `DELETE FROM kites WHERE updated_at < (now() at time zone 'utc') - ((INTERVAL '1 second') * $1)`

	rows, err := p.DB.Exec(cleanOldRows, int64(expire/time.Second))
	if err != nil {
		return 0, err
	}

	return rows.RowsAffected()
}

func (p *Postgres) Get(query *protocol.KontrolQuery) (Kites, error) {
	// only let query with usernames, otherwise the whole tree will be fetched
	// which is not good for us
	if query.Username == "" {
		return nil, errors.New("username is not specified in query")
	}

	path := ltreePath(query)

	var hasVersionConstraint bool // does query contains a constraint on version?
	var keyRest string            // query key after the version field
	var versionConstraint version.Constraints
	// NewVersion returns an error if it's a constraint, like: ">= 1.0, < 1.4"
	_, err := version.NewVersion(query.Version)
	if err != nil && query.Version != "" {
		// now parse our constraint
		versionConstraint, err = version.NewConstraint(query.Version)
		if err != nil {
			// version is a malformed, just return the error
			return nil, err
		}

		hasVersionConstraint = true
		nameQuery := &protocol.KontrolQuery{
			Username:    query.Username,
			Environment: query.Environment,
			Name:        query.Name,
		}

		// We will make a get request to all nodes under this name
		// and filter the result later.
		path = ltreePath(nameQuery)

		// Rest of the key after version field
		keyRest = "/" + strings.TrimRight(
			query.Region+"/"+query.Hostname+"/"+query.ID, "/")

	}

	rows, err := p.DB.Query(`SELECT kite, url FROM kites WHERE kite <@ $1`, path)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var kitePath string
	var url string

	kites := make(Kites, 0)

	for rows.Next() {
		err := rows.Scan(&kitePath, &url)
		if err != nil {
			return nil, err
		}

		kiteProt, err := kiteFromPath(kitePath)
		if err != nil {
			return nil, err
		}

		kites = append(kites, &protocol.KiteWithToken{
			Kite: *kiteProt,
			URL:  url,
		})
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}

	// if it's just single result there is no need to shuffle or filter
	// according to the version constraint
	if len(kites) == 1 {
		return kites, nil
	}

	// Filter kites by version constraint
	if hasVersionConstraint {
		kites.Filter(versionConstraint, keyRest)
	}

	// randomize the result
	kites.Shuffle()

	return kites, nil
}

func (p *Postgres) Add(kiteProt *protocol.Kite, value *kontrolprotocol.RegisterValue) error {
	// check that the incoming URL is valid to prevent malformed input
	_, err := url.Parse(value.URL)
	if err != nil {
		return err
	}

	_, err = p.DB.Exec("INSERT into kites(kite, url, id) VALUES($1, $2, $3)",
		ltreePath(kiteProt.Query()),
		value.URL,
		kiteProt.ID,
	)
	return err
}

func (p *Postgres) Update(kiteProt *protocol.Kite, value *kontrolprotocol.RegisterValue) error {
	// check that the incoming url is valid to prevent malformed input
	_, err := url.Parse(value.URL)
	if err != nil {
		return err
	}

	// TODO: also consider just using WHERE id = kiteProt.ID, see how it's
	// performs out
	_, err = p.DB.Exec(`UPDATE kites SET url = $1, updated_at = (now() at time zone 'utc')
	WHERE kite ~ $2`,
		value.URL, ltreePath(kiteProt.Query()))

	return err
}

func (p *Postgres) Delete(kiteProt *protocol.Kite) error {
	deleteKite := `DELETE FROM kites WHERE kite ~ $1`
	_, err := p.DB.Exec(deleteKite, ltreePath(kiteProt.Query()))
	return err
}

func (p *Postgres) Clear() error {
	_, err := p.DB.Exec(`DROP TABLE kites`)
	return err
}

// ltreeLabel satisfies a invalid ltree definition of a label in path.
// According to the definition it is: "A label is a sequence of alphanumeric
// characters and underscores (for example, in C locale the characters
// A-Za-z0-9_ are allowed). Labels must be less than 256 bytes long."
//
// We could express one character with "[A-Za-z0-9_]", a word with
// "[A-Za-z0-9_]+". However we want to catch words that are not valid labels so
// we negate them with the "^" character, so it will be : "[^[A-Za-z0-9_]]+".
// Finally we cann use the POSIX character class: [:word:] which is:
// "Alphanumeric characters plus "_"", so the final regexp will be
// "[^[:word]]+"
var invalidLabelRe = regexp.MustCompile("[^[:word:]]+")

// ltreePath returns a query path to be used with the ltree module in postgress
// in the form of "username.environment.kitename.version.region.hostname.id"
func ltreePath(query *protocol.KontrolQuery) string {
	path := ""
	fields := query.Fields()

	// we stop for the first empty value
	for _, key := range keyOrder {
		v := fields[key]
		if v == "" {
			break
		}

		// replace anything that doesn't match the definition for a ltree path
		// label with a underscore, so the version "0.0.1" will be "0_0_1", or
		// uuid of "1111-2222-3333-4444" will be converted to
		// 1111_2222_3333_4444. Strings that satisfies the requirement are
		// untouched.
		v = invalidLabelRe.ReplaceAllLiteralString(v, "_")

		path = path + v + "."
	}

	// remove the latest dot which causes an invalid query
	path = strings.TrimSuffix(path, ".")
	return path
}

// kiteFromPath returns a Query from the given ltree path label
func kiteFromPath(path string) (*protocol.Kite, error) {
	fields := strings.Split(path, ".")

	if len(fields) != 7 {
		return nil, fmt.Errorf("invalid ltree path: %s", path)
	}

	// those labels were converted by us, therefore convert them back
	version := strings.Replace(fields[3], "_", ".", -1)
	id := strings.Replace(fields[6], "_", "-", -1)

	return &protocol.Kite{
		Username:    fields[0],
		Environment: fields[1],
		Name:        fields[2],
		Version:     version,
		Region:      fields[4],
		Hostname:    fields[5],
		ID:          id,
	}, nil

}
<commit_msg>kontrol/postgres: add btree index to updated_at column<commit_after>package kontrol

import (
	"database/sql"
	"errors"
	"fmt"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/hashicorp/go-version"
	_ "github.com/lib/pq"

	"github.com/koding/kite"
	kontrolprotocol "github.com/koding/kite/kontrol/protocol"
	"github.com/koding/kite/protocol"
)

// Postgres holds Postgresql database related configuration
type PostgresConfig struct {
	Host     string
	Port     int
	Username string
	Password string
	DBName   string
}

type Postgres struct {
	DB  *sql.DB
	Log kite.Logger
}

func NewPostgres(conf *PostgresConfig, log kite.Logger) *Postgres {
	if conf == nil {
		conf = &PostgresConfig{}
	}

	if conf.Port == 0 {
		conf.Port = 5432
	}

	if conf.Host == "" {
		conf.Host = "localhost"
	}

	if conf.DBName == "" {
		conf.DBName = os.Getenv("KONTROL_POSTGRES_DBNAME")
		if conf.DBName == "" {
			panic("db name is not set for postgres kontrol storage")
		}
	}

	connString := fmt.Sprintf(
		"host=%s port=%d dbname=%s sslmode=disable",
		conf.Host, conf.Port, conf.DBName,
	)

	if conf.Password != "" {
		connString += " password=" + conf.Password
	}

	if conf.Username == "" {
		conf.Username = os.Getenv("KONTROL_POSTGRES_USERNAME")
		if conf.Username == "" {
			panic("username is not set for postgres kontrol storage")
		}
	}

	connString += " user=" + conf.Username

	db, err := sql.Open("postgres", connString)
	if err != nil {
		panic(err)
	}

	// enable the ltree module which we are going to use, any error means it's
	// failed so there is no sense to continue, panic!
	enableTree := `CREATE EXTENSION IF NOT EXISTS ltree`
	if _, err := db.Exec(enableTree); err != nil {
		panic(err)
	}

	// create our initial kites table
	// * kite is going to be our ltree
	// * url is containing the kite's register url
	// * id is going to be kites' unique id (which also exists in the ltree
	//   path). We are adding it as a primary key so each kite with the full
	//   path can only exist once.
	// * created_at and updated_at are updated at creation and updating (like
	//  if the URL has changed)
	// Some notes:
	// * path label can only contain a sequence of alphanumeric characters
	//   and underscores. So for example a version string of "1.0.4" needs to
	//   be converted to "1_0_4" or uuid of 1111-2222-3333-4444 needs to be
	//   converted to 1111_2222_3333_4444.
	table := `CREATE TABLE IF NOT EXISTS kites (
		kite ltree NOT NULL,
		url text NOT NULL,
		id uuid PRIMARY KEY,
		created_at timestamptz NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
		updated_at timestamptz NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC')
	);`

	if _, err := db.Exec(table); err != nil {
		panic(err)
	}

	// We enable index on the kite and updated_at columns. We don't return on
	// errors because the operator `IF NOT EXISTS` doesn't work for index
	// creation, therefore we assume the indexes might be already created.
	enableGistIndex := `CREATE INDEX kite_path_gist_idx ON kites USING GIST(kite)`
	if _, err := db.Exec(enableGistIndex); err != nil {
		log.Warning("postgres: enable gist index: %s", err)
	}

	enableBtreeIndex := `CREATE INDEX kite_path_btree_idx ON kites USING BTREE(updated_at)`
	if _, err := db.Exec(enableBtreeIndex); err != nil {
		log.Warning("postgres: enable btree index: %s", err)
	}

	p := &Postgres{
		DB:  db,
		Log: log,
	}

	cleanInterval := 30 * time.Second  // clean every 30 second
	expireInterval := 10 * time.Second // clean rows that are 10 second old
	go p.RunCleaner(cleanInterval, expireInterval)

	return p
}

// RunCleaner delets every "interval" duration rows which are older than
// "expire" duration based on the "updated_at" field. For more info check
// CleanExpireRows which is used to delete old rows.
func (p *Postgres) RunCleaner(interval, expire time.Duration) {
	cleanFunc := func() {
		affectedRows, err := p.CleanExpiredRows(expire)
		if err != nil {
			p.Log.Warning("postgres: cleaning old rows failed: %s", err)
		} else if affectedRows != 0 {
			p.Log.Info("postgres: cleaned up %d rows", affectedRows)
		}
	}

	cleanFunc() // run for the first time
	for _ = range time.Tick(interval) {
		cleanFunc()
	}
}

// CleanExpiredRows deletes rows that are at least "expire" duration old. So if
// say an expire duration of 10 second is given, it will delete all rows that
// were updated 10 seconds ago
func (p *Postgres) CleanExpiredRows(expire time.Duration) (int64, error) {
	// See: http://stackoverflow.com/questions/14465727/how-to-insert-things-like-now-interval-2-minutes-into-php-pdo-query
	// basically by passing an integer to INTERVAL is not possible, we need to
	// cast it. However there is a more simpler way, we can multiply INTERVAL
	// with an integer so we just declare a one second INTERVAL and multiply it
	// with the amount we want.
	cleanOldRows := `DELETE FROM kites WHERE updated_at < (now() at time zone 'utc') - ((INTERVAL '1 second') * $1)`

	rows, err := p.DB.Exec(cleanOldRows, int64(expire/time.Second))
	if err != nil {
		return 0, err
	}

	return rows.RowsAffected()
}

func (p *Postgres) Get(query *protocol.KontrolQuery) (Kites, error) {
	// only let query with usernames, otherwise the whole tree will be fetched
	// which is not good for us
	if query.Username == "" {
		return nil, errors.New("username is not specified in query")
	}

	path := ltreePath(query)

	var hasVersionConstraint bool // does query contains a constraint on version?
	var keyRest string            // query key after the version field
	var versionConstraint version.Constraints
	// NewVersion returns an error if it's a constraint, like: ">= 1.0, < 1.4"
	_, err := version.NewVersion(query.Version)
	if err != nil && query.Version != "" {
		// now parse our constraint
		versionConstraint, err = version.NewConstraint(query.Version)
		if err != nil {
			// version is a malformed, just return the error
			return nil, err
		}

		hasVersionConstraint = true
		nameQuery := &protocol.KontrolQuery{
			Username:    query.Username,
			Environment: query.Environment,
			Name:        query.Name,
		}

		// We will make a get request to all nodes under this name
		// and filter the result later.
		path = ltreePath(nameQuery)

		// Rest of the key after version field
		keyRest = "/" + strings.TrimRight(
			query.Region+"/"+query.Hostname+"/"+query.ID, "/")

	}

	rows, err := p.DB.Query(`SELECT kite, url FROM kites WHERE kite <@ $1`, path)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var kitePath string
	var url string

	kites := make(Kites, 0)

	for rows.Next() {
		err := rows.Scan(&kitePath, &url)
		if err != nil {
			return nil, err
		}

		kiteProt, err := kiteFromPath(kitePath)
		if err != nil {
			return nil, err
		}

		kites = append(kites, &protocol.KiteWithToken{
			Kite: *kiteProt,
			URL:  url,
		})
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}

	// if it's just single result there is no need to shuffle or filter
	// according to the version constraint
	if len(kites) == 1 {
		return kites, nil
	}

	// Filter kites by version constraint
	if hasVersionConstraint {
		kites.Filter(versionConstraint, keyRest)
	}

	// randomize the result
	kites.Shuffle()

	return kites, nil
}

func (p *Postgres) Add(kiteProt *protocol.Kite, value *kontrolprotocol.RegisterValue) error {
	// check that the incoming URL is valid to prevent malformed input
	_, err := url.Parse(value.URL)
	if err != nil {
		return err
	}

	_, err = p.DB.Exec("INSERT into kites(kite, url, id) VALUES($1, $2, $3)",
		ltreePath(kiteProt.Query()),
		value.URL,
		kiteProt.ID,
	)
	return err
}

func (p *Postgres) Update(kiteProt *protocol.Kite, value *kontrolprotocol.RegisterValue) error {
	// check that the incoming url is valid to prevent malformed input
	_, err := url.Parse(value.URL)
	if err != nil {
		return err
	}

	// TODO: also consider just using WHERE id = kiteProt.ID, see how it's
	// performs out
	_, err = p.DB.Exec(`UPDATE kites SET url = $1, updated_at = (now() at time zone 'utc')
	WHERE kite ~ $2`,
		value.URL, ltreePath(kiteProt.Query()))

	return err
}

func (p *Postgres) Delete(kiteProt *protocol.Kite) error {
	deleteKite := `DELETE FROM kites WHERE kite ~ $1`
	_, err := p.DB.Exec(deleteKite, ltreePath(kiteProt.Query()))
	return err
}

func (p *Postgres) Clear() error {
	_, err := p.DB.Exec(`DROP TABLE kites`)
	return err
}

// ltreeLabel satisfies a invalid ltree definition of a label in path.
// According to the definition it is: "A label is a sequence of alphanumeric
// characters and underscores (for example, in C locale the characters
// A-Za-z0-9_ are allowed). Labels must be less than 256 bytes long."
//
// We could express one character with "[A-Za-z0-9_]", a word with
// "[A-Za-z0-9_]+". However we want to catch words that are not valid labels so
// we negate them with the "^" character, so it will be : "[^[A-Za-z0-9_]]+".
// Finally we cann use the POSIX character class: [:word:] which is:
// "Alphanumeric characters plus "_"", so the final regexp will be
// "[^[:word]]+"
var invalidLabelRe = regexp.MustCompile("[^[:word:]]+")

// ltreePath returns a query path to be used with the ltree module in postgress
// in the form of "username.environment.kitename.version.region.hostname.id"
func ltreePath(query *protocol.KontrolQuery) string {
	path := ""
	fields := query.Fields()

	// we stop for the first empty value
	for _, key := range keyOrder {
		v := fields[key]
		if v == "" {
			break
		}

		// replace anything that doesn't match the definition for a ltree path
		// label with a underscore, so the version "0.0.1" will be "0_0_1", or
		// uuid of "1111-2222-3333-4444" will be converted to
		// 1111_2222_3333_4444. Strings that satisfies the requirement are
		// untouched.
		v = invalidLabelRe.ReplaceAllLiteralString(v, "_")

		path = path + v + "."
	}

	// remove the latest dot which causes an invalid query
	path = strings.TrimSuffix(path, ".")
	return path
}

// kiteFromPath returns a Query from the given ltree path label
func kiteFromPath(path string) (*protocol.Kite, error) {
	fields := strings.Split(path, ".")

	if len(fields) != 7 {
		return nil, fmt.Errorf("invalid ltree path: %s", path)
	}

	// those labels were converted by us, therefore convert them back
	version := strings.Replace(fields[3], "_", ".", -1)
	id := strings.Replace(fields[6], "_", "-", -1)

	return &protocol.Kite{
		Username:    fields[0],
		Environment: fields[1],
		Name:        fields[2],
		Version:     version,
		Region:      fields[4],
		Hostname:    fields[5],
		ID:          id,
	}, nil

}
<|endoftext|>


<commit_before>package tccpoutputs

import (
	"context"
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/giantswarm/microerror"

	"github.com/giantswarm/aws-operator/service/controller/clusterapi/v28/cloudformation"
	"github.com/giantswarm/aws-operator/service/controller/clusterapi/v28/controllercontext"
	"github.com/giantswarm/aws-operator/service/controller/clusterapi/v28/key"
)

const (
	DockerVolumeResourceNameKey   = "DockerVolumeResourceName"
	HostedZoneNameServersKey      = "HostedZoneNameServers"
	MasterCloudConfigVersionKey   = "MasterCloudConfigVersion"
	MasterImageIDKey              = "MasterImageID"
	MasterInstanceResourceNameKey = "MasterInstanceResourceName"
	MasterInstanceTypeKey         = "MasterInstanceType"
	VersionBundleVersionKey       = "VersionBundleVersion"
	VPCIDKey                      = "VPCID"
	VPCPeeringConnectionIDKey     = "VPCPeeringConnectionID"
	WorkerASGNameKey              = "WorkerASGName"
	WorkerCloudConfigVersionKey   = "WorkerCloudConfigVersion"
	WorkerDockerVolumeSizeKey     = "WorkerDockerVolumeSizeGB"
	WorkerImageIDKey              = "WorkerImageID"
	WorkerInstanceTypeKey         = "WorkerInstanceType"
)

func (r *Resource) EnsureCreated(ctx context.Context, obj interface{}) error {
	cr, err := key.ToCluster(obj)
	if err != nil {
		return microerror.Mask(err)
	}
	cc, err := controllercontext.FromContext(ctx)
	if err != nil {
		return microerror.Mask(err)
	}

	var cloudFormation *cloudformation.CloudFormation
	{
		c := cloudformation.Config{
			Client: cc.Client.TenantCluster.AWS.CloudFormation,
		}

		cloudFormation, err = cloudformation.New(c)
		if err != nil {
			return microerror.Mask(err)
		}
	}

	var outputs []cloudformation.Output
	{
		r.logger.LogCtx(ctx, "level", "debug", "message", "finding the tenant cluster cloud formation stack outputs")

		o, s, err := cloudFormation.DescribeOutputsAndStatus(key.StackNameTCCP(cr))
		if cloudformation.IsStackNotFound(err) {
			r.logger.LogCtx(ctx, "level", "debug", "message", "did not find the tenant cluster cloud formation stack outputs")
			r.logger.LogCtx(ctx, "level", "debug", "message", "the tenant cluster cloud formation stack does not exist")
			r.logger.LogCtx(ctx, "level", "debug", "message", "canceling resource")
			return nil

		} else if cloudformation.IsOutputsNotAccessible(err) {
			r.logger.LogCtx(ctx, "level", "debug", "message", "did not find the tenant cluster cloud formation stack outputs")
			r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("the tenant cluster main cloud formation stack output values are not accessible due to stack status %#q", s))
			r.logger.LogCtx(ctx, "level", "debug", "message", "canceling resource")
			cc.Status.TenantCluster.TCCP.IsTransitioning = true
			return nil

		} else if err != nil {
			return microerror.Mask(err)
		}

		outputs = o

		r.logger.LogCtx(ctx, "level", "debug", "message", "found the tenant cluster cloud formation stack outputs")
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, DockerVolumeResourceNameKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.MasterInstance.DockerVolumeResourceName = v
	}

	if r.route53Enabled {
		v, err := cloudFormation.GetOutputValue(outputs, HostedZoneNameServersKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.HostedZoneNameServers = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, MasterImageIDKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.MasterInstance.Image = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, MasterInstanceResourceNameKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.MasterInstance.ResourceName = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, MasterInstanceTypeKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.MasterInstance.Type = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, MasterCloudConfigVersionKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.MasterInstance.CloudConfigVersion = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, VersionBundleVersionKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.VersionBundleVersion = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, VPCIDKey)
		if cloudformation.IsOutputNotFound(err) {
			// TODO this exception is necessary for clusters upgrading from v24 to
			// v25. The code can be cleaned up in v28 and the controller context value
			// assignment can be managed like the other examples below.
			//
			//     https://github.com/giantswarm/giantswarm/issues/5570
			//
			v, err := searchVPCID(cc.Client.TenantCluster.AWS.EC2, key.ClusterID(cr))
			if err != nil {
				return microerror.Mask(err)
			}
			cc.Status.TenantCluster.TCCP.VPC.ID = v
		} else if err != nil {
			return microerror.Mask(err)
		} else {
			cc.Status.TenantCluster.TCCP.VPC.ID = v
		}
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, VPCPeeringConnectionIDKey)
		if cloudformation.IsOutputNotFound(err) {
			// TODO this exception is necessary for clusters upgrading from v23 to
			// v24. The code can be cleaned up in v25 and the controller context value
			// assignment can be managed like the other examples below.
			//
			//     https://github.com/giantswarm/giantswarm/issues/5496
			//
			v, err := searchPeeringConnectionID(cc.Client.TenantCluster.AWS.EC2, key.ClusterID(cr))
			if err != nil {
				return microerror.Mask(err)
			}
			cc.Status.TenantCluster.TCCP.VPC.PeeringConnectionID = v
		} else if err != nil {
			return microerror.Mask(err)
		} else {
			cc.Status.TenantCluster.TCCP.VPC.PeeringConnectionID = v
		}
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, WorkerCloudConfigVersionKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.WorkerInstance.CloudConfigVersion = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, WorkerDockerVolumeSizeKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.WorkerInstance.DockerVolumeSizeGB = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, WorkerImageIDKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.WorkerInstance.Image = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, WorkerInstanceTypeKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.WorkerInstance.Type = v
	}

	return nil
}

func searchPeeringConnectionID(client EC2, clusterID string) (string, error) {
	var peeringID string
	{
		i := &ec2.DescribeVpcPeeringConnectionsInput{
			Filters: []*ec2.Filter{
				{
					Name: aws.String("status-code"),
					Values: []*string{
						aws.String("active"),
					},
				},
				{
					Name: aws.String("tag:Name"),
					Values: []*string{
						aws.String(clusterID),
					},
				},
			},
		}

		o, err := client.DescribeVpcPeeringConnections(i)
		if err != nil {
			return "", microerror.Mask(err)
		}
		if len(o.VpcPeeringConnections) != 1 {
			return "", microerror.Maskf(executionFailedError, "expected one vpc peering connection, got %d", len(o.VpcPeeringConnections))
		}

		peeringID = *o.VpcPeeringConnections[0].VpcPeeringConnectionId
	}

	return peeringID, nil
}

func searchVPCID(client EC2, clusterID string) (string, error) {
	var vpcID string
	{
		i := &ec2.DescribeVpcsInput{
			Filters: []*ec2.Filter{
				{
					Name: aws.String("tag:Name"),
					Values: []*string{
						aws.String(clusterID),
					},
				},
			},
		}

		o, err := client.DescribeVpcs(i)
		if err != nil {
			return "", microerror.Mask(err)
		}
		if len(o.Vpcs) != 1 {
			return "", microerror.Maskf(executionFailedError, "expected one vpc, got %d", len(o.Vpcs))
		}

		vpcID = *o.Vpcs[0].VpcId
	}

	return vpcID, nil
}
<commit_msg>address TODOs in tccpoutputs resource (#1672)<commit_after>package tccpoutputs

import (
	"context"
	"fmt"

	"github.com/giantswarm/microerror"

	"github.com/giantswarm/aws-operator/service/controller/clusterapi/v28/cloudformation"
	"github.com/giantswarm/aws-operator/service/controller/clusterapi/v28/controllercontext"
	"github.com/giantswarm/aws-operator/service/controller/clusterapi/v28/key"
)

const (
	DockerVolumeResourceNameKey   = "DockerVolumeResourceName"
	HostedZoneNameServersKey      = "HostedZoneNameServers"
	MasterCloudConfigVersionKey   = "MasterCloudConfigVersion"
	MasterImageIDKey              = "MasterImageID"
	MasterInstanceResourceNameKey = "MasterInstanceResourceName"
	MasterInstanceTypeKey         = "MasterInstanceType"
	VersionBundleVersionKey       = "VersionBundleVersion"
	VPCIDKey                      = "VPCID"
	VPCPeeringConnectionIDKey     = "VPCPeeringConnectionID"
	WorkerASGNameKey              = "WorkerASGName"
	WorkerCloudConfigVersionKey   = "WorkerCloudConfigVersion"
	WorkerDockerVolumeSizeKey     = "WorkerDockerVolumeSizeGB"
	WorkerImageIDKey              = "WorkerImageID"
	WorkerInstanceTypeKey         = "WorkerInstanceType"
)

func (r *Resource) EnsureCreated(ctx context.Context, obj interface{}) error {
	cr, err := key.ToCluster(obj)
	if err != nil {
		return microerror.Mask(err)
	}
	cc, err := controllercontext.FromContext(ctx)
	if err != nil {
		return microerror.Mask(err)
	}

	var cloudFormation *cloudformation.CloudFormation
	{
		c := cloudformation.Config{
			Client: cc.Client.TenantCluster.AWS.CloudFormation,
		}

		cloudFormation, err = cloudformation.New(c)
		if err != nil {
			return microerror.Mask(err)
		}
	}

	var outputs []cloudformation.Output
	{
		r.logger.LogCtx(ctx, "level", "debug", "message", "finding the tenant cluster cloud formation stack outputs")

		o, s, err := cloudFormation.DescribeOutputsAndStatus(key.StackNameTCCP(cr))
		if cloudformation.IsStackNotFound(err) {
			r.logger.LogCtx(ctx, "level", "debug", "message", "did not find the tenant cluster cloud formation stack outputs")
			r.logger.LogCtx(ctx, "level", "debug", "message", "the tenant cluster cloud formation stack does not exist")
			r.logger.LogCtx(ctx, "level", "debug", "message", "canceling resource")
			return nil

		} else if cloudformation.IsOutputsNotAccessible(err) {
			r.logger.LogCtx(ctx, "level", "debug", "message", "did not find the tenant cluster cloud formation stack outputs")
			r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("the tenant cluster main cloud formation stack output values are not accessible due to stack status %#q", s))
			r.logger.LogCtx(ctx, "level", "debug", "message", "canceling resource")
			cc.Status.TenantCluster.TCCP.IsTransitioning = true
			return nil

		} else if err != nil {
			return microerror.Mask(err)
		}

		outputs = o

		r.logger.LogCtx(ctx, "level", "debug", "message", "found the tenant cluster cloud formation stack outputs")
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, DockerVolumeResourceNameKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.MasterInstance.DockerVolumeResourceName = v
	}

	if r.route53Enabled {
		v, err := cloudFormation.GetOutputValue(outputs, HostedZoneNameServersKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.HostedZoneNameServers = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, MasterImageIDKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.MasterInstance.Image = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, MasterInstanceResourceNameKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.MasterInstance.ResourceName = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, MasterInstanceTypeKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.MasterInstance.Type = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, MasterCloudConfigVersionKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.MasterInstance.CloudConfigVersion = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, VersionBundleVersionKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.VersionBundleVersion = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, VPCIDKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.TCCP.VPC.ID = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, VPCPeeringConnectionIDKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.TCCP.VPC.PeeringConnectionID = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, WorkerCloudConfigVersionKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.WorkerInstance.CloudConfigVersion = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, WorkerDockerVolumeSizeKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.WorkerInstance.DockerVolumeSizeGB = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, WorkerImageIDKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.WorkerInstance.Image = v
	}

	{
		v, err := cloudFormation.GetOutputValue(outputs, WorkerInstanceTypeKey)
		if err != nil {
			return microerror.Mask(err)
		}
		cc.Status.TenantCluster.WorkerInstance.Type = v
	}

	return nil
}
<|endoftext|>


<commit_before>package main

/*
#include <stdlib.h>
#include <jni.h>
#include <android/input.h>
*/
import "C"

import (
	"github.com/scottferg/Go-SDL/gfx"
	gl "github.com/scottferg/egles/es2"
	"log"
)

type Video struct {
	prog          uint
	texture       uint
	fpsmanager    *gfx.FPSmanager
	width, height int
	textureUni    int
	pixelBuffer   chan []uint32
	blank         [240 * 224]uint32
}

var video Video

const vertShaderSrcDef = `
	attribute vec4 vPosition;
	attribute vec2 vTexCoord;
	varying vec2 texCoord;

	void main() {
		texCoord = vec2(vTexCoord.x, -vTexCoord.y);
		gl_Position = vec4((vPosition.xy * 2.0) - 1.0, vPosition.zw);
	}
`

const fragShaderSrcDef = `
	precision mediump float;
	varying vec2 texCoord;
	uniform sampler2D texture;

	void main() {
		vec4 c = texture2D(texture, texCoord);
		gl_FragColor = vec4(c.a, c.b, c.g, 1.0);
	}
`

func (video *Video) initGL() {
	log.Printf("GL_VERSION: %v GL_RENDERER: %v GL_VENDOR %v\n",
		gl.GetString(gl.VERSION), gl.GetString(gl.RENDERER), gl.GetString(gl.VENDOR))
	log.Printf("GL_EXTENSIONS: %v\n", gl.GetString(gl.EXTENSIONS))

	video.fpsmanager = gfx.NewFramerate()
	video.fpsmanager.SetFramerate(60)

	gl.ClearColor(0.0, 0.0, 0.0, 1.0)
	gl.Enable(gl.CULL_FACE)
	gl.Enable(gl.DEPTH_TEST)

	video.prog = createProgram(vertShaderSrcDef, fragShaderSrcDef)
	posAttrib := attribLocation(video.prog, "vPosition")
	texCoordAttr := attribLocation(video.prog, "vTexCoord")
	video.textureUni = uniformLocation(video.prog, "texture")

	video.texture = GenTexture()
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, video.texture)

	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)

	gl.UseProgram(video.prog)
	gl.EnableVertexAttribArray(posAttrib)
	gl.EnableVertexAttribArray(texCoordAttr)

	vertVBO := GenBuffer()
	checkGLError()
	gl.BindBuffer(gl.ARRAY_BUFFER, vertVBO)
	verts := []float32{-1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0}
	gl.BufferData(gl.ARRAY_BUFFER, len(verts)*4, gl.Void(&verts[0]), gl.STATIC_DRAW)

	textCoorBuf := GenBuffer()
	checkGLError()
	gl.BindBuffer(gl.ARRAY_BUFFER, textCoorBuf)
	texVerts := []float32{0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0}
	gl.BufferData(gl.ARRAY_BUFFER, len(texVerts)*4, gl.Void(&texVerts[0]), gl.STATIC_DRAW)

	gl.VertexAttribPointer(posAttrib, 2, gl.FLOAT, false, 0, 0)
	gl.VertexAttribPointer(texCoordAttr, 2, gl.FLOAT, false, 0, 0)

}

func (video *Video) resize(width, height int) {
	video.width = width
	video.height = height

	gl.Viewport(0, 0, width, height)
}

func (video *Video) drawFrame() {
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
	gl.UseProgram(video.prog)
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, video.texture)

	if video.pixelBuffer != nil {
		if bmp := <-video.pixelBuffer; bmp != nil {
			gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 240, 224, 0,
				gl.RGBA, gl.UNSIGNED_BYTE, gl.Void(&bmp[0]))
		}
	} else {
		gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 240, 224, 0,
			gl.RGBA, gl.UNSIGNED_BYTE, gl.Void(&video.blank[0]))
	}

	gl.DrawArrays(gl.TRIANGLES, 0, 6)
	video.fpsmanager.FramerateDelay()
}

func createProgram(vertShaderSrc string, fragShaderSrc string) uint {
	vertShader := loadShader(gl.VERTEX_SHADER, vertShaderSrc)
	fragShader := loadShader(gl.FRAGMENT_SHADER, fragShaderSrc)

	prog := gl.CreateProgram()
	if prog == 0 {
		panic("Failed to create shader program")
	}

	gl.AttachShader(prog, vertShader)
	gl.AttachShader(prog, fragShader)
	gl.LinkProgram(prog)

	gl.GetProgramiv(prog, gl.LINK_STATUS, make([]int32, 1))

	return prog
}

func loadShader(shaderType uint, source string) uint {
	handle := gl.CreateShader(shaderType)
	if handle == 0 {
		log.Fatalf("Failed to create shader of type %v", shaderType)
	}

	gl.ShaderSource(handle, source)
	gl.CompileShader(handle)

	gl.GetShaderiv(handle, gl.COMPILE_STATUS, make([]int32, 1))

	return handle
}

func attribLocation(prog uint, name string) uint {
	attrib := gl.GetAttribLocation(prog, name)
	checkGLError()

	if attrib == -1 {
		log.Fatalf("Failed to find attrib position for %v", name)
	}

	return uint(attrib)
}

func uniformLocation(prog uint, name string) int {
	attrib := gl.GetUniformLocation(prog, name)
	checkGLError()

	if attrib == -1 {
		log.Fatalf("Failed to find attrib position for %v", name)
	}

	return attrib
}

func GenBuffer() uint {
	var buf uint
	gl.GenBuffers(1, gl.Void(&buf))
	return buf
}

func GenTexture() uint {
	var tex uint
	gl.GenBuffers(1, gl.Void(&tex))
	return tex
}

func checkGLError() {
	if glErr := gl.GetError(); glErr != gl.NO_ERROR {
		log.Fatalf("gl. error: %v", glErr)
	}
}

//export Java_com_ferg_afergulator_Engine_drawFrame
func Java_com_ferg_afergulator_Engine_drawFrame(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: drawFrame: %v\n", err)
		}
	}()
	video.drawFrame()
}

//export Java_com_ferg_afergulator_Engine_init
func Java_com_ferg_afergulator_Engine_init(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: init: %v\n", err)
		}
	}()
	video.initGL()
}

//export Java_com_ferg_afergulator_Engine_resize
func Java_com_ferg_afergulator_Engine_resize(env *C.JNIEnv, clazz C.jclass, width, height C.jint) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: resize: %v\n", err)
		}
	}()
	video.resize(int(width), int(height))
}
<commit_msg>Fixed build, rendering (skewed) on Galaxy Nexus/Glass<commit_after>package main

/*
#include <stdlib.h>
#include <jni.h>
#include <android/input.h>
*/
import "C"

import (
	"github.com/scottferg/Go-SDL/gfx"
	gl "github.com/scottferg/egles/es2"
	"log"
)

type Video struct {
	prog          uint
	texture       uint
	fpsmanager    *gfx.FPSmanager
	width, height int
	textureUni    int
	pixelBuffer   chan []uint32
	blank         [240 * 224]uint32
}

var video Video

const vertShaderSrcDef = `
	attribute vec4 vPosition;
	attribute vec2 vTexCoord;
	varying vec2 texCoord;

	void main() {
		texCoord = vec2(vTexCoord.x, -vTexCoord.y);
		gl_Position = vec4((vPosition.xy * 2.0) - 1.0, vPosition.zw);
	}
`

const fragShaderSrcDef = `
	precision mediump float;
	varying vec2 texCoord;
	uniform sampler2D texture;

	void main() {
		vec4 c = texture2D(texture, texCoord);
		gl_FragColor = vec4(c.a, c.b, c.g, 1.0);
	}
`

func (video *Video) initGL() {
	video.fpsmanager = gfx.NewFramerate()
	video.fpsmanager.SetFramerate(60)

	gl.ClearColor(0.0, 0.0, 0.0, 1.0)
	gl.Enable(gl.CULL_FACE)
	gl.Enable(gl.DEPTH_TEST)

	video.prog = createProgram(vertShaderSrcDef, fragShaderSrcDef)
	posAttrib := attribLocation(video.prog, "vPosition")
	texCoordAttr := attribLocation(video.prog, "vTexCoord")
	video.textureUni = uniformLocation(video.prog, "texture")

	video.texture = GenTexture()
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, video.texture)

	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)

	gl.UseProgram(video.prog)
	gl.EnableVertexAttribArray(posAttrib)
	gl.EnableVertexAttribArray(texCoordAttr)

	vertVBO := GenBuffer()
	checkGLError()
	gl.BindBuffer(gl.ARRAY_BUFFER, vertVBO)
	verts := []float32{-1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0}
	gl.BufferData(gl.ARRAY_BUFFER, len(verts)*4, gl.Void(&verts[0]), gl.STATIC_DRAW)

	textCoorBuf := GenBuffer()
	checkGLError()
	gl.BindBuffer(gl.ARRAY_BUFFER, textCoorBuf)
	texVerts := []float32{0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0}
	gl.BufferData(gl.ARRAY_BUFFER, len(texVerts)*4, gl.Void(&texVerts[0]), gl.STATIC_DRAW)

	gl.VertexAttribPointer(posAttrib, 2, gl.FLOAT, false, 0, 0)
	gl.VertexAttribPointer(texCoordAttr, 2, gl.FLOAT, false, 0, 0)

}

func (video *Video) resize(width, height int) {
	video.width = width
	video.height = height

	gl.Viewport(0, 0, width, height)
}

func (video *Video) drawFrame() {
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
	gl.UseProgram(video.prog)
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, video.texture)

	if video.pixelBuffer != nil {
		if bmp := <-video.pixelBuffer; bmp != nil {
			buf := make([]uint32, 256*256)
			for i, v := range bmp {
				buf[i] = v
			}
			gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 256, 256, 0,
				gl.RGBA, gl.UNSIGNED_BYTE, gl.Void(&buf[0]))
		}
	} else {
		gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 240, 224, 0,
			gl.RGBA, gl.UNSIGNED_BYTE, gl.Void(&video.blank[0]))
	}

	gl.DrawArrays(gl.TRIANGLES, 0, 6)
	video.fpsmanager.FramerateDelay()
}

func createProgram(vertShaderSrc string, fragShaderSrc string) uint {
	vertShader := loadShader(gl.VERTEX_SHADER, vertShaderSrc)
	fragShader := loadShader(gl.FRAGMENT_SHADER, fragShaderSrc)

	prog := gl.CreateProgram()
	if prog == 0 {
		panic("Failed to create shader program")
	}

	gl.AttachShader(prog, vertShader)
	gl.AttachShader(prog, fragShader)
	gl.LinkProgram(prog)

	gl.GetProgramiv(prog, gl.LINK_STATUS, make([]int32, 1))

	return prog
}

func loadShader(shaderType uint, source string) uint {
	handle := gl.CreateShader(shaderType)
	if handle == 0 {
		log.Fatalf("Failed to create shader of type %v", shaderType)
	}

	gl.ShaderSource(handle, source)
	gl.CompileShader(handle)

	gl.GetShaderiv(handle, gl.COMPILE_STATUS, make([]int32, 1))

	return handle
}

func attribLocation(prog uint, name string) uint {
	attrib := gl.GetAttribLocation(prog, name)
	checkGLError()

	if attrib == -1 {
		log.Fatalf("Failed to find attrib position for %v", name)
	}

	return uint(attrib)
}

func uniformLocation(prog uint, name string) int {
	attrib := gl.GetUniformLocation(prog, name)
	checkGLError()

	if attrib == -1 {
		log.Fatalf("Failed to find attrib position for %v", name)
	}

	return attrib
}

func GenBuffer() uint {
	var buf uint
	gl.GenBuffers(1, gl.Void(&buf))
	return buf
}

func GenTexture() uint {
	var tex uint
	gl.GenBuffers(1, gl.Void(&tex))
	return tex
}

func checkGLError() {
	if glErr := gl.GetError(); glErr != gl.NO_ERROR {
		log.Fatalf("gl. error: %v", glErr)
	}
}

//export Java_com_ferg_afergulator_Engine_drawFrame
func Java_com_ferg_afergulator_Engine_drawFrame(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: drawFrame: %v\n", err)
		}
	}()
	video.drawFrame()
}

//export Java_com_ferg_afergulator_Engine_init
func Java_com_ferg_afergulator_Engine_init(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: init: %v\n", err)
		}
	}()
	video.initGL()
}

//export Java_com_ferg_afergulator_Engine_resize
func Java_com_ferg_afergulator_Engine_resize(env *C.JNIEnv, clazz C.jclass, width, height C.jint) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: resize: %v\n", err)
		}
	}()
	video.resize(int(width), int(height))
}
<|endoftext|>


<commit_before>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package yaml

import (
	"bytes"
	"strings"

	"gopkg.in/yaml.v3"
	"sigs.k8s.io/kustomize/kyaml/errors"
	"sigs.k8s.io/kustomize/kyaml/sets"
)

// IsYNodeTaggedNull returns true if the node is explicitly tagged Null.
func IsYNodeTaggedNull(n *yaml.Node) bool {
	return n != nil && n.Tag == NodeTagNull
}

// IsYNodeEmptyMap is true if the Node is a non-nil empty map.
func IsYNodeEmptyMap(n *yaml.Node) bool {
	return n != nil && n.Kind == yaml.MappingNode && len(n.Content) == 0
}

// IsYNodeEmptyMap is true if the Node is a non-nil empty sequence.
func IsYNodeEmptySeq(n *yaml.Node) bool {
	return n != nil && n.Kind == yaml.SequenceNode && len(n.Content) == 0
}

// IsYNodeEmptyDoc is true if the node is a Document with no content.
// E.g.: "---\n---"
func IsYNodeEmptyDoc(n *yaml.Node) bool {
	return n.Kind == yaml.DocumentNode && n.Content[0].Tag == NodeTagNull
}

func IsYNodeString(n *yaml.Node) bool {
	return n.Kind == yaml.ScalarNode && n.Tag == NodeTagString
}

// Parser parses values into configuration.
type Parser struct {
	Kind  string `yaml:"kind,omitempty"`
	Value string `yaml:"value,omitempty"`
}

func (p Parser) Filter(_ *RNode) (*RNode, error) {
	d := yaml.NewDecoder(bytes.NewBuffer([]byte(p.Value)))
	o := &RNode{value: &yaml.Node{}}
	return o, d.Decode(o.value)
}

// TODO(pwittrock): test this
func GetStyle(styles ...string) Style {
	var style Style
	for _, s := range styles {
		switch s {
		case "TaggedStyle":
			style |= TaggedStyle
		case "DoubleQuotedStyle":
			style |= DoubleQuotedStyle
		case "SingleQuotedStyle":
			style |= SingleQuotedStyle
		case "LiteralStyle":
			style |= LiteralStyle
		case "FoldedStyle":
			style |= FoldedStyle
		case "FlowStyle":
			style |= FlowStyle
		}
	}
	return style
}

// Filter defines a function to manipulate an individual RNode such as by changing
// its values, or returning a field.
//
// When possible, Filters should be serializable to yaml so that they can be described
// declaratively as data.
//
// Analogous to http://www.linfo.org/filters.html
type Filter interface {
	Filter(object *RNode) (*RNode, error)
}

type FilterFunc func(object *RNode) (*RNode, error)

func (f FilterFunc) Filter(object *RNode) (*RNode, error) {
	return f(object)
}

// TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta
// No need for a direct dependence; the fields are stable.
type TypeMeta struct {
	// APIVersion is the apiVersion field of a Resource
	APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
	// Kind is the kind field of a Resource
	Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
}

// Hardcoded list.
// TODO: replace this with data acquired from openapi.
var notNamespaceableKinds = []string{
	"APIService",
	"CSIDriver",
	"CSINode",
	"CertificateSigningRequest",
	"Cluster",
	"ClusterRole",
	"ClusterRoleBinding",
	"ComponentStatus",
	"CustomResourceDefinition",
	"MutatingWebhookConfiguration",
	"Namespace",
	"Node",
	"PersistentVolume",
	"PodSecurityPolicy",
	"PriorityClass",
	"RuntimeClass",
	"SelfSubjectAccessReview",
	"SelfSubjectRulesReview",
	"StorageClass",
	"SubjectAccessReview",
	"TokenReview",
	"ValidatingWebhookConfiguration",
	"VolumeAttachment",
}

// IsNamespaceable returns true if this TypeMeta is for an object
// that can be placed in a namespace.
// Implements https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#not-all-objects-are-in-a-namespace
func (tm TypeMeta) IsNamespaceable() bool {
	for _, k := range notNamespaceableKinds {
		if k == tm.Kind {
			return false
		}
	}
	return true
}

// NameMeta contains name information.
type NameMeta struct {
	// Name is the metadata.name field of a Resource
	Name string `json:"name,omitempty" yaml:"name,omitempty"`
	// Namespace is the metadata.namespace field of a Resource
	Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
}

// ResourceMeta contains the metadata for a both Resource Type and Resource.
type ResourceMeta struct {
	TypeMeta `json:",inline" yaml:",inline"`
	// ObjectMeta is the metadata field of a Resource
	ObjectMeta `yaml:"metadata,omitempty"`
}

// ObjectMeta contains metadata about a Resource
type ObjectMeta struct {
	NameMeta `json:",inline" yaml:",inline"`
	// Labels is the metadata.labels field of a Resource
	Labels map[string]string `yaml:"labels,omitempty"`
	// Annotations is the metadata.annotations field of a Resource.
	Annotations map[string]string `yaml:"annotations,omitempty"`
}

// GetIdentifier returns a ResourceIdentifier that includes
// the information needed to uniquely identify a resource in a cluster.
func (m *ResourceMeta) GetIdentifier() ResourceIdentifier {
	return ResourceIdentifier{
		TypeMeta: m.TypeMeta,
		NameMeta: m.NameMeta,
	}
}

// ResourceIdentifier contains the information needed to uniquely
// identify a resource in a cluster.
type ResourceIdentifier struct {
	TypeMeta `json:",inline" yaml:",inline"`
	NameMeta `json:",inline" yaml:",inline"`
}

// Comments struct is comment yaml comment types
type Comments struct {
	LineComment string `yaml:"lineComment,omitempty"`
	HeadComment string `yaml:"headComment,omitempty"`
	FootComment string `yaml:"footComment,omitempty"`
}

func (r *ResourceIdentifier) GetName() string {
	return r.Name
}

func (r *ResourceIdentifier) GetNamespace() string {
	return r.Namespace
}

func (r *ResourceIdentifier) GetAPIVersion() string {
	return r.APIVersion
}

func (r *ResourceIdentifier) GetKind() string {
	return r.Kind
}

const (
	Trim = "Trim"
	Flow = "Flow"
)

// String returns a string value for a Node, applying the supplied formatting options
func String(node *yaml.Node, opts ...string) (string, error) {
	if node == nil {
		return "", nil
	}
	optsSet := sets.String{}
	optsSet.Insert(opts...)
	if optsSet.Has(Flow) {
		oldStyle := node.Style
		defer func() {
			node.Style = oldStyle
		}()
		node.Style = yaml.FlowStyle
	}

	b := &bytes.Buffer{}
	e := NewEncoder(b)
	err := e.Encode(node)
	e.Close()
	val := b.String()
	if optsSet.Has(Trim) {
		val = strings.TrimSpace(val)
	}
	return val, errors.Wrap(err)
}
<commit_msg>Update comment in types.go<commit_after>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package yaml

import (
	"bytes"
	"strings"

	"gopkg.in/yaml.v3"
	"sigs.k8s.io/kustomize/kyaml/errors"
	"sigs.k8s.io/kustomize/kyaml/sets"
)

// IsYNodeTaggedNull returns true if the node is explicitly tagged Null.
func IsYNodeTaggedNull(n *yaml.Node) bool {
	return n != nil && n.Tag == NodeTagNull
}

// IsYNodeEmptyMap is true if the Node is a non-nil empty map.
func IsYNodeEmptyMap(n *yaml.Node) bool {
	return n != nil && n.Kind == yaml.MappingNode && len(n.Content) == 0
}

// IsYNodeEmptyMap is true if the Node is a non-nil empty sequence.
func IsYNodeEmptySeq(n *yaml.Node) bool {
	return n != nil && n.Kind == yaml.SequenceNode && len(n.Content) == 0
}

// IsYNodeEmptyDoc is true if the node is a Document with no content.
// E.g.: "---\n---"
func IsYNodeEmptyDoc(n *yaml.Node) bool {
	return n.Kind == yaml.DocumentNode && n.Content[0].Tag == NodeTagNull
}

func IsYNodeString(n *yaml.Node) bool {
	return n.Kind == yaml.ScalarNode && n.Tag == NodeTagString
}

// Parser parses values into configuration.
type Parser struct {
	Kind  string `yaml:"kind,omitempty"`
	Value string `yaml:"value,omitempty"`
}

func (p Parser) Filter(_ *RNode) (*RNode, error) {
	d := yaml.NewDecoder(bytes.NewBuffer([]byte(p.Value)))
	o := &RNode{value: &yaml.Node{}}
	return o, d.Decode(o.value)
}

// TODO(pwittrock): test this
func GetStyle(styles ...string) Style {
	var style Style
	for _, s := range styles {
		switch s {
		case "TaggedStyle":
			style |= TaggedStyle
		case "DoubleQuotedStyle":
			style |= DoubleQuotedStyle
		case "SingleQuotedStyle":
			style |= SingleQuotedStyle
		case "LiteralStyle":
			style |= LiteralStyle
		case "FoldedStyle":
			style |= FoldedStyle
		case "FlowStyle":
			style |= FlowStyle
		}
	}
	return style
}

// Filter defines a function to manipulate an individual RNode such as by changing
// its values, or returning a field.
//
// When possible, Filters should be serializable to yaml so that they can be described
// declaratively as data.
//
// Analogous to http://www.linfo.org/filters.html
type Filter interface {
	Filter(object *RNode) (*RNode, error)
}

type FilterFunc func(object *RNode) (*RNode, error)

func (f FilterFunc) Filter(object *RNode) (*RNode, error) {
	return f(object)
}

// TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta
// No need for a direct dependence; the fields are stable.
type TypeMeta struct {
	// APIVersion is the apiVersion field of a Resource
	APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
	// Kind is the kind field of a Resource
	Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
}

// Hardcoded list.
// TODO(#2861): replace this with data acquired from openapi.
var notNamespaceableKinds = []string{
	"APIService",
	"CSIDriver",
	"CSINode",
	"CertificateSigningRequest",
	"Cluster",
	"ClusterRole",
	"ClusterRoleBinding",
	"ComponentStatus",
	"CustomResourceDefinition",
	"MutatingWebhookConfiguration",
	"Namespace",
	"Node",
	"PersistentVolume",
	"PodSecurityPolicy",
	"PriorityClass",
	"RuntimeClass",
	"SelfSubjectAccessReview",
	"SelfSubjectRulesReview",
	"StorageClass",
	"SubjectAccessReview",
	"TokenReview",
	"ValidatingWebhookConfiguration",
	"VolumeAttachment",
}

// IsNamespaceable returns true if this TypeMeta is for an object
// that can be placed in a namespace.
// Implements https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#not-all-objects-are-in-a-namespace
func (tm TypeMeta) IsNamespaceable() bool {
	for _, k := range notNamespaceableKinds {
		if k == tm.Kind {
			return false
		}
	}
	return true
}

// NameMeta contains name information.
type NameMeta struct {
	// Name is the metadata.name field of a Resource
	Name string `json:"name,omitempty" yaml:"name,omitempty"`
	// Namespace is the metadata.namespace field of a Resource
	Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
}

// ResourceMeta contains the metadata for a both Resource Type and Resource.
type ResourceMeta struct {
	TypeMeta `json:",inline" yaml:",inline"`
	// ObjectMeta is the metadata field of a Resource
	ObjectMeta `yaml:"metadata,omitempty"`
}

// ObjectMeta contains metadata about a Resource
type ObjectMeta struct {
	NameMeta `json:",inline" yaml:",inline"`
	// Labels is the metadata.labels field of a Resource
	Labels map[string]string `yaml:"labels,omitempty"`
	// Annotations is the metadata.annotations field of a Resource.
	Annotations map[string]string `yaml:"annotations,omitempty"`
}

// GetIdentifier returns a ResourceIdentifier that includes
// the information needed to uniquely identify a resource in a cluster.
func (m *ResourceMeta) GetIdentifier() ResourceIdentifier {
	return ResourceIdentifier{
		TypeMeta: m.TypeMeta,
		NameMeta: m.NameMeta,
	}
}

// ResourceIdentifier contains the information needed to uniquely
// identify a resource in a cluster.
type ResourceIdentifier struct {
	TypeMeta `json:",inline" yaml:",inline"`
	NameMeta `json:",inline" yaml:",inline"`
}

// Comments struct is comment yaml comment types
type Comments struct {
	LineComment string `yaml:"lineComment,omitempty"`
	HeadComment string `yaml:"headComment,omitempty"`
	FootComment string `yaml:"footComment,omitempty"`
}

func (r *ResourceIdentifier) GetName() string {
	return r.Name
}

func (r *ResourceIdentifier) GetNamespace() string {
	return r.Namespace
}

func (r *ResourceIdentifier) GetAPIVersion() string {
	return r.APIVersion
}

func (r *ResourceIdentifier) GetKind() string {
	return r.Kind
}

const (
	Trim = "Trim"
	Flow = "Flow"
)

// String returns a string value for a Node, applying the supplied formatting options
func String(node *yaml.Node, opts ...string) (string, error) {
	if node == nil {
		return "", nil
	}
	optsSet := sets.String{}
	optsSet.Insert(opts...)
	if optsSet.Has(Flow) {
		oldStyle := node.Style
		defer func() {
			node.Style = oldStyle
		}()
		node.Style = yaml.FlowStyle
	}

	b := &bytes.Buffer{}
	e := NewEncoder(b)
	err := e.Encode(node)
	e.Close()
	val := b.String()
	if optsSet.Has(Trim) {
		val = strings.TrimSpace(val)
	}
	return val, errors.Wrap(err)
}
<|endoftext|>