<commit_before>package config

import (
	"os"
	"text/template"

	"github.com/Masterminds/sprig"
	"github.com/drud/bootstrap/cli/cms/model"
)

const (
	wordpressTemplate = `<?php
{{ $config := . }}
/* Automatically generated WordPress wp-config.php file. */

// ============================
// Production database settings
// ============================
if ( ! defined( 'DB_NAME' ) )
    define( 'DB_NAME', "{{ $config.DatabaseName }}" );
if ( ! defined( 'DB_USER' ) )
    define( 'DB_USER', "{{ $config.DatabaseUsername }}" );
if ( ! defined( 'DB_PASSWORD' ) )
    define( 'DB_PASSWORD', "{{ $config.DatabasePassword }}" );
if ( ! defined( 'DB_HOST' ) )
    define( 'DB_HOST', "{{ $config.DatabaseHost }}" );

// ======================================================
// Additonal DB settings, you do not want to change these
// ======================================================
if (file_exists(__DIR__ . '/custom.settings.php')) {
  include __DIR__ . '/custom.settings.php';
}

define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );

// =================
// Site and WP URL's
// =================
if ( ! defined( 'WP_SITEURL' ) ) {
  // define( 'WP_SITEURL', 'http://' . $_SERVER['HTTP_HOST'] . '/wp' );
  {{ if $config.WPGeneric }}
  define( 'WP_SITEURL', '{{ $config.DeployURL }}' );
  {{ else }}
  define( 'WP_SITEURL', '{{ $config.DeployURL }}/wp' );
  {{ end }}
}
if ( ! defined( 'WP_HOME' ) ) {
  // define( 'WP_HOME', 'http://' . $_SERVER['HTTP_HOST'] );
  define( 'WP_HOME', '{{ $config.DeployURL }}' );
}
// ============================================
// Custom Content Directory.
// Can also change them in local/dev-config.php
// ============================================
if ( ! defined( 'WP_CONTENT_DIR' ) ) {
  {{ if $config.WPGeneric }}
  define( 'WP_CONTENT_DIR', dirname( __FILE__ ) . '/wp-content' );
  {{ else }}
  define( 'WP_CONTENT_DIR', dirname( __FILE__ ) . '/content' );
  {{ end }}
  //define( 'WP_CONTENT_DIR', '{{ $config.Docroot }}/content' );
}
if ( ! defined( 'WP_CONTENT_URL' ) )
  {{ if $config.WPGeneric }}
  define( 'WP_CONTENT_URL', WP_HOME . '/wp-content' );
  {{ else }}
  define( 'WP_CONTENT_URL', WP_HOME . '/content' );
  {{ end }}

// Allows for WP to work behind an reverse proxy with HTTPS
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
       $_SERVER['HTTPS']='on';

// ==============================================================
// Salts, for security
// Grab these from: https://api.wordpress.org/secret-key/1.1/salt
// ==============================================================
define( 'AUTH_KEY',         '{{ $config.AuthKey }}' );
define( 'SECURE_AUTH_KEY',  '{{ $config.SecureAuthKey }}' );
define( 'LOGGED_IN_KEY',    '{{ $config.LoggedInKey }}' );
define( 'NONCE_KEY',        '{{ $config.NonceKey }}' );
define( 'AUTH_SALT',        '{{ $config.AuthSalt }}' );
define( 'SECURE_AUTH_SALT', '{{ $config.SecureAuthSalt }}' );
define( 'LOGGED_IN_SALT',   '{{ $config.LoggedInSalt }}' );
define( 'NONCE_SALT',       '{{ $config.NonceSalt }}' );

// ==============================================================
// Table prefix
// Change this if you have multiple installs in the same database
// ==============================================================
$table_prefix  = '{{ $config.TablePrefix }}';

// ================================
// Language
// Leave blank for American English
// ================================
if ( !defined( 'WPLANG' ) )
  define( 'WPLANG', '' );

// ===========
// Hide errors
// ===========
ini_set( 'display_errors', 0 );
if ( !defined( 'WP_DEBUG_DISPLAY' ) )
  define( 'WP_DEBUG_DISPLAY', false );

// ============================================
// Debug mode
// Can also enable them in local/dev-config.php
// ============================================
{{ if eq $config.DeployName "default" }}
define( 'WP_DEBUG', true );
if ( !defined( 'SAVEQUERIES' ) )
  define( 'SAVEQUERIES', true );
if ( !defined( 'SCRIPT_DEBUG' ) )
  define('SCRIPT_DEBUG', true);
if ( !defined( 'WP_ENV' ) )
  define('WP_ENV', 'development');
{{ else }}
define( 'WP_DEBUG', false );
if ( !defined( 'SAVEQUERIES' ) )
  define( 'SAVEQUERIES', false );
if ( !defined( 'SCRIPT_DEBUG' ) )
  define('SCRIPT_DEBUG', false);
if ( !defined( 'WP_ENV' ) )
  define('WP_ENV', 'production');
{{ end }}

// =====================================
// Change Autosave Interval - in seconds
// =====================================
if ( !defined( 'AUTOSAVE_INTERVAL' ) )
  define('AUTOSAVE_INTERVAL', 240 );

// ==============================================================
// Configure Post Revisions - false if you don't want to save any
// ==============================================================
if ( !defined( 'WP_POST_REVISIONS' ) )
  define( 'WP_POST_REVISIONS', 3 ); // or false

// ========================================
// Remove Trash - In days, WP default is 30
// ========================================
define( 'EMPTY_TRASH_DAYS', 60 );

// =========================
// Increase PHP Memory Limit
// =========================
if ( !defined( 'WP_MEMORY_LIMIT' ) )
  define( 'WP_MEMORY_LIMIT', '128M' );

// =============================================
// Dis-Allow Plugin / Theme - Editing / Updating
// =============================================
if ( !defined( 'DISALLOW_FILE_EDIT' ) ) // editor
  define('DISALLOW_FILE_EDIT', true);
if ( !defined( 'DISALLOW_FILE_MODS' ) ) // updates
  define( 'DISALLOW_FILE_MODS', true );

// =====================================
// WP - Core only updates the core files
// No Akisemet or Hello Dolly
// =====================================
if ( !defined( 'CORE_UPGRADE_SKIP_NEW_BUNDLED' ) )
  define( 'CORE_UPGRADE_SKIP_NEW_BUNDLED', true );

// ===========================================
// Override default permissions
// If you want allow direct plugin downloading
// ===========================================
if ( !defined( 'FS_CHMOD_DIR' ) )
  define( 'FS_CHMOD_DIR', ( 0755 & ~ umask() ) );
if ( !defined( 'FS_CHMOD_FILE' ) )
  define( 'FS_CHMOD_FILE', ( 0644 & ~ umask() ) );

// ======================================
// Load a Memcached config if we have one
// ======================================
if ( file_exists( dirname( __FILE__ ) . '/memcached.php' ) )
  $memcached_servers = include( dirname( __FILE__ ) . '/memcached.php' );

// ===========================================================================================
// This can be used to programatically set the stage when deploying (e.g. production, staging)
// ===========================================================================================
define( 'WP_STAGE', 'xxxx' );
define( 'STAGING_DOMAIN', 'xxxx' ); // Does magic in WP Stack to handle staging domain rewriting

// ========================================
// Absolute path to the WordPress directory
// ========================================
if ( !defined( 'ABSPATH' ) ) {
  // define( 'ABSPATH', dirname( __FILE__ ) . '/wp/' );
  define( 'ABSPATH', '{{ $config.Docroot }}' . '/wp/' );
}

// ====================
// Bootstraps WordPress
// ====================
require_once( ABSPATH . 'wp-settings.php' );
`
)

// WriteWordpressConfig dynamically produces valid wp-config.php file by combining a configuration
// object with a data-driven template.
func WriteWordpressConfig(wordpressConfig *model.WordpressConfig, filePath string) error {
	tmpl, err := template.New("wordpressConfig").Funcs(sprig.TxtFuncMap()).Parse(wordpressTemplate)
	if err != nil {
		return err
	}
	file, err := os.Create(filePath)
	if err != nil {
		return err
	}
	err = tmpl.Execute(file, wordpressConfig)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>fix Undefined index: HTTP_X_FORWARDED_PROTO in phar (#203)<commit_after>package config

import (
	"os"
	"text/template"

	"github.com/Masterminds/sprig"
	"github.com/drud/bootstrap/cli/cms/model"
)

const (
	wordpressTemplate = `<?php
{{ $config := . }}
/* Automatically generated WordPress wp-config.php file. */

// ============================
// Production database settings
// ============================
if ( ! defined( 'DB_NAME' ) )
    define( 'DB_NAME', "{{ $config.DatabaseName }}" );
if ( ! defined( 'DB_USER' ) )
    define( 'DB_USER', "{{ $config.DatabaseUsername }}" );
if ( ! defined( 'DB_PASSWORD' ) )
    define( 'DB_PASSWORD', "{{ $config.DatabasePassword }}" );
if ( ! defined( 'DB_HOST' ) )
    define( 'DB_HOST', "{{ $config.DatabaseHost }}" );

// ======================================================
// Additonal DB settings, you do not want to change these
// ======================================================
if (file_exists(__DIR__ . '/custom.settings.php')) {
  include __DIR__ . '/custom.settings.php';
}

define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );

// =================
// Site and WP URL's
// =================
if ( ! defined( 'WP_SITEURL' ) ) {
  // define( 'WP_SITEURL', 'http://' . $_SERVER['HTTP_HOST'] . '/wp' );
  {{ if $config.WPGeneric }}
  define( 'WP_SITEURL', '{{ $config.DeployURL }}' );
  {{ else }}
  define( 'WP_SITEURL', '{{ $config.DeployURL }}/wp' );
  {{ end }}
}
if ( ! defined( 'WP_HOME' ) ) {
  // define( 'WP_HOME', 'http://' . $_SERVER['HTTP_HOST'] );
  define( 'WP_HOME', '{{ $config.DeployURL }}' );
}
// ============================================
// Custom Content Directory.
// Can also change them in local/dev-config.php
// ============================================
if ( ! defined( 'WP_CONTENT_DIR' ) ) {
  {{ if $config.WPGeneric }}
  define( 'WP_CONTENT_DIR', dirname( __FILE__ ) . '/wp-content' );
  {{ else }}
  define( 'WP_CONTENT_DIR', dirname( __FILE__ ) . '/content' );
  {{ end }}
  //define( 'WP_CONTENT_DIR', '{{ $config.Docroot }}/content' );
}
if ( ! defined( 'WP_CONTENT_URL' ) )
  {{ if $config.WPGeneric }}
  define( 'WP_CONTENT_URL', WP_HOME . '/wp-content' );
  {{ else }}
  define( 'WP_CONTENT_URL', WP_HOME . '/content' );
  {{ end }}

// Allows for WP to work behind an reverse proxy with HTTPS
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
       $_SERVER['HTTPS']='on';

// ==============================================================
// Salts, for security
// Grab these from: https://api.wordpress.org/secret-key/1.1/salt
// ==============================================================
define( 'AUTH_KEY',         '{{ $config.AuthKey }}' );
define( 'SECURE_AUTH_KEY',  '{{ $config.SecureAuthKey }}' );
define( 'LOGGED_IN_KEY',    '{{ $config.LoggedInKey }}' );
define( 'NONCE_KEY',        '{{ $config.NonceKey }}' );
define( 'AUTH_SALT',        '{{ $config.AuthSalt }}' );
define( 'SECURE_AUTH_SALT', '{{ $config.SecureAuthSalt }}' );
define( 'LOGGED_IN_SALT',   '{{ $config.LoggedInSalt }}' );
define( 'NONCE_SALT',       '{{ $config.NonceSalt }}' );

// ==============================================================
// Table prefix
// Change this if you have multiple installs in the same database
// ==============================================================
$table_prefix  = '{{ $config.TablePrefix }}';

// ================================
// Language
// Leave blank for American English
// ================================
if ( !defined( 'WPLANG' ) )
  define( 'WPLANG', '' );

// ===========
// Hide errors
// ===========
ini_set( 'display_errors', 0 );
if ( !defined( 'WP_DEBUG_DISPLAY' ) )
  define( 'WP_DEBUG_DISPLAY', false );

// ============================================
// Debug mode
// Can also enable them in local/dev-config.php
// ============================================
{{ if eq $config.DeployName "default" }}
define( 'WP_DEBUG', true );
if ( !defined( 'SAVEQUERIES' ) )
  define( 'SAVEQUERIES', true );
if ( !defined( 'SCRIPT_DEBUG' ) )
  define('SCRIPT_DEBUG', true);
if ( !defined( 'WP_ENV' ) )
  define('WP_ENV', 'development');
{{ else }}
define( 'WP_DEBUG', false );
if ( !defined( 'SAVEQUERIES' ) )
  define( 'SAVEQUERIES', false );
if ( !defined( 'SCRIPT_DEBUG' ) )
  define('SCRIPT_DEBUG', false);
if ( !defined( 'WP_ENV' ) )
  define('WP_ENV', 'production');
{{ end }}

// =====================================
// Change Autosave Interval - in seconds
// =====================================
if ( !defined( 'AUTOSAVE_INTERVAL' ) )
  define('AUTOSAVE_INTERVAL', 240 );

// ==============================================================
// Configure Post Revisions - false if you don't want to save any
// ==============================================================
if ( !defined( 'WP_POST_REVISIONS' ) )
  define( 'WP_POST_REVISIONS', 3 ); // or false

// ========================================
// Remove Trash - In days, WP default is 30
// ========================================
define( 'EMPTY_TRASH_DAYS', 60 );

// =========================
// Increase PHP Memory Limit
// =========================
if ( !defined( 'WP_MEMORY_LIMIT' ) )
  define( 'WP_MEMORY_LIMIT', '128M' );

// =============================================
// Dis-Allow Plugin / Theme - Editing / Updating
// =============================================
if ( !defined( 'DISALLOW_FILE_EDIT' ) ) // editor
  define('DISALLOW_FILE_EDIT', true);
if ( !defined( 'DISALLOW_FILE_MODS' ) ) // updates
  define( 'DISALLOW_FILE_MODS', true );

// =====================================
// WP - Core only updates the core files
// No Akisemet or Hello Dolly
// =====================================
if ( !defined( 'CORE_UPGRADE_SKIP_NEW_BUNDLED' ) )
  define( 'CORE_UPGRADE_SKIP_NEW_BUNDLED', true );

// ===========================================
// Override default permissions
// If you want allow direct plugin downloading
// ===========================================
if ( !defined( 'FS_CHMOD_DIR' ) )
  define( 'FS_CHMOD_DIR', ( 0755 & ~ umask() ) );
if ( !defined( 'FS_CHMOD_FILE' ) )
  define( 'FS_CHMOD_FILE', ( 0644 & ~ umask() ) );

// ======================================
// Load a Memcached config if we have one
// ======================================
if ( file_exists( dirname( __FILE__ ) . '/memcached.php' ) )
  $memcached_servers = include( dirname( __FILE__ ) . '/memcached.php' );

// ===========================================================================================
// This can be used to programatically set the stage when deploying (e.g. production, staging)
// ===========================================================================================
define( 'WP_STAGE', 'xxxx' );
define( 'STAGING_DOMAIN', 'xxxx' ); // Does magic in WP Stack to handle staging domain rewriting

// ========================================
// Absolute path to the WordPress directory
// ========================================
if ( !defined( 'ABSPATH' ) ) {
  // define( 'ABSPATH', dirname( __FILE__ ) . '/wp/' );
  define( 'ABSPATH', '{{ $config.Docroot }}' . '/wp/' );
}

// ====================
// Bootstraps WordPress
// ====================
require_once( ABSPATH . 'wp-settings.php' );
`
)

// WriteWordpressConfig dynamically produces valid wp-config.php file by combining a configuration
// object with a data-driven template.
func WriteWordpressConfig(wordpressConfig *model.WordpressConfig, filePath string) error {
	tmpl, err := template.New("wordpressConfig").Funcs(sprig.TxtFuncMap()).Parse(wordpressTemplate)
	if err != nil {
		return err
	}
	file, err := os.Create(filePath)
	if err != nil {
		return err
	}
	err = tmpl.Execute(file, wordpressConfig)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package appui

import (
	"reflect"
	"testing"
)

func TestRowFilter_ByPattern(t *testing.T) {
	type args struct {
		pattern string
	}
	tests := []struct {
		name string
		rf   RowFilter
		args args
		want RowFilter
	}{
	// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.rf.ByPattern(tt.args.pattern); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("RowFilter.ByPattern() = %v, want %v", got, tt.want)
			}
		})
	}
}
<commit_msg>Test row filter by pattern<commit_after>package appui

import (
	gizak "github.com/gizak/termui"
	"github.com/moncho/dry/ui/termui"
	"testing"
)

func TestRowFilter_ByPattern(t *testing.T) {
	type args struct {
		pattern string
	}
	tests := []struct {
		name       string
		filterable FilterableRow
		args       args
		want       bool
	}{
		{
			"blabla -> true",
			filterable{
				columns: []*termui.ParColumn{
					&termui.ParColumn{
						Par: gizak.Par{Text: "blabla"},
					},
				},
			},
			args{
				"blabla",
			},
			true,
		},
		{
			"nope -> false",
			filterable{
				columns: []*termui.ParColumn{
					&termui.ParColumn{
						Par: gizak.Par{Text: "yes"},
					},
				},
			},
			args{
				"nope",
			},
			false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			filter := RowFilters.ByPattern(tt.args.pattern)
			got := filter(tt.filterable)
			if got != tt.want {
				t.Errorf("RowFilter.ByPattern() = %v, want %v", got, tt.want)
			}
		})
	}
}

type filterable struct {
	columns []*termui.ParColumn
}

func (f filterable) ColumnsForFilter() []*termui.ParColumn {
	return f.columns
}
<|endoftext|>


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

package grpc

import (
	"fmt"
	"strings"

	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/internal/channelz"
	"google.golang.org/grpc/resolver"
)

// ccResolverWrapper is a wrapper on top of cc for resolvers.
// It implements resolver.ClientConnection interface.
type ccResolverWrapper struct {
	cc                 *ClientConn
	resolver           resolver.Resolver
	addrCh             chan []resolver.Address
	scCh               chan string
	done               chan struct{}
	lastAddressesCount int
}

// split2 returns the values from strings.SplitN(s, sep, 2).
// If sep is not found, it returns ("", s, false) instead.
func split2(s, sep string) (string, string, bool) {
	spl := strings.SplitN(s, sep, 2)
	if len(spl) < 2 {
		return "", "", false
	}
	return spl[0], spl[1], true
}

// parseTarget splits target into a struct containing scheme, authority and
// endpoint.
//
// If target is not a valid scheme://authority/endpoint, it returns {Endpoint:
// target}.
func parseTarget(target string) (ret resolver.Target) {
	var ok bool
	ret.Scheme, ret.Endpoint, ok = split2(target, "://")
	if !ok {
		return resolver.Target{Endpoint: target}
	}
	ret.Authority, ret.Endpoint, ok = split2(ret.Endpoint, "/")
	if !ok {
		return resolver.Target{Endpoint: target}
	}
	return ret
}

// newCCResolverWrapper parses cc.target for scheme and gets the resolver
// builder for this scheme and builds the resolver. The monitoring goroutine
// for it is not started yet and can be created by calling start().
//
// If withResolverBuilder dial option is set, the specified resolver will be
// used instead.
func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) {
	rb := cc.dopts.resolverBuilder
	if rb == nil {
		return nil, fmt.Errorf("could not get resolver for scheme: %q", cc.parsedTarget.Scheme)
	}

	ccr := &ccResolverWrapper{
		cc:     cc,
		addrCh: make(chan []resolver.Address, 1),
		scCh:   make(chan string, 1),
		done:   make(chan struct{}),
	}

	var err error
	ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, resolver.BuildOption{DisableServiceConfig: cc.dopts.disableServiceConfig})
	if err != nil {
		return nil, err
	}
	return ccr, nil
}

func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOption) {
	ccr.resolver.ResolveNow(o)
}

func (ccr *ccResolverWrapper) close() {
	ccr.resolver.Close()
	close(ccr.done)
}

// NewAddress is called by the resolver implemenetion to send addresses to gRPC.
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
	select {
	case <-ccr.done:
		return
	default:
	}
	grpclog.Infof("ccResolverWrapper: sending new addresses to cc: %v", addrs)
	if channelz.IsOn() {
		ccr.addChannelzTraceEvent(addrs)
	}
	ccr.cc.handleResolvedAddrs(addrs, nil)
}

// NewServiceConfig is called by the resolver implemenetion to send service
// configs to gRPC.
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
	select {
	case <-ccr.done:
		return
	default:
	}
	grpclog.Infof("ccResolverWrapper: got new service config: %v", sc)
	ccr.cc.handleServiceConfig(sc)
}

func (ccr *ccResolverWrapper) addChannelzTraceEvent(addrs []resolver.Address) {
	if len(addrs) == 0 && ccr.lastAddressesCount != 0 {
		channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
			Desc:     "Resolver returns an empty address list",
			Severity: channelz.CtWarning,
		})
	} else if len(addrs) != 0 && ccr.lastAddressesCount == 0 {
		var s string
		for i, a := range addrs {
			if a.ServerName != "" {
				s += a.Addr + "(" + a.ServerName + ")"
			} else {
				s += a.Addr
			}
			if i != len(addrs)-1 {
				s += " "
			}
		}
		channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
			Desc:     fmt.Sprintf("Resolver returns a non-empty address list (previous one was empty) %q", s),
			Severity: channelz.CtINFO,
		})
	}
	ccr.lastAddressesCount = len(addrs)
}
<commit_msg>internal: change split2() comment to be consistent with behavior (#2547)<commit_after>/*
 *
 * Copyright 2017 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package grpc

import (
	"fmt"
	"strings"

	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/internal/channelz"
	"google.golang.org/grpc/resolver"
)

// ccResolverWrapper is a wrapper on top of cc for resolvers.
// It implements resolver.ClientConnection interface.
type ccResolverWrapper struct {
	cc                 *ClientConn
	resolver           resolver.Resolver
	addrCh             chan []resolver.Address
	scCh               chan string
	done               chan struct{}
	lastAddressesCount int
}

// split2 returns the values from strings.SplitN(s, sep, 2).
// If sep is not found, it returns ("", "", false) instead.
func split2(s, sep string) (string, string, bool) {
	spl := strings.SplitN(s, sep, 2)
	if len(spl) < 2 {
		return "", "", false
	}
	return spl[0], spl[1], true
}

// parseTarget splits target into a struct containing scheme, authority and
// endpoint.
//
// If target is not a valid scheme://authority/endpoint, it returns {Endpoint:
// target}.
func parseTarget(target string) (ret resolver.Target) {
	var ok bool
	ret.Scheme, ret.Endpoint, ok = split2(target, "://")
	if !ok {
		return resolver.Target{Endpoint: target}
	}
	ret.Authority, ret.Endpoint, ok = split2(ret.Endpoint, "/")
	if !ok {
		return resolver.Target{Endpoint: target}
	}
	return ret
}

// newCCResolverWrapper parses cc.target for scheme and gets the resolver
// builder for this scheme and builds the resolver. The monitoring goroutine
// for it is not started yet and can be created by calling start().
//
// If withResolverBuilder dial option is set, the specified resolver will be
// used instead.
func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) {
	rb := cc.dopts.resolverBuilder
	if rb == nil {
		return nil, fmt.Errorf("could not get resolver for scheme: %q", cc.parsedTarget.Scheme)
	}

	ccr := &ccResolverWrapper{
		cc:     cc,
		addrCh: make(chan []resolver.Address, 1),
		scCh:   make(chan string, 1),
		done:   make(chan struct{}),
	}

	var err error
	ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, resolver.BuildOption{DisableServiceConfig: cc.dopts.disableServiceConfig})
	if err != nil {
		return nil, err
	}
	return ccr, nil
}

func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOption) {
	ccr.resolver.ResolveNow(o)
}

func (ccr *ccResolverWrapper) close() {
	ccr.resolver.Close()
	close(ccr.done)
}

// NewAddress is called by the resolver implemenetion to send addresses to gRPC.
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
	select {
	case <-ccr.done:
		return
	default:
	}
	grpclog.Infof("ccResolverWrapper: sending new addresses to cc: %v", addrs)
	if channelz.IsOn() {
		ccr.addChannelzTraceEvent(addrs)
	}
	ccr.cc.handleResolvedAddrs(addrs, nil)
}

// NewServiceConfig is called by the resolver implemenetion to send service
// configs to gRPC.
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
	select {
	case <-ccr.done:
		return
	default:
	}
	grpclog.Infof("ccResolverWrapper: got new service config: %v", sc)
	ccr.cc.handleServiceConfig(sc)
}

func (ccr *ccResolverWrapper) addChannelzTraceEvent(addrs []resolver.Address) {
	if len(addrs) == 0 && ccr.lastAddressesCount != 0 {
		channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
			Desc:     "Resolver returns an empty address list",
			Severity: channelz.CtWarning,
		})
	} else if len(addrs) != 0 && ccr.lastAddressesCount == 0 {
		var s string
		for i, a := range addrs {
			if a.ServerName != "" {
				s += a.Addr + "(" + a.ServerName + ")"
			} else {
				s += a.Addr
			}
			if i != len(addrs)-1 {
				s += " "
			}
		}
		channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
			Desc:     fmt.Sprintf("Resolver returns a non-empty address list (previous one was empty) %q", s),
			Severity: channelz.CtINFO,
		})
	}
	ccr.lastAddressesCount = len(addrs)
}
<|endoftext|>


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

package grpc

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"google.golang.org/grpc/balancer"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/internal/channelz"
	"google.golang.org/grpc/internal/grpcsync"
	"google.golang.org/grpc/resolver"
	"google.golang.org/grpc/serviceconfig"
)

// ccResolverWrapper is a wrapper on top of cc for resolvers.
// It implements resolver.ClientConnection interface.
type ccResolverWrapper struct {
	cc         *ClientConn
	resolverMu sync.Mutex
	resolver   resolver.Resolver
	done       *grpcsync.Event
	curState   resolver.State

	pollingMu sync.Mutex
	polling   chan struct{}
}

// split2 returns the values from strings.SplitN(s, sep, 2).
// If sep is not found, it returns ("", "", false) instead.
func split2(s, sep string) (string, string, bool) {
	spl := strings.SplitN(s, sep, 2)
	if len(spl) < 2 {
		return "", "", false
	}
	return spl[0], spl[1], true
}

// parseTarget splits target into a struct containing scheme, authority and
// endpoint.
//
// If target is not a valid scheme://authority/endpoint, it returns {Endpoint:
// target}.
func parseTarget(target string) (ret resolver.Target) {
	var ok bool
	ret.Scheme, ret.Endpoint, ok = split2(target, "://")
	if !ok {
		return resolver.Target{Endpoint: target}
	}
	ret.Authority, ret.Endpoint, ok = split2(ret.Endpoint, "/")
	if !ok {
		return resolver.Target{Endpoint: target}
	}
	return ret
}

// newCCResolverWrapper uses the resolver.Builder stored in the ClientConn to
// build a Resolver and returns a ccResolverWrapper object which wraps the
// newly built resolver.
func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) {
	rb := cc.dopts.resolverBuilder
	if rb == nil {
		return nil, fmt.Errorf("could not get resolver for scheme: %q", cc.parsedTarget.Scheme)
	}

	ccr := &ccResolverWrapper{
		cc:   cc,
		done: grpcsync.NewEvent(),
	}

	var credsClone credentials.TransportCredentials
	if creds := cc.dopts.copts.TransportCredentials; creds != nil {
		credsClone = creds.Clone()
	}
	rbo := resolver.BuildOptions{
		DisableServiceConfig: cc.dopts.disableServiceConfig,
		DialCreds:            credsClone,
		CredsBundle:          cc.dopts.copts.CredsBundle,
		Dialer:               cc.dopts.copts.Dialer,
	}

	var err error
	// We need to hold the lock here while we assign to the ccr.resolver field
	// to guard against a data race caused by the following code path,
	// rb.Build-->ccr.ReportError-->ccr.poll-->ccr.resolveNow, would end up
	// accessing ccr.resolver which is being assigned here.
	ccr.resolverMu.Lock()
	defer ccr.resolverMu.Unlock()
	ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, rbo)
	if err != nil {
		return nil, err
	}
	return ccr, nil
}

func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOptions) {
	ccr.resolverMu.Lock()
	if !ccr.done.HasFired() {
		ccr.resolver.ResolveNow(o)
	}
	ccr.resolverMu.Unlock()
}

func (ccr *ccResolverWrapper) close() {
	ccr.resolverMu.Lock()
	ccr.resolver.Close()
	ccr.done.Fire()
	ccr.resolverMu.Unlock()
}

// poll begins or ends asynchronous polling of the resolver based on whether
// err is ErrBadResolverState.
func (ccr *ccResolverWrapper) poll(err error) {
	ccr.pollingMu.Lock()
	defer ccr.pollingMu.Unlock()
	if err != balancer.ErrBadResolverState {
		// stop polling
		if ccr.polling != nil {
			close(ccr.polling)
			ccr.polling = nil
		}
		return
	}
	if ccr.polling != nil {
		// already polling
		return
	}
	p := make(chan struct{})
	ccr.polling = p
	go func() {
		for i := 0; ; i++ {
			ccr.resolveNow(resolver.ResolveNowOptions{})
			t := time.NewTimer(ccr.cc.dopts.resolveNowBackoff(i))
			select {
			case <-p:
				t.Stop()
				return
			case <-ccr.done.Done():
				// Resolver has been closed.
				t.Stop()
				return
			case <-t.C:
				select {
				case <-p:
					return
				default:
				}
				// Timer expired; re-resolve.
			}
		}
	}()
}

func (ccr *ccResolverWrapper) UpdateState(s resolver.State) {
	if ccr.done.HasFired() {
		return
	}
	grpclog.Infof("ccResolverWrapper: sending update to cc: %v", s)
	if channelz.IsOn() {
		ccr.addChannelzTraceEvent(s)
	}
	ccr.curState = s
	ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
}

func (ccr *ccResolverWrapper) ReportError(err error) {
	if ccr.done.HasFired() {
		return
	}
	grpclog.Warningf("ccResolverWrapper: reporting error to cc: %v", err)
	if channelz.IsOn() {
		channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
			Desc:     fmt.Sprintf("Resolver reported error: %v", err),
			Severity: channelz.CtWarning,
		})
	}
	ccr.poll(ccr.cc.updateResolverState(resolver.State{}, err))
}

// NewAddress is called by the resolver implementation to send addresses to gRPC.
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
	if ccr.done.HasFired() {
		return
	}
	grpclog.Infof("ccResolverWrapper: sending new addresses to cc: %v", addrs)
	if channelz.IsOn() {
		ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig})
	}
	ccr.curState.Addresses = addrs
	ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
}

// NewServiceConfig is called by the resolver implementation to send service
// configs to gRPC.
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
	if ccr.done.HasFired() {
		return
	}
	grpclog.Infof("ccResolverWrapper: got new service config: %v", sc)
	if ccr.cc.dopts.disableServiceConfig {
		grpclog.Infof("Service config lookups disabled; ignoring config")
		return
	}
	scpr := parseServiceConfig(sc)
	if scpr.Err != nil {
		grpclog.Warningf("ccResolverWrapper: error parsing service config: %v", scpr.Err)
		if channelz.IsOn() {
			channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
				Desc:     fmt.Sprintf("Error parsing service config: %v", scpr.Err),
				Severity: channelz.CtWarning,
			})
		}
		ccr.poll(balancer.ErrBadResolverState)
		return
	}
	if channelz.IsOn() {
		ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr})
	}
	ccr.curState.ServiceConfig = scpr
	ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
}

func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult {
	return parseServiceConfig(scJSON)
}

func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) {
	var updates []string
	var oldSC, newSC *ServiceConfig
	var oldOK, newOK bool
	if ccr.curState.ServiceConfig != nil {
		oldSC, oldOK = ccr.curState.ServiceConfig.Config.(*ServiceConfig)
	}
	if s.ServiceConfig != nil {
		newSC, newOK = s.ServiceConfig.Config.(*ServiceConfig)
	}
	if oldOK != newOK || (oldOK && newOK && oldSC.rawJSONString != newSC.rawJSONString) {
		updates = append(updates, "service config updated")
	}
	if len(ccr.curState.Addresses) > 0 && len(s.Addresses) == 0 {
		updates = append(updates, "resolver returned an empty address list")
	} else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 {
		updates = append(updates, "resolver returned new addresses")
	}
	channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
		Desc:     fmt.Sprintf("Resolver state updated: %+v (%v)", s, strings.Join(updates, "; ")),
		Severity: channelz.CtINFO,
	})
}
<commit_msg>Fix resolver.ClientConnection to resolver.ClientConn (#3288)<commit_after>/*
 *
 * Copyright 2017 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package grpc

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"google.golang.org/grpc/balancer"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/internal/channelz"
	"google.golang.org/grpc/internal/grpcsync"
	"google.golang.org/grpc/resolver"
	"google.golang.org/grpc/serviceconfig"
)

// ccResolverWrapper is a wrapper on top of cc for resolvers.
// It implements resolver.ClientConn interface.
type ccResolverWrapper struct {
	cc         *ClientConn
	resolverMu sync.Mutex
	resolver   resolver.Resolver
	done       *grpcsync.Event
	curState   resolver.State

	pollingMu sync.Mutex
	polling   chan struct{}
}

// split2 returns the values from strings.SplitN(s, sep, 2).
// If sep is not found, it returns ("", "", false) instead.
func split2(s, sep string) (string, string, bool) {
	spl := strings.SplitN(s, sep, 2)
	if len(spl) < 2 {
		return "", "", false
	}
	return spl[0], spl[1], true
}

// parseTarget splits target into a struct containing scheme, authority and
// endpoint.
//
// If target is not a valid scheme://authority/endpoint, it returns {Endpoint:
// target}.
func parseTarget(target string) (ret resolver.Target) {
	var ok bool
	ret.Scheme, ret.Endpoint, ok = split2(target, "://")
	if !ok {
		return resolver.Target{Endpoint: target}
	}
	ret.Authority, ret.Endpoint, ok = split2(ret.Endpoint, "/")
	if !ok {
		return resolver.Target{Endpoint: target}
	}
	return ret
}

// newCCResolverWrapper uses the resolver.Builder stored in the ClientConn to
// build a Resolver and returns a ccResolverWrapper object which wraps the
// newly built resolver.
func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) {
	rb := cc.dopts.resolverBuilder
	if rb == nil {
		return nil, fmt.Errorf("could not get resolver for scheme: %q", cc.parsedTarget.Scheme)
	}

	ccr := &ccResolverWrapper{
		cc:   cc,
		done: grpcsync.NewEvent(),
	}

	var credsClone credentials.TransportCredentials
	if creds := cc.dopts.copts.TransportCredentials; creds != nil {
		credsClone = creds.Clone()
	}
	rbo := resolver.BuildOptions{
		DisableServiceConfig: cc.dopts.disableServiceConfig,
		DialCreds:            credsClone,
		CredsBundle:          cc.dopts.copts.CredsBundle,
		Dialer:               cc.dopts.copts.Dialer,
	}

	var err error
	// We need to hold the lock here while we assign to the ccr.resolver field
	// to guard against a data race caused by the following code path,
	// rb.Build-->ccr.ReportError-->ccr.poll-->ccr.resolveNow, would end up
	// accessing ccr.resolver which is being assigned here.
	ccr.resolverMu.Lock()
	defer ccr.resolverMu.Unlock()
	ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, rbo)
	if err != nil {
		return nil, err
	}
	return ccr, nil
}

func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOptions) {
	ccr.resolverMu.Lock()
	if !ccr.done.HasFired() {
		ccr.resolver.ResolveNow(o)
	}
	ccr.resolverMu.Unlock()
}

func (ccr *ccResolverWrapper) close() {
	ccr.resolverMu.Lock()
	ccr.resolver.Close()
	ccr.done.Fire()
	ccr.resolverMu.Unlock()
}

// poll begins or ends asynchronous polling of the resolver based on whether
// err is ErrBadResolverState.
func (ccr *ccResolverWrapper) poll(err error) {
	ccr.pollingMu.Lock()
	defer ccr.pollingMu.Unlock()
	if err != balancer.ErrBadResolverState {
		// stop polling
		if ccr.polling != nil {
			close(ccr.polling)
			ccr.polling = nil
		}
		return
	}
	if ccr.polling != nil {
		// already polling
		return
	}
	p := make(chan struct{})
	ccr.polling = p
	go func() {
		for i := 0; ; i++ {
			ccr.resolveNow(resolver.ResolveNowOptions{})
			t := time.NewTimer(ccr.cc.dopts.resolveNowBackoff(i))
			select {
			case <-p:
				t.Stop()
				return
			case <-ccr.done.Done():
				// Resolver has been closed.
				t.Stop()
				return
			case <-t.C:
				select {
				case <-p:
					return
				default:
				}
				// Timer expired; re-resolve.
			}
		}
	}()
}

func (ccr *ccResolverWrapper) UpdateState(s resolver.State) {
	if ccr.done.HasFired() {
		return
	}
	grpclog.Infof("ccResolverWrapper: sending update to cc: %v", s)
	if channelz.IsOn() {
		ccr.addChannelzTraceEvent(s)
	}
	ccr.curState = s
	ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
}

func (ccr *ccResolverWrapper) ReportError(err error) {
	if ccr.done.HasFired() {
		return
	}
	grpclog.Warningf("ccResolverWrapper: reporting error to cc: %v", err)
	if channelz.IsOn() {
		channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
			Desc:     fmt.Sprintf("Resolver reported error: %v", err),
			Severity: channelz.CtWarning,
		})
	}
	ccr.poll(ccr.cc.updateResolverState(resolver.State{}, err))
}

// NewAddress is called by the resolver implementation to send addresses to gRPC.
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
	if ccr.done.HasFired() {
		return
	}
	grpclog.Infof("ccResolverWrapper: sending new addresses to cc: %v", addrs)
	if channelz.IsOn() {
		ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig})
	}
	ccr.curState.Addresses = addrs
	ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
}

// NewServiceConfig is called by the resolver implementation to send service
// configs to gRPC.
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
	if ccr.done.HasFired() {
		return
	}
	grpclog.Infof("ccResolverWrapper: got new service config: %v", sc)
	if ccr.cc.dopts.disableServiceConfig {
		grpclog.Infof("Service config lookups disabled; ignoring config")
		return
	}
	scpr := parseServiceConfig(sc)
	if scpr.Err != nil {
		grpclog.Warningf("ccResolverWrapper: error parsing service config: %v", scpr.Err)
		if channelz.IsOn() {
			channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
				Desc:     fmt.Sprintf("Error parsing service config: %v", scpr.Err),
				Severity: channelz.CtWarning,
			})
		}
		ccr.poll(balancer.ErrBadResolverState)
		return
	}
	if channelz.IsOn() {
		ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr})
	}
	ccr.curState.ServiceConfig = scpr
	ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
}

func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult {
	return parseServiceConfig(scJSON)
}

func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) {
	var updates []string
	var oldSC, newSC *ServiceConfig
	var oldOK, newOK bool
	if ccr.curState.ServiceConfig != nil {
		oldSC, oldOK = ccr.curState.ServiceConfig.Config.(*ServiceConfig)
	}
	if s.ServiceConfig != nil {
		newSC, newOK = s.ServiceConfig.Config.(*ServiceConfig)
	}
	if oldOK != newOK || (oldOK && newOK && oldSC.rawJSONString != newSC.rawJSONString) {
		updates = append(updates, "service config updated")
	}
	if len(ccr.curState.Addresses) > 0 && len(s.Addresses) == 0 {
		updates = append(updates, "resolver returned an empty address list")
	} else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 {
		updates = append(updates, "resolver returned new addresses")
	}
	channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
		Desc:     fmt.Sprintf("Resolver state updated: %+v (%v)", s, strings.Join(updates, "; ")),
		Severity: channelz.CtINFO,
	})
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"time"

	"github.com/Cox-Automotive/alks-go"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAlksIamRole() *schema.Resource {
	return &schema.Resource{
		Create: resourceAlksIamRoleCreate,
		Read:   resourceAlksIamRoleRead,
		Exists: resourceAlksIamRoleExists,
		Delete: resourceAlksIamRoleDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"include_default_policies": &schema.Schema{
				Type:     schema.TypeBool,
				Required: true,
				ForceNew: true,
			},
			"role_added_to_ip": &schema.Schema{
				Type:     schema.TypeBool,
				Computed: true,
			},
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"ip_arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAlksIamTrustRole() *schema.Resource {
	return &schema.Resource{
		Create: resourceAlksIamTrustRoleCreate,
		Read:   resourceAlksIamRoleRead,
		Exists: resourceAlksIamRoleExists,
		Delete: resourceAlksIamRoleDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"trust_arn": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"role_added_to_ip": &schema.Schema{
				Type:     schema.TypeBool,
				Computed: true,
			},
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"ip_arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAlksIamRoleCreate(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[INFO] ALKS IAM Role Create")

	var roleName = d.Get("name").(string)
	var roleType = d.Get("type").(string)
	var incDefPol = d.Get("include_default_policies").(bool)

	client := meta.(*alks.Client)
	resp, err := client.CreateIamRole(roleName, roleType, incDefPol)

	if err != nil {
		return err
	}

	d.SetId(resp.RoleName)
	d.Set("arn", resp.RoleArn)
	d.Set("ip_arn", resp.RoleIPArn)
	d.Set("role_added_to_ip", resp.RoleAddedToIP)

	log.Printf("[INFO] alks_iamrole.id: %v", d.Id())

	return nil
}

func resourceAlksIamTrustRoleCreate(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[INFO] ALKS IAM Trust Role Create")

	var roleName = d.Get("name").(string)
	var roleType = d.Get("type").(string)
	var trustArn = d.Get("trust_arn").(string)

	client := meta.(*alks.Client)

	var resp *alks.IamRoleResponse
	err := resource.Retry(2*time.Minute, func() *resource.RetryError {
		var err error
		resp, err = client.CreateIamTrustRole(roleName, roleType, trustArn)
		if err != nil {
			return resource.RetryableError(err)
		}
		return resource.NonRetryableError(err)
	})

	if err != nil {
		return err
	}

	response := *resp

	d.SetId(response.RoleName)
	d.Set("arn", response.RoleArn)
	d.Set("ip_arn", response.RoleIPArn)
	d.Set("role_added_to_ip", response.RoleAddedToIP)

	log.Printf("[INFO] alks_iamtrustrole.id: %v", d.Id())

	return nil
}

func resourceAlksIamRoleDelete(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[INFO] ALKS IAM Role Delete")

	client := meta.(*alks.Client)
	err := client.DeleteIamRole(d.Id())

	if err != nil {
		return err
	}

	return nil
}

func resourceAlksIamRoleExists(d *schema.ResourceData, meta interface{}) (b bool, e error) {
	log.Printf("[INFO] ALKS IAM Role Exists")

	client := meta.(*alks.Client)

	foundrole, err := client.GetIamRole(d.Id())

	if err != nil {
		return false, err
	}

	if foundrole == nil {
		return false, nil
	}

	return true, nil
}

func resourceAlksIamRoleRead(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[INFO] ALKS IAM Role Read")

	client := meta.(*alks.Client)

	foundrole, err := client.GetIamRole(d.Id())

	if err != nil {
		return err
	}

	return populateResourceDataFromRole(foundrole, d)
}

func populateResourceDataFromRole(role *alks.IamRoleResponse, d *schema.ResourceData) error {
	d.SetId(role.RoleName)
	d.Set("arn", role.RoleArn)
	d.Set("ip_arn", role.RoleIPArn)
	// role type isnt returned by alks api so this will always false report on a remote state change
	// for more info see issue #125 on ALKS repo
	// d.Set("type", role.RoleType)

	return nil
}
<commit_msg>adds enable_alks_access field to iam role and trust role resources<commit_after>package main

import (
	"log"
	"time"

	"github.com/Cox-Automotive/alks-go"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAlksIamRole() *schema.Resource {
	return &schema.Resource{
		Create: resourceAlksIamRoleCreate,
		Read:   resourceAlksIamRoleRead,
		Exists: resourceAlksIamRoleExists,
		Delete: resourceAlksIamRoleDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"include_default_policies": &schema.Schema{
				Type:     schema.TypeBool,
				Required: true,
				ForceNew: true,
			},
			"role_added_to_ip": &schema.Schema{
				Type:     schema.TypeBool,
				Computed: true,
			},
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"ip_arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"enable_alks_access": &schema.Schema{
				Type:     schema.TypeBool,
				Default:  false,
				Optional: true,
				ForceNew: true,
			},
		},
	}
}

func resourceAlksIamTrustRole() *schema.Resource {
	return &schema.Resource{
		Create: resourceAlksIamTrustRoleCreate,
		Read:   resourceAlksIamRoleRead,
		Exists: resourceAlksIamRoleExists,
		Delete: resourceAlksIamRoleDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"trust_arn": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"role_added_to_ip": &schema.Schema{
				Type:     schema.TypeBool,
				Computed: true,
			},
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"ip_arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"enable_alks_access": &schema.Schema{
				Type:     schema.TypeBool,
				Default:  false,
				Optional: true,
				ForceNew: true,
			},
		},
	}
}

func resourceAlksIamRoleCreate(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[INFO] ALKS IAM Role Create")

	var roleName = d.Get("name").(string)
	var roleType = d.Get("type").(string)
	var incDefPol = d.Get("include_default_policies").(bool)
	var enableAlksAccess = d.Get("enable_alks_access").(bool)

	client := meta.(*alks.Client)
	resp, err := client.CreateIamRole(roleName, roleType, incDefPol, enableAlksAccess)

	if err != nil {
		return err
	}

	d.SetId(resp.RoleName)
	d.Set("arn", resp.RoleArn)
	d.Set("ip_arn", resp.RoleIPArn)
	d.Set("role_added_to_ip", resp.RoleAddedToIP)

	log.Printf("[INFO] alks_iamrole.id: %v", d.Id())

	return nil
}

func resourceAlksIamTrustRoleCreate(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[INFO] ALKS IAM Trust Role Create")

	var roleName = d.Get("name").(string)
	var roleType = d.Get("type").(string)
	var trustArn = d.Get("trust_arn").(string)
	var enableAlksAccess = d.Get("enable_alks_access").(bool)

	client := meta.(*alks.Client)

	var resp *alks.IamRoleResponse
	err := resource.Retry(2*time.Minute, func() *resource.RetryError {
		var err error
		resp, err = client.CreateIamTrustRole(roleName, roleType, trustArn, enableAlksAccess)
		if err != nil {
			return resource.RetryableError(err)
		}
		return resource.NonRetryableError(err)
	})

	if err != nil {
		return err
	}

	response := *resp

	d.SetId(response.RoleName)
	d.Set("arn", response.RoleArn)
	d.Set("ip_arn", response.RoleIPArn)
	d.Set("role_added_to_ip", response.RoleAddedToIP)

	log.Printf("[INFO] alks_iamtrustrole.id: %v", d.Id())

	return nil
}

func resourceAlksIamRoleDelete(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[INFO] ALKS IAM Role Delete")

	client := meta.(*alks.Client)
	err := client.DeleteIamRole(d.Id())

	if err != nil {
		return err
	}

	return nil
}

func resourceAlksIamRoleExists(d *schema.ResourceData, meta interface{}) (b bool, e error) {
	log.Printf("[INFO] ALKS IAM Role Exists")

	client := meta.(*alks.Client)

	foundrole, err := client.GetIamRole(d.Id())

	if err != nil {
		return false, err
	}

	if foundrole == nil {
		return false, nil
	}

	return true, nil
}

func resourceAlksIamRoleRead(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[INFO] ALKS IAM Role Read")

	client := meta.(*alks.Client)

	foundrole, err := client.GetIamRole(d.Id())

	if err != nil {
		return err
	}

	return populateResourceDataFromRole(foundrole, d)
}

func populateResourceDataFromRole(role *alks.IamRoleResponse, d *schema.ResourceData) error {
	d.SetId(role.RoleName)
	d.Set("arn", role.RoleArn)
	d.Set("ip_arn", role.RoleIPArn)
	// role type isnt returned by alks api so this will always false report on a remote state change
	// for more info see issue #125 on ALKS repo
	// d.Set("type", role.RoleType)

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 ebitenutil

import (
	"image"
	"image/color"
	"math"
	"strings"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil/internal/assets"
)

type debugPrintImageParts string

func (f debugPrintImageParts) Len() int {
	return len(f)
}

func (f debugPrintImageParts) Dst(i int) (x0, y0, x1, y1 int) {
	cw, ch := assets.TextImageCharWidth, assets.TextImageCharHeight
	x := i - strings.LastIndex(string(f)[:i], "\n") - 1
	y := strings.Count(string(f)[:i], "\n")
	x *= cw
	y *= ch
	if x < 0 {
		return 0, 0, 0, 0
	}
	return x, y, x + cw, y + ch
}

func (f debugPrintImageParts) Src(i int) (x0, y0, x1, y1 int) {
	cw, ch := assets.TextImageCharWidth, assets.TextImageCharHeight
	const n = assets.TextImageWidth / assets.TextImageCharWidth
	code := int(f[i])
	if code == '\n' {
		return 0, 0, 0, 0
	}
	x := (code % n) * cw
	y := (code / n) * ch
	return x, y, x + cw, y + ch
}

type debugPrintState struct {
	textImage              *ebiten.Image
	debugPrintRenderTarget *ebiten.Image
}

var defaultDebugPrintState = &debugPrintState{}

// DebugPrint draws the string str on the image.
//
// DebugPrint always returns nil as of 1.5.0-alpha.
func DebugPrint(image *ebiten.Image, str string) error {
	defaultDebugPrintState.DebugPrint(image, str)
	return nil
}

func (d *debugPrintState) drawText(rt *ebiten.Image, str string, ox, oy int, c color.Color) {
	op := &ebiten.DrawImageOptions{}
	ur, ug, ub, ua := c.RGBA()
	const max = math.MaxUint16
	r := float64(ur) / max
	g := float64(ug) / max
	b := float64(ub) / max
	a := float64(ua) / max
	if 0 < a {
		r /= a
		g /= a
		b /= a
	}
	op.ColorM.Scale(r, g, b, a)
	x := 0
	y := 0
	for _, c := range str {
		const cw = assets.TextImageCharWidth
		const ch = assets.TextImageCharHeight
		if c == '\n' {
			x = 0
			y += ch
			continue
		}
		const n = assets.TextImageWidth / cw
		sx := (int(c) % n) * cw
		sy := (int(c) / n) * ch
		r := image.Rect(sx, sy, sx+cw, sy+ch)
		op.SourceRect = &r
		op.GeoM.Reset()
		op.GeoM.Translate(float64(x), float64(y))
		op.GeoM.Translate(float64(ox+1), float64(oy))
		_ = rt.DrawImage(d.textImage, op)
		x += cw
	}
}

// DebugPrint prints the given text str on the given image r.
func (d *debugPrintState) DebugPrint(r *ebiten.Image, str string) {
	if d.textImage == nil {
		img := assets.TextImage()
		d.textImage, _ = ebiten.NewImageFromImage(img, ebiten.FilterNearest)
	}
	if d.debugPrintRenderTarget == nil {
		width, height := 256, 256
		d.debugPrintRenderTarget, _ = ebiten.NewImage(width, height, ebiten.FilterNearest)
	}
	d.drawText(r, str, 1, 1, color.NRGBA{0x00, 0x00, 0x00, 0x80})
	d.drawText(r, str, 0, 0, color.NRGBA{0xff, 0xff, 0xff, 0xff})
}
<commit_msg>ebitenutil: Remove debugPrintImageParts<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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 ebitenutil

import (
	"image"
	"image/color"
	"math"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil/internal/assets"
)

type debugPrintState struct {
	textImage              *ebiten.Image
	debugPrintRenderTarget *ebiten.Image
}

var defaultDebugPrintState = &debugPrintState{}

// DebugPrint draws the string str on the image.
//
// DebugPrint always returns nil as of 1.5.0-alpha.
func DebugPrint(image *ebiten.Image, str string) error {
	defaultDebugPrintState.DebugPrint(image, str)
	return nil
}

func (d *debugPrintState) drawText(rt *ebiten.Image, str string, ox, oy int, c color.Color) {
	op := &ebiten.DrawImageOptions{}
	ur, ug, ub, ua := c.RGBA()
	const max = math.MaxUint16
	r := float64(ur) / max
	g := float64(ug) / max
	b := float64(ub) / max
	a := float64(ua) / max
	if 0 < a {
		r /= a
		g /= a
		b /= a
	}
	op.ColorM.Scale(r, g, b, a)
	x := 0
	y := 0
	for _, c := range str {
		const cw = assets.TextImageCharWidth
		const ch = assets.TextImageCharHeight
		if c == '\n' {
			x = 0
			y += ch
			continue
		}
		const n = assets.TextImageWidth / cw
		sx := (int(c) % n) * cw
		sy := (int(c) / n) * ch
		r := image.Rect(sx, sy, sx+cw, sy+ch)
		op.SourceRect = &r
		op.GeoM.Reset()
		op.GeoM.Translate(float64(x), float64(y))
		op.GeoM.Translate(float64(ox+1), float64(oy))
		_ = rt.DrawImage(d.textImage, op)
		x += cw
	}
}

// DebugPrint prints the given text str on the given image r.
func (d *debugPrintState) DebugPrint(r *ebiten.Image, str string) {
	if d.textImage == nil {
		img := assets.TextImage()
		d.textImage, _ = ebiten.NewImageFromImage(img, ebiten.FilterNearest)
	}
	if d.debugPrintRenderTarget == nil {
		width, height := 256, 256
		d.debugPrintRenderTarget, _ = ebiten.NewImage(width, height, ebiten.FilterNearest)
	}
	d.drawText(r, str, 1, 1, color.NRGBA{0x00, 0x00, 0x00, 0x80})
	d.drawText(r, str, 0, 0, color.NRGBA{0xff, 0xff, 0xff, 0xff})
}
<|endoftext|>


<commit_before>package rabbitsmpp

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/streadway/amqp"
	"golang.org/x/net/context"
)

const (
	defaultPrefetchCount = 20
	defaultPrefetchSize  = 0
	defaultGlobalQos     = false
)

type ConsumeOption struct {
	f func(*consumeOptions)
}

type consumeOptions struct {
	prefetchCount int
	prefetchSize  int
	globalQos     bool
}

func SetPrefetchCount(n int) ConsumeOption {
	return ConsumeOption{func(o *consumeOptions) {
		o.prefetchCount = n
	}}
}

func SetPrefetchSize(n int) ConsumeOption {
	return ConsumeOption{func(o *consumeOptions) {
		o.prefetchSize = n
	}}
}

func SetGlobalQos(a bool) ConsumeOption {
	return ConsumeOption{func(o *consumeOptions) {
		o.globalQos = a
	}}
}

type Consumer interface {
	Consume() (<-chan Job, <-chan error, error)
	Stop() error
	ID() string
}

type consumer struct {
	*client
	channel       Channel
	ctx           context.Context
	cancel        context.CancelFunc
	prefetchCount int
	prefetchSize  int
	globalQos     bool
}

func NewConsumer(conf Config, options ...ConsumeOption) (Consumer, error) {
	c := NewClient(conf).(*client)
	ctx, cancel := context.WithCancel(context.Background())
	o := consumeOptions{
		prefetchCount: defaultPrefetchCount,
		prefetchSize:  defaultPrefetchSize,
		globalQos:     defaultGlobalQos,
	}
	for _, option := range options {
		option.f(&o)
	}

	return &consumer{
		client:        c,
		channel:       nil,
		ctx:           ctx,
		cancel:        cancel,
		prefetchCount: o.prefetchCount,
		prefetchSize:  o.prefetchSize,
		globalQos:     o.globalQos,
	}, nil
}

func newConsumerWithContext(ctx context.Context, conf Config) (Consumer, error) {
	c := NewClient(conf).(*client)
	ctx, cancel := context.WithCancel(ctx)

	return &consumer{
		client:  c,
		channel: nil,
		ctx:     ctx,
		cancel:  cancel,
	}, nil
}

func (c *consumer) ID() string {
	return c.client.QueueName()
}

func (c *consumer) bindWithRetry() chan *amqp.Error {
	closeChan, err := c.Bind()
	for err != nil {
		log.Println("Failed to bind consumer:", err)
		time.Sleep(5 * time.Second)
		closeChan, err = c.Bind()
	}
	return closeChan
}

func (c *consumer) getConsumeChannel() (<-chan amqp.Delivery, error) {
	ch, err := c.Channel()
	if err != nil {
		return nil, err
	}
	err = ch.Qos(c.prefetchCount, c.prefetchSize, c.globalQos)
	if err != nil {
		return nil, err
	}

	c.channel = ch

	q, err := c.channel.QueueDeclare(
		c.QueueName(), // name
		true,          // durable
		false,         // delete when unused
		false,         // exclusive
		false,         // no-wait
		nil,           // arguments
	)
	if err != nil {
		return nil, err
	}

	return c.channel.Consume(
		q.Name, // queue
		"",     // consumer
		false,  // auto-ackey
		false,  // exclusive
		false,  // no-local
		false,  // no-wait
		nil,    // args
	)
}

func (c *consumer) Consume() (<-chan Job, <-chan error, error) {
	if c.channel != nil {
		return nil, nil, errors.New("consumer already active")
	}
	closeChan, err := c.Bind()
	if err != nil {
		return nil, nil, err
	}
	dlvChan, err := c.getConsumeChannel()
	if err != nil {
		return nil, nil, err
	}
	jobChan := make(chan Job)
	errChan := make(chan error)

	go func() {
		defer func() {
			_ = c.channel.Close()
			c.channel = nil
		}()
		defer close(jobChan)
		defer close(errChan)
		for {
			err = c.consume(dlvChan, closeChan, jobChan)
			// if consume returns without an error, means that it was terminated
			// properly, otherwise something went wrong and it needs to restart
			if err == nil {
				log.Printf("EOF consuming for: %s", c.ID())
				return
			}
			errChan <- err
			log.Println("stopped consuming jobs:", err)
			closeChan = c.bindWithRetry()
			dlvChan, err = c.getConsumeChannel()
			for err != nil {
				time.Sleep(5 * time.Second)
				dlvChan, err = c.getConsumeChannel()
			}
		}
	}()

	return jobChan, errChan, nil
}

func (c *consumer) consume(dlvChan <-chan amqp.Delivery, closeChan <-chan *amqp.Error, jobChan chan<- Job) error {
	for {
		select {
		case d := <-dlvChan:
			j := Job{}
			err := json.Unmarshal(d.Body, &j)
			if err != nil {
				return fmt.Errorf("failed to unmarshal PDU: %v", err)
			}
			j.delivery = &d
			jobChan <- j
		case err := <-closeChan:
			return err
		case <-c.ctx.Done():
			return nil
		}
	}
}

func (c *consumer) Stop() error {
	if c.channel == nil {
		return nil
	}
	// Sends the stop signal
	c.cancel()
	return nil
}
<commit_msg>Use them in the other constructor as well<commit_after>package rabbitsmpp

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/streadway/amqp"
	"golang.org/x/net/context"
)

const (
	defaultPrefetchCount = 20
	defaultPrefetchSize  = 0
	defaultGlobalQos     = false
)

type ConsumeOption struct {
	f func(*consumeOptions)
}

type consumeOptions struct {
	prefetchCount int
	prefetchSize  int
	globalQos     bool
}

func SetPrefetchCount(n int) ConsumeOption {
	return ConsumeOption{func(o *consumeOptions) {
		o.prefetchCount = n
	}}
}

func SetPrefetchSize(n int) ConsumeOption {
	return ConsumeOption{func(o *consumeOptions) {
		o.prefetchSize = n
	}}
}

func SetGlobalQos(a bool) ConsumeOption {
	return ConsumeOption{func(o *consumeOptions) {
		o.globalQos = a
	}}
}

type Consumer interface {
	Consume() (<-chan Job, <-chan error, error)
	Stop() error
	ID() string
}

type consumer struct {
	*client
	channel       Channel
	ctx           context.Context
	cancel        context.CancelFunc
	prefetchCount int
	prefetchSize  int
	globalQos     bool
}

func NewConsumer(conf Config, options ...ConsumeOption) (Consumer, error) {
	c := NewClient(conf).(*client)
	ctx, cancel := context.WithCancel(context.Background())
	o := consumeOptions{
		prefetchCount: defaultPrefetchCount,
		prefetchSize:  defaultPrefetchSize,
		globalQos:     defaultGlobalQos,
	}
	for _, option := range options {
		option.f(&o)
	}

	return &consumer{
		client:        c,
		channel:       nil,
		ctx:           ctx,
		cancel:        cancel,
		prefetchCount: o.prefetchCount,
		prefetchSize:  o.prefetchSize,
		globalQos:     o.globalQos,
	}, nil
}

func newConsumerWithContext(ctx context.Context, conf Config, options ...ConsumeOption) (Consumer, error) {
	c := NewClient(conf).(*client)
	ctx, cancel := context.WithCancel(ctx)
	o := consumeOptions{
		prefetchCount: defaultPrefetchCount,
		prefetchSize:  defaultPrefetchSize,
		globalQos:     defaultGlobalQos,
	}
	for _, option := range options {
		option.f(&o)
	}

	return &consumer{
		client:        c,
		channel:       nil,
		ctx:           ctx,
		cancel:        cancel,
		prefetchCount: o.prefetchCount,
		prefetchSize:  o.prefetchSize,
		globalQos:     o.globalQos,
	}, nil
}

func (c *consumer) ID() string {
	return c.client.QueueName()
}

func (c *consumer) bindWithRetry() chan *amqp.Error {
	closeChan, err := c.Bind()
	for err != nil {
		log.Println("Failed to bind consumer:", err)
		time.Sleep(5 * time.Second)
		closeChan, err = c.Bind()
	}
	return closeChan
}

func (c *consumer) getConsumeChannel() (<-chan amqp.Delivery, error) {
	ch, err := c.Channel()
	if err != nil {
		return nil, err
	}
	err = ch.Qos(c.prefetchCount, c.prefetchSize, c.globalQos)
	if err != nil {
		return nil, err
	}

	c.channel = ch

	q, err := c.channel.QueueDeclare(
		c.QueueName(), // name
		true,          // durable
		false,         // delete when unused
		false,         // exclusive
		false,         // no-wait
		nil,           // arguments
	)
	if err != nil {
		return nil, err
	}

	return c.channel.Consume(
		q.Name, // queue
		"",     // consumer
		false,  // auto-ackey
		false,  // exclusive
		false,  // no-local
		false,  // no-wait
		nil,    // args
	)
}

func (c *consumer) Consume() (<-chan Job, <-chan error, error) {
	if c.channel != nil {
		return nil, nil, errors.New("consumer already active")
	}
	closeChan, err := c.Bind()
	if err != nil {
		return nil, nil, err
	}
	dlvChan, err := c.getConsumeChannel()
	if err != nil {
		return nil, nil, err
	}
	jobChan := make(chan Job)
	errChan := make(chan error)

	go func() {
		defer func() {
			_ = c.channel.Close()
			c.channel = nil
		}()
		defer close(jobChan)
		defer close(errChan)
		for {
			err = c.consume(dlvChan, closeChan, jobChan)
			// if consume returns without an error, means that it was terminated
			// properly, otherwise something went wrong and it needs to restart
			if err == nil {
				log.Printf("EOF consuming for: %s", c.ID())
				return
			}
			errChan <- err
			log.Println("stopped consuming jobs:", err)
			closeChan = c.bindWithRetry()
			dlvChan, err = c.getConsumeChannel()
			for err != nil {
				time.Sleep(5 * time.Second)
				dlvChan, err = c.getConsumeChannel()
			}
		}
	}()

	return jobChan, errChan, nil
}

func (c *consumer) consume(dlvChan <-chan amqp.Delivery, closeChan <-chan *amqp.Error, jobChan chan<- Job) error {
	for {
		select {
		case d := <-dlvChan:
			j := Job{}
			err := json.Unmarshal(d.Body, &j)
			if err != nil {
				return fmt.Errorf("failed to unmarshal PDU: %v", err)
			}
			j.delivery = &d
			jobChan <- j
		case err := <-closeChan:
			return err
		case <-c.ctx.Done():
			return nil
		}
	}
}

func (c *consumer) Stop() error {
	if c.channel == nil {
		return nil
	}
	// Sends the stop signal
	c.cancel()
	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net"
	"strings"

	pb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
	"github.com/golang/protobuf/proto"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/peer"
	"google.golang.org/grpc/status"

	"github.com/buchgr/bazel-remote/cache"
)

var (
	// This is an Internal error rather than InvalidArgument because
	// we modify incoming ActionResults to make them non-zero.
	errEmptyActionResult = status.Error(codes.Internal,
		"rejecting empty ActionResult")
)

const (
	// gRPC by default rejects messages larger than 4M.
	// Inline a little less than this, enough so we don't
	// need to worry about serialization overhead.
	maxInlineSize = 3 * 1024 * 1024 // 3M
)

// ActionCache interface:

func (s *grpcServer) GetActionResult(ctx context.Context,
	req *pb.GetActionResultRequest) (*pb.ActionResult, error) {

	logPrefix := "GRPC AC GET"
	err := s.validateHash(req.ActionDigest.Hash, req.ActionDigest.SizeBytes, logPrefix)
	if err != nil {
		return nil, err
	}

	// Clients provides hash and size of the Action, but not size of the ActionResult
	// checked by the the disk cache.
	const unknownActionResultSize = -1

	if !s.depsCheck {
		logPrefix = "GRPC AC GET NODEPSCHECK"

		rdr, sizeBytes, err := s.cache.Get(cache.AC, req.ActionDigest.Hash, unknownActionResultSize)
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Unknown, err.Error())
		}
		if rdr == nil || sizeBytes <= 0 {
			s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, "NOT FOUND")
			return nil, status.Error(codes.NotFound,
				fmt.Sprintf("%s not found in AC", req.ActionDigest.Hash))
		}

		acdata, err := ioutil.ReadAll(rdr)
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Unknown, err.Error())
		}

		result := &pb.ActionResult{}
		err = proto.Unmarshal(acdata, result)
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Unknown, err.Error())
		}

		s.accessLogger.Printf("%s %s OK", logPrefix, req.ActionDigest.Hash)
		return result, nil
	}

	result, _, err := s.cache.GetValidatedActionResult(req.ActionDigest.Hash)
	if err != nil {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
		return nil, status.Error(codes.Unknown, err.Error())
	}

	if result == nil {
		s.accessLogger.Printf("%s %s NOT FOUND", logPrefix, req.ActionDigest.Hash)
		return nil, status.Error(codes.NotFound,
			fmt.Sprintf("%s not found in AC", req.ActionDigest.Hash))
	}

	// Don't inline stdout/stderr/output files unless they were requested.

	var inlinedSoFar int64

	err = s.maybeInline(req.InlineStdout,
		&result.StdoutRaw, &result.StdoutDigest, &inlinedSoFar)
	if err != nil {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
		return nil, status.Error(codes.Unknown, err.Error())
	}

	err = s.maybeInline(req.InlineStderr,
		&result.StderrRaw, &result.StderrDigest, &inlinedSoFar)
	if err != nil {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
		return nil, status.Error(codes.Unknown, err.Error())
	}

	inlinableFiles := make(map[string]struct{}, len(req.InlineOutputFiles))
	for _, p := range req.InlineOutputFiles {
		inlinableFiles[p] = struct{}{}
	}
	for _, of := range result.GetOutputFiles() {
		_, ok := inlinableFiles[of.Path]
		err = s.maybeInline(ok, &of.Contents, &of.Digest, &inlinedSoFar)
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Unknown, err.Error())
		}
	}

	s.accessLogger.Printf("GRPC AC GET %s OK", req.ActionDigest.Hash)

	return result, nil
}

func (s *grpcServer) maybeInline(inline bool, slice *[]byte, digest **pb.Digest, inlinedSoFar *int64) error {

	if (*inlinedSoFar + int64(len(*slice))) > maxInlineSize {
		inline = false
	} else if digest != nil && *digest != nil &&
		(*inlinedSoFar+(*digest).SizeBytes) > maxInlineSize {
		inline = false
	}

	if !inline {
		if len(*slice) == 0 {
			return nil // Not inlined, nothing to do.
		}

		if *digest == nil {
			hash := sha256.Sum256(*slice)
			*digest = &pb.Digest{
				Hash:      hex.EncodeToString(hash[:]),
				SizeBytes: int64(len(*slice)),
			}
		}

		found, _ := s.cache.Contains(cache.CAS, (*digest).Hash, (*digest).SizeBytes)
		if !found {
			err := s.cache.Put(cache.CAS, (*digest).Hash, (*digest).SizeBytes,
				bytes.NewReader(*slice))
			if err != nil {
				return err
			}
		}

		*slice = []byte{}
		return nil
	}

	if len(*slice) > 0 {
		*inlinedSoFar += int64(len(*slice))
		return nil // Already inlined.
	}

	if digest == nil || *digest == nil || (*digest).SizeBytes == 0 {
		return nil // Nothing to inline?
	}

	// Otherwise, attempt to inline.
	if (*digest).SizeBytes > 0 {
		data, err := s.getBlobData((*digest).Hash, (*digest).SizeBytes)
		if err != nil {
			return err
		}
		*slice = data
		*inlinedSoFar += (*digest).SizeBytes
	}

	return nil
}

func (s *grpcServer) UpdateActionResult(ctx context.Context,
	req *pb.UpdateActionResultRequest) (*pb.ActionResult, error) {

	logPrefix := "GRPC AC PUT"
	err := s.validateHash(req.ActionDigest.Hash, req.ActionDigest.SizeBytes, logPrefix)
	if err != nil {
		return nil, err
	}

	// Ensure that the serialized ActionResult has non-zero length.
	addWorkerMetadataGRPC(ctx, req.ActionResult)

	data, err := proto.Marshal(req.ActionResult)
	if err != nil {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
		return nil, status.Error(codes.Internal, err.Error())
	}

	if len(data) == 0 {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash,
			errEmptyActionResult.Error())
		return nil, errEmptyActionResult
	}

	err = s.cache.Put(cache.AC, req.ActionDigest.Hash,
		int64(len(data)), bytes.NewReader(data))
	if err != nil {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
		return nil, status.Error(codes.Internal, err.Error())
	}

	// Also cache any inlined blobs, separately in the CAS.
	//
	// TODO: consider normalizing what we store in the AC (store all results
	// inlined? or de-inline all results?)

	for _, f := range req.ActionResult.OutputFiles {
		if f != nil && len(f.Contents) > 0 {
			err = s.cache.Put(cache.CAS, f.Digest.Hash,
				f.Digest.SizeBytes, bytes.NewReader(f.Contents))
			if err != nil {
				s.accessLogger.Printf("%s %s %s", logPrefix,
					req.ActionDigest.Hash, err)
				return nil, status.Error(codes.Internal, err.Error())
			}
		}
	}

	if len(req.ActionResult.StdoutRaw) > 0 {
		var hash string
		var sizeBytes int64
		if req.ActionResult.StdoutDigest != nil {
			hash = req.ActionResult.StdoutDigest.Hash
			sizeBytes = req.ActionResult.StdoutDigest.SizeBytes
		} else {
			hashData := sha256.Sum256(req.ActionResult.StdoutRaw)
			hash = hex.EncodeToString(hashData[:])
			sizeBytes = int64(len(req.ActionResult.StdoutRaw))
		}

		err = s.cache.Put(cache.CAS, hash, sizeBytes,
			bytes.NewReader(req.ActionResult.StdoutRaw))
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix,
				req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Internal, err.Error())
		}
	}

	if len(req.ActionResult.StderrRaw) > 0 {
		var hash string
		var sizeBytes int64
		if req.ActionResult.StderrDigest != nil {
			hash = req.ActionResult.StderrDigest.Hash
			sizeBytes = req.ActionResult.StderrDigest.SizeBytes
		} else {
			hashData := sha256.Sum256(req.ActionResult.StderrRaw)
			hash = hex.EncodeToString(hashData[:])
			sizeBytes = int64(len(req.ActionResult.StderrRaw))
		}

		err = s.cache.Put(cache.CAS, hash, sizeBytes,
			bytes.NewReader(req.ActionResult.StderrRaw))
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix,
				req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Internal, err.Error())
		}
	}

	s.accessLogger.Printf("GRPC AC PUT %s OK", req.ActionDigest.Hash)

	// Trivia: the RE API wants us to return the ActionResult from the
	// request, in order to follow this standard method style guide:
	// https://cloud.google.com/apis/design/standard_methods
	return req.ActionResult, nil
}

func addWorkerMetadataGRPC(ctx context.Context, ar *pb.ActionResult) {
	if ar.ExecutionMetadata == nil {
		ar.ExecutionMetadata = &pb.ExecutedActionMetadata{}
	} else if ar.ExecutionMetadata.Worker != "" {
		return
	}

	p, ok := peer.FromContext(ctx)
	if !ok {
		ar.ExecutionMetadata.Worker = "unknown"
		return
	}

	addr := p.Addr.String()

	if addr == "" {
		ar.ExecutionMetadata.Worker = "unknown"
		return
	}

	if !strings.ContainsAny(addr, ":") {
		// The addr in our unit tests is "bufconn".
		ar.ExecutionMetadata.Worker = addr
		return
	}

	worker, _, err := net.SplitHostPort(addr)
	if err != nil {
		ar.ExecutionMetadata.Worker = addr
		return
	}

	ar.ExecutionMetadata.Worker = worker
}
<commit_msg>grpc: don't panic if we receive an ActionResult with nil output file digests<commit_after>package server

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net"
	"strings"

	pb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
	"github.com/golang/protobuf/proto"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/peer"
	"google.golang.org/grpc/status"

	"github.com/buchgr/bazel-remote/cache"
)

var (
	// This is an Internal error rather than InvalidArgument because
	// we modify incoming ActionResults to make them non-zero.
	errEmptyActionResult = status.Error(codes.Internal,
		"rejecting empty ActionResult")
)

const (
	// gRPC by default rejects messages larger than 4M.
	// Inline a little less than this, enough so we don't
	// need to worry about serialization overhead.
	maxInlineSize = 3 * 1024 * 1024 // 3M
)

// ActionCache interface:

func (s *grpcServer) GetActionResult(ctx context.Context,
	req *pb.GetActionResultRequest) (*pb.ActionResult, error) {

	logPrefix := "GRPC AC GET"
	err := s.validateHash(req.ActionDigest.Hash, req.ActionDigest.SizeBytes, logPrefix)
	if err != nil {
		return nil, err
	}

	// Clients provides hash and size of the Action, but not size of the ActionResult
	// checked by the the disk cache.
	const unknownActionResultSize = -1

	if !s.depsCheck {
		logPrefix = "GRPC AC GET NODEPSCHECK"

		rdr, sizeBytes, err := s.cache.Get(cache.AC, req.ActionDigest.Hash, unknownActionResultSize)
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Unknown, err.Error())
		}
		if rdr == nil || sizeBytes <= 0 {
			s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, "NOT FOUND")
			return nil, status.Error(codes.NotFound,
				fmt.Sprintf("%s not found in AC", req.ActionDigest.Hash))
		}

		acdata, err := ioutil.ReadAll(rdr)
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Unknown, err.Error())
		}

		result := &pb.ActionResult{}
		err = proto.Unmarshal(acdata, result)
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Unknown, err.Error())
		}

		s.accessLogger.Printf("%s %s OK", logPrefix, req.ActionDigest.Hash)
		return result, nil
	}

	result, _, err := s.cache.GetValidatedActionResult(req.ActionDigest.Hash)
	if err != nil {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
		return nil, status.Error(codes.Unknown, err.Error())
	}

	if result == nil {
		s.accessLogger.Printf("%s %s NOT FOUND", logPrefix, req.ActionDigest.Hash)
		return nil, status.Error(codes.NotFound,
			fmt.Sprintf("%s not found in AC", req.ActionDigest.Hash))
	}

	// Don't inline stdout/stderr/output files unless they were requested.

	var inlinedSoFar int64

	err = s.maybeInline(req.InlineStdout,
		&result.StdoutRaw, &result.StdoutDigest, &inlinedSoFar)
	if err != nil {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
		return nil, status.Error(codes.Unknown, err.Error())
	}

	err = s.maybeInline(req.InlineStderr,
		&result.StderrRaw, &result.StderrDigest, &inlinedSoFar)
	if err != nil {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
		return nil, status.Error(codes.Unknown, err.Error())
	}

	inlinableFiles := make(map[string]struct{}, len(req.InlineOutputFiles))
	for _, p := range req.InlineOutputFiles {
		inlinableFiles[p] = struct{}{}
	}
	for _, of := range result.GetOutputFiles() {
		_, ok := inlinableFiles[of.Path]
		err = s.maybeInline(ok, &of.Contents, &of.Digest, &inlinedSoFar)
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Unknown, err.Error())
		}
	}

	s.accessLogger.Printf("GRPC AC GET %s OK", req.ActionDigest.Hash)

	return result, nil
}

func (s *grpcServer) maybeInline(inline bool, slice *[]byte, digest **pb.Digest, inlinedSoFar *int64) error {

	if (*inlinedSoFar + int64(len(*slice))) > maxInlineSize {
		inline = false
	} else if digest != nil && *digest != nil &&
		(*inlinedSoFar+(*digest).SizeBytes) > maxInlineSize {
		inline = false
	}

	if !inline {
		if len(*slice) == 0 {
			return nil // Not inlined, nothing to do.
		}

		if *digest == nil {
			hash := sha256.Sum256(*slice)
			*digest = &pb.Digest{
				Hash:      hex.EncodeToString(hash[:]),
				SizeBytes: int64(len(*slice)),
			}
		}

		found, _ := s.cache.Contains(cache.CAS, (*digest).Hash, (*digest).SizeBytes)
		if !found {
			err := s.cache.Put(cache.CAS, (*digest).Hash, (*digest).SizeBytes,
				bytes.NewReader(*slice))
			if err != nil {
				return err
			}
		}

		*slice = []byte{}
		return nil
	}

	if len(*slice) > 0 {
		*inlinedSoFar += int64(len(*slice))
		return nil // Already inlined.
	}

	if digest == nil || *digest == nil || (*digest).SizeBytes == 0 {
		return nil // Nothing to inline?
	}

	// Otherwise, attempt to inline.
	if (*digest).SizeBytes > 0 {
		data, err := s.getBlobData((*digest).Hash, (*digest).SizeBytes)
		if err != nil {
			return err
		}
		*slice = data
		*inlinedSoFar += (*digest).SizeBytes
	}

	return nil
}

func (s *grpcServer) UpdateActionResult(ctx context.Context,
	req *pb.UpdateActionResultRequest) (*pb.ActionResult, error) {

	logPrefix := "GRPC AC PUT"
	err := s.validateHash(req.ActionDigest.Hash, req.ActionDigest.SizeBytes, logPrefix)
	if err != nil {
		return nil, err
	}

	// Ensure that the serialized ActionResult has non-zero length.
	addWorkerMetadataGRPC(ctx, req.ActionResult)

	data, err := proto.Marshal(req.ActionResult)
	if err != nil {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
		return nil, status.Error(codes.Internal, err.Error())
	}

	if len(data) == 0 {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash,
			errEmptyActionResult.Error())
		return nil, errEmptyActionResult
	}

	err = s.cache.Put(cache.AC, req.ActionDigest.Hash,
		int64(len(data)), bytes.NewReader(data))
	if err != nil {
		s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
		return nil, status.Error(codes.Internal, err.Error())
	}

	// Also cache any inlined blobs, separately in the CAS.
	//
	// TODO: consider normalizing what we store in the AC (store all results
	// inlined? or de-inline all results?)

	for _, f := range req.ActionResult.OutputFiles {
		if f != nil && len(f.Contents) > 0 {

			if f.Digest == nil {
				hashData := sha256.Sum256(f.Contents)
				f.Digest = &pb.Digest{
					Hash:      hex.EncodeToString(hashData[:]),
					SizeBytes: int64(len(f.Contents)),
				}
			}

			err = s.cache.Put(cache.CAS, f.Digest.Hash,
				f.Digest.SizeBytes, bytes.NewReader(f.Contents))
			if err != nil {
				s.accessLogger.Printf("%s %s %s", logPrefix,
					req.ActionDigest.Hash, err)
				return nil, status.Error(codes.Internal, err.Error())
			}
		}
	}

	if len(req.ActionResult.StdoutRaw) > 0 {
		var hash string
		var sizeBytes int64
		if req.ActionResult.StdoutDigest != nil {
			hash = req.ActionResult.StdoutDigest.Hash
			sizeBytes = req.ActionResult.StdoutDigest.SizeBytes
		} else {
			hashData := sha256.Sum256(req.ActionResult.StdoutRaw)
			hash = hex.EncodeToString(hashData[:])
			sizeBytes = int64(len(req.ActionResult.StdoutRaw))
		}

		err = s.cache.Put(cache.CAS, hash, sizeBytes,
			bytes.NewReader(req.ActionResult.StdoutRaw))
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix,
				req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Internal, err.Error())
		}
	}

	if len(req.ActionResult.StderrRaw) > 0 {
		var hash string
		var sizeBytes int64
		if req.ActionResult.StderrDigest != nil {
			hash = req.ActionResult.StderrDigest.Hash
			sizeBytes = req.ActionResult.StderrDigest.SizeBytes
		} else {
			hashData := sha256.Sum256(req.ActionResult.StderrRaw)
			hash = hex.EncodeToString(hashData[:])
			sizeBytes = int64(len(req.ActionResult.StderrRaw))
		}

		err = s.cache.Put(cache.CAS, hash, sizeBytes,
			bytes.NewReader(req.ActionResult.StderrRaw))
		if err != nil {
			s.accessLogger.Printf("%s %s %s", logPrefix,
				req.ActionDigest.Hash, err)
			return nil, status.Error(codes.Internal, err.Error())
		}
	}

	s.accessLogger.Printf("GRPC AC PUT %s OK", req.ActionDigest.Hash)

	// Trivia: the RE API wants us to return the ActionResult from the
	// request, in order to follow this standard method style guide:
	// https://cloud.google.com/apis/design/standard_methods
	return req.ActionResult, nil
}

func addWorkerMetadataGRPC(ctx context.Context, ar *pb.ActionResult) {
	if ar.ExecutionMetadata == nil {
		ar.ExecutionMetadata = &pb.ExecutedActionMetadata{}
	} else if ar.ExecutionMetadata.Worker != "" {
		return
	}

	p, ok := peer.FromContext(ctx)
	if !ok {
		ar.ExecutionMetadata.Worker = "unknown"
		return
	}

	addr := p.Addr.String()

	if addr == "" {
		ar.ExecutionMetadata.Worker = "unknown"
		return
	}

	if !strings.ContainsAny(addr, ":") {
		// The addr in our unit tests is "bufconn".
		ar.ExecutionMetadata.Worker = addr
		return
	}

	worker, _, err := net.SplitHostPort(addr)
	if err != nil {
		ar.ExecutionMetadata.Worker = addr
		return
	}

	ar.ExecutionMetadata.Worker = worker
}
<|endoftext|>


<commit_before>// Package text supports marshalling Cap'n Proto as text based on a schema.
package text

import (
	"bytes"
	"fmt"
	"strconv"

	"zombiezen.com/go/capnproto2"
	"zombiezen.com/go/capnproto2/internal/schema"
)

// AppendStruct appends the text representation of s to dst.
func AppendStruct(dst []byte, s capnp.Struct, typeID uint64, schemaData []byte) ([]byte, error) {
	msg, err := capnp.Unmarshal(schemaData)
	if err != nil {
		return nil, err
	}
	req, err := schema.ReadRootCodeGeneratorRequest(msg)
	if err != nil {
		return nil, err
	}
	nodes, err := req.Nodes()
	if err != nil {
		return nil, err
	}
	nodeMap := make(map[uint64]schema.Node, nodes.Len())
	for i := 0; i < nodes.Len(); i++ {
		n := nodes.At(i)
		nodeMap[n.Id()] = n
	}
	m := &marshaller{
		buf:   bytes.NewBuffer(dst),
		nodes: nodeMap,
	}
	err = m.marshalStruct(typeID, s)
	return m.buf.Bytes(), err
}

type marshaller struct {
	buf   *bytes.Buffer
	tmp   []byte
	nodes map[uint64]schema.Node
}

func (m *marshaller) marshalBool(v bool) {
	if v {
		m.buf.WriteString("true")
	} else {
		m.buf.WriteString("false")
	}
}

func (m *marshaller) marshalInt(i int64) {
	m.tmp = strconv.AppendInt(m.tmp[:0], i, 10)
	m.buf.Write(m.tmp)
}

func (m *marshaller) marshalUint(i uint64) {
	m.tmp = strconv.AppendUint(m.tmp[:0], i, 10)
	m.buf.Write(m.tmp)
}

func (m *marshaller) marshalText(t []byte) {
	m.buf.WriteByte('"')
	last := 0
	for i, b := range t {
		if !needsEscape(b) {
			continue
		}
		m.buf.Write(t[last:i])
		switch b {
		case '\a':
			m.buf.WriteString("\\a")
		case '\b':
			m.buf.WriteString("\\b")
		case '\f':
			m.buf.WriteString("\\f")
		case '\n':
			m.buf.WriteString("\\n")
		case '\r':
			m.buf.WriteString("\\r")
		case '\t':
			m.buf.WriteString("\\t")
		case '\v':
			m.buf.WriteString("\\v")
		case '\'':
			m.buf.WriteString("\\'")
		case '"':
			m.buf.WriteString("\\\"")
		case '\\':
			m.buf.WriteString("\\\\")
		default:
			m.buf.WriteString("\\x")
			m.buf.WriteByte(hexDigit(b / 16))
			m.buf.WriteByte(hexDigit(b % 16))
		}
		last = i + 1
	}
	m.buf.Write(t[last:])
	m.buf.WriteByte('"')
}

func needsEscape(b byte) bool {
	return b < 0x20 || b >= 0x7f
}

func hexDigit(b byte) byte {
	const digits = "0123456789abcdef"
	return digits[b]
}

func (m *marshaller) marshalStruct(typeID uint64, s capnp.Struct) error {
	n := m.nodes[typeID]
	if !n.IsValid() || n.Which() != schema.Node_Which_structNode {
		return fmt.Errorf("cannot find struct type %#x", typeID)
	}
	var discriminant uint16
	if n.StructNode().DiscriminantCount() > 0 {
		discriminant = s.Uint16(capnp.DataOffset(n.StructNode().DiscriminantOffset() * 2))
	}
	m.buf.WriteByte('(')
	fields := codeOrderFields(n.StructNode())
	first := true
	for _, f := range fields {
		if !(f.Which() == schema.Field_Which_slot || f.Which() == schema.Field_Which_group) {
			continue
		}
		if dv := f.DiscriminantValue(); !(dv == schema.Field_noDiscriminant || dv == discriminant) {
			continue
		}
		if !first {
			m.buf.WriteString(", ")
		}
		first = false
		name, err := f.NameBytes()
		if err != nil {
			return err
		}
		m.buf.Write(name)
		m.buf.WriteString(" = ")
		switch f.Which() {
		case schema.Field_Which_slot:
			if err := m.marshalFieldValue(s, f); err != nil {
				return err
			}
		case schema.Field_Which_group:
			if err := m.marshalStruct(f.Group().TypeId(), s); err != nil {
				return err
			}
		}
	}
	m.buf.WriteByte(')')
	return nil
}

func (m *marshaller) marshalFieldValue(s capnp.Struct, f schema.Field) error {
	typ, err := f.Slot().Type()
	if err != nil {
		return err
	}
	dv, err := f.Slot().DefaultValue()
	if err != nil {
		return err
	}
	if dv.IsValid() && int(typ.Which()) != int(dv.Which()) {
		name, _ := f.Name()
		return fmt.Errorf("marshal field %s: default value is a %v, want %v", name, dv.Which(), typ.Which())
	}
	switch typ.Which() {
	case schema.Type_Which_void:
		m.buf.WriteString("void")
	case schema.Type_Which_bool:
		v := s.Bit(capnp.BitOffset(f.Slot().Offset()))
		d := dv.Bool()
		m.marshalBool(!d && v || d && !v)
	case schema.Type_Which_int8:
		v := s.Uint8(capnp.DataOffset(f.Slot().Offset()))
		d := uint8(dv.Int8())
		m.marshalInt(int64(int8(v ^ d)))
	case schema.Type_Which_int16:
		v := s.Uint16(capnp.DataOffset(f.Slot().Offset() * 2))
		d := uint16(dv.Int16())
		m.marshalInt(int64(int16(v ^ d)))
	case schema.Type_Which_int32:
		v := s.Uint32(capnp.DataOffset(f.Slot().Offset() * 4))
		d := uint32(dv.Int32())
		m.marshalInt(int64(int32(v ^ d)))
	case schema.Type_Which_int64:
		v := s.Uint64(capnp.DataOffset(f.Slot().Offset() * 8))
		d := uint64(dv.Int64())
		m.marshalInt(int64(v ^ d))
	case schema.Type_Which_structType:
		p, err := s.Ptr(uint16(f.Slot().Offset()))
		if err != nil {
			return err
		}
		if !p.IsValid() {
			p, _ = dv.StructValuePtr()
		}
		return m.marshalStruct(typ.StructType().TypeId(), p.Struct())
	case schema.Type_Which_data:
		p, err := s.Ptr(uint16(f.Slot().Offset()))
		if err != nil {
			return err
		}
		if !p.IsValid() {
			b, _ := dv.Data()
			m.marshalText(b)
			return nil
		}
		m.marshalText(p.Data())
	case schema.Type_Which_text:
		p, err := s.Ptr(uint16(f.Slot().Offset()))
		if err != nil {
			return err
		}
		if !p.IsValid() {
			b, _ := dv.TextBytes()
			m.marshalText(b)
			return nil
		}
		b := p.Data()
		if len(b) > 0 {
			// Trim NUL byte
			b = b[:len(b)-1]
		}
		m.marshalText(b)
	}
	return nil
}

func codeOrderFields(s schema.Node_structNode) []schema.Field {
	list, _ := s.Fields()
	n := list.Len()
	fields := make([]schema.Field, n)
	for i := 0; i < n; i++ {
		f := list.At(i)
		fields[f.CodeOrder()] = f
	}
	return fields
}
<commit_msg>encoding/text: add unsigned integers<commit_after>// Package text supports marshalling Cap'n Proto as text based on a schema.
package text

import (
	"bytes"
	"fmt"
	"strconv"

	"zombiezen.com/go/capnproto2"
	"zombiezen.com/go/capnproto2/internal/schema"
)

// AppendStruct appends the text representation of s to dst.
func AppendStruct(dst []byte, s capnp.Struct, typeID uint64, schemaData []byte) ([]byte, error) {
	msg, err := capnp.Unmarshal(schemaData)
	if err != nil {
		return nil, err
	}
	req, err := schema.ReadRootCodeGeneratorRequest(msg)
	if err != nil {
		return nil, err
	}
	nodes, err := req.Nodes()
	if err != nil {
		return nil, err
	}
	nodeMap := make(map[uint64]schema.Node, nodes.Len())
	for i := 0; i < nodes.Len(); i++ {
		n := nodes.At(i)
		nodeMap[n.Id()] = n
	}
	m := &marshaller{
		buf:   bytes.NewBuffer(dst),
		nodes: nodeMap,
	}
	err = m.marshalStruct(typeID, s)
	return m.buf.Bytes(), err
}

type marshaller struct {
	buf   *bytes.Buffer
	tmp   []byte
	nodes map[uint64]schema.Node
}

func (m *marshaller) marshalBool(v bool) {
	if v {
		m.buf.WriteString("true")
	} else {
		m.buf.WriteString("false")
	}
}

func (m *marshaller) marshalInt(i int64) {
	m.tmp = strconv.AppendInt(m.tmp[:0], i, 10)
	m.buf.Write(m.tmp)
}

func (m *marshaller) marshalUint(i uint64) {
	m.tmp = strconv.AppendUint(m.tmp[:0], i, 10)
	m.buf.Write(m.tmp)
}

func (m *marshaller) marshalText(t []byte) {
	m.buf.WriteByte('"')
	last := 0
	for i, b := range t {
		if !needsEscape(b) {
			continue
		}
		m.buf.Write(t[last:i])
		switch b {
		case '\a':
			m.buf.WriteString("\\a")
		case '\b':
			m.buf.WriteString("\\b")
		case '\f':
			m.buf.WriteString("\\f")
		case '\n':
			m.buf.WriteString("\\n")
		case '\r':
			m.buf.WriteString("\\r")
		case '\t':
			m.buf.WriteString("\\t")
		case '\v':
			m.buf.WriteString("\\v")
		case '\'':
			m.buf.WriteString("\\'")
		case '"':
			m.buf.WriteString("\\\"")
		case '\\':
			m.buf.WriteString("\\\\")
		default:
			m.buf.WriteString("\\x")
			m.buf.WriteByte(hexDigit(b / 16))
			m.buf.WriteByte(hexDigit(b % 16))
		}
		last = i + 1
	}
	m.buf.Write(t[last:])
	m.buf.WriteByte('"')
}

func needsEscape(b byte) bool {
	return b < 0x20 || b >= 0x7f
}

func hexDigit(b byte) byte {
	const digits = "0123456789abcdef"
	return digits[b]
}

func (m *marshaller) marshalStruct(typeID uint64, s capnp.Struct) error {
	n := m.nodes[typeID]
	if !n.IsValid() || n.Which() != schema.Node_Which_structNode {
		return fmt.Errorf("cannot find struct type %#x", typeID)
	}
	var discriminant uint16
	if n.StructNode().DiscriminantCount() > 0 {
		discriminant = s.Uint16(capnp.DataOffset(n.StructNode().DiscriminantOffset() * 2))
	}
	m.buf.WriteByte('(')
	fields := codeOrderFields(n.StructNode())
	first := true
	for _, f := range fields {
		if !(f.Which() == schema.Field_Which_slot || f.Which() == schema.Field_Which_group) {
			continue
		}
		if dv := f.DiscriminantValue(); !(dv == schema.Field_noDiscriminant || dv == discriminant) {
			continue
		}
		if !first {
			m.buf.WriteString(", ")
		}
		first = false
		name, err := f.NameBytes()
		if err != nil {
			return err
		}
		m.buf.Write(name)
		m.buf.WriteString(" = ")
		switch f.Which() {
		case schema.Field_Which_slot:
			if err := m.marshalFieldValue(s, f); err != nil {
				return err
			}
		case schema.Field_Which_group:
			if err := m.marshalStruct(f.Group().TypeId(), s); err != nil {
				return err
			}
		}
	}
	m.buf.WriteByte(')')
	return nil
}

func (m *marshaller) marshalFieldValue(s capnp.Struct, f schema.Field) error {
	typ, err := f.Slot().Type()
	if err != nil {
		return err
	}
	dv, err := f.Slot().DefaultValue()
	if err != nil {
		return err
	}
	if dv.IsValid() && int(typ.Which()) != int(dv.Which()) {
		name, _ := f.Name()
		return fmt.Errorf("marshal field %s: default value is a %v, want %v", name, dv.Which(), typ.Which())
	}
	switch typ.Which() {
	case schema.Type_Which_void:
		m.buf.WriteString("void")
	case schema.Type_Which_bool:
		v := s.Bit(capnp.BitOffset(f.Slot().Offset()))
		d := dv.Bool()
		m.marshalBool(!d && v || d && !v)
	case schema.Type_Which_int8:
		v := s.Uint8(capnp.DataOffset(f.Slot().Offset()))
		d := uint8(dv.Int8())
		m.marshalInt(int64(int8(v ^ d)))
	case schema.Type_Which_int16:
		v := s.Uint16(capnp.DataOffset(f.Slot().Offset() * 2))
		d := uint16(dv.Int16())
		m.marshalInt(int64(int16(v ^ d)))
	case schema.Type_Which_int32:
		v := s.Uint32(capnp.DataOffset(f.Slot().Offset() * 4))
		d := uint32(dv.Int32())
		m.marshalInt(int64(int32(v ^ d)))
	case schema.Type_Which_int64:
		v := s.Uint64(capnp.DataOffset(f.Slot().Offset() * 8))
		d := uint64(dv.Int64())
		m.marshalInt(int64(v ^ d))
	case schema.Type_Which_uint8:
		v := s.Uint8(capnp.DataOffset(f.Slot().Offset()))
		d := dv.Uint8()
		m.marshalUint(uint64(v ^ d))
	case schema.Type_Which_uint16:
		v := s.Uint16(capnp.DataOffset(f.Slot().Offset() * 2))
		d := dv.Uint16()
		m.marshalUint(uint64(v ^ d))
	case schema.Type_Which_uint32:
		v := s.Uint32(capnp.DataOffset(f.Slot().Offset() * 4))
		d := dv.Uint32()
		m.marshalUint(uint64(v ^ d))
	case schema.Type_Which_int64:
		v := s.Uint64(capnp.DataOffset(f.Slot().Offset() * 8))
		d := dv.Uint64()
		m.marshalUint(v ^ d)
	case schema.Type_Which_structType:
		p, err := s.Ptr(uint16(f.Slot().Offset()))
		if err != nil {
			return err
		}
		if !p.IsValid() {
			p, _ = dv.StructValuePtr()
		}
		return m.marshalStruct(typ.StructType().TypeId(), p.Struct())
	case schema.Type_Which_data:
		p, err := s.Ptr(uint16(f.Slot().Offset()))
		if err != nil {
			return err
		}
		if !p.IsValid() {
			b, _ := dv.Data()
			m.marshalText(b)
			return nil
		}
		m.marshalText(p.Data())
	case schema.Type_Which_text:
		p, err := s.Ptr(uint16(f.Slot().Offset()))
		if err != nil {
			return err
		}
		if !p.IsValid() {
			b, _ := dv.TextBytes()
			m.marshalText(b)
			return nil
		}
		b := p.Data()
		if len(b) > 0 {
			// Trim NUL byte
			b = b[:len(b)-1]
		}
		m.marshalText(b)
	}
	return nil
}

func codeOrderFields(s schema.Node_structNode) []schema.Field {
	list, _ := s.Fields()
	n := list.Len()
	fields := make([]schema.Field, n)
	for i := 0; i < n; i++ {
		f := list.At(i)
		fields[f.CodeOrder()] = f
	}
	return fields
}
<|endoftext|>