<commit_before>package drivers

import "strings"

var queryMap = map[string]bool{
	"WITH":   true,
	"PRAGMA": true,

	"EXPLAIN": true, // show the execution plan of a statement
	"FETCH":   true, // retrieve rows from a query using a cursor
	"SELECT":  true, // retrieve rows from a table or view
	"SHOW":    true, // show the value of a run-time parameter
	"VALUES":  true, // compute a set of rows

	"EXEC": true, // execute a stored procedure that returns rows (not postgres)
}

var execMap = map[string]bool{
	"USE": true,

	"ABORT":                            true, // abort the current transaction
	"ALTER AGGREGATE":                  true, // change the definition of an aggregate function
	"ALTER COLLATION":                  true, // change the definition of a collation
	"ALTER CONVERSION":                 true, // change the definition of a conversion
	"ALTER DATABASE":                   true, // change a database
	"ALTER DEFAULT PRIVILEGES":         true, // define default access privileges
	"ALTER DOMAIN":                     true, // change the definition of a domain
	"ALTER EVENT TRIGGER":              true, // change the definition of an event trigger
	"ALTER EXTENSION":                  true, // change the definition of an extension
	"ALTER FOREIGN DATA WRAPPER":       true, // change the definition of a foreign-data wrapper
	"ALTER FOREIGN TABLE":              true, // change the definition of a foreign table
	"ALTER FUNCTION":                   true, // change the definition of a function
	"ALTER GROUP":                      true, // change role name or membership
	"ALTER INDEX":                      true, // change the definition of an index
	"ALTER LANGUAGE":                   true, // change the definition of a procedural language
	"ALTER LARGE OBJECT":               true, // change the definition of a large object
	"ALTER MATERIALIZED VIEW":          true, // change the definition of a materialized view
	"ALTER OPERATOR":                   true, // change the definition of an operator
	"ALTER OPERATOR CLASS":             true, // change the definition of an operator class
	"ALTER OPERATOR FAMILY":            true, // change the definition of an operator family
	"ALTER POLICY":                     true, // change the definition of a row level security policy
	"ALTER ROLE":                       true, // change a database role
	"ALTER RULE":                       true, // change the definition of a rule
	"ALTER SCHEMA":                     true, // change the definition of a schema
	"ALTER SEQUENCE":                   true, // change the definition of a sequence generator
	"ALTER SERVER":                     true, // change the definition of a foreign server
	"ALTER SYSTEM":                     true, // change a server configuration parameter
	"ALTER TABLE":                      true, // change the definition of a table
	"ALTER TABLESPACE":                 true, // change the definition of a tablespace
	"ALTER TEXT SEARCH CONFIGURATION":  true, // change the definition of a text search configuration
	"ALTER TEXT SEARCH DICTIONARY":     true, // change the definition of a text search dictionary
	"ALTER TEXT SEARCH PARSER":         true, // change the definition of a text search parser
	"ALTER TEXT SEARCH TEMPLATE":       true, // change the definition of a text search template
	"ALTER TRIGGER":                    true, // change the definition of a trigger
	"ALTER TYPE":                       true, // change the definition of a type
	"ALTER USER":                       true, // change a database role
	"ALTER USER MAPPING":               true, // change the definition of a user mapping
	"ALTER VIEW":                       true, // change the definition of a view
	"ANALYZE":                          true, // collect statistics about a database
	"BEGIN":                            true, // start a transaction block
	"CHECKPOINT":                       true, // force a transaction log checkpoint
	"CLOSE":                            true, // close a cursor
	"CLUSTER":                          true, // cluster a table according to an index
	"COMMENT":                          true, // define or change the comment of an object
	"COMMIT":                           true, // commit the current transaction
	"COMMIT PREPARED":                  true, // commit a transaction that was earlier prepared for two-phase commit
	"COPY":                             true, // copy data between a file and a table
	"CREATE ACCESS METHOD":             true, // define a new access method
	"CREATE AGGREGATE":                 true, // define a new aggregate function
	"CREATE CAST":                      true, // define a new cast
	"CREATE COLLATION":                 true, // define a new collation
	"CREATE CONVERSION":                true, // define a new encoding conversion
	"CREATE DATABASE":                  true, // create a new database
	"CREATE DOMAIN":                    true, // define a new domain
	"CREATE EVENT TRIGGER":             true, // define a new event trigger
	"CREATE EXTENSION":                 true, // install an extension
	"CREATE FOREIGN DATA WRAPPER":      true, // define a new foreign-data wrapper
	"CREATE FOREIGN TABLE":             true, // define a new foreign table
	"CREATE FUNCTION":                  true, // define a new function
	"CREATE GROUP":                     true, // define a new database role
	"CREATE INDEX":                     true, // define a new index
	"CREATE LANGUAGE":                  true, // define a new procedural language
	"CREATE MATERIALIZED VIEW":         true, // define a new materialized view
	"CREATE OPERATOR":                  true, // define a new operator
	"CREATE OPERATOR CLASS":            true, // define a new operator class
	"CREATE OPERATOR FAMILY":           true, // define a new operator family
	"CREATE POLICY":                    true, // define a new row level security policy for a table
	"CREATE ROLE":                      true, // define a new database role
	"CREATE RULE":                      true, // define a new rewrite rule
	"CREATE SCHEMA":                    true, // define a new schema
	"CREATE SEQUENCE":                  true, // define a new sequence generator
	"CREATE SERVER":                    true, // define a new foreign server
	"CREATE TABLE":                     true, // define a new table
	"CREATE TABLE AS":                  true, // define a new table from the results of a query
	"CREATE TABLESPACE":                true, // define a new tablespace
	"CREATE TEXT SEARCH CONFIGURATION": true, // define a new text search configuration
	"CREATE TEXT SEARCH DICTIONARY":    true, // define a new text search dictionary
	"CREATE TEXT SEARCH PARSER":        true, // define a new text search parser
	"CREATE TEXT SEARCH TEMPLATE":      true, // define a new text search template
	"CREATE TRANSFORM":                 true, // define a new transform
	"CREATE TRIGGER":                   true, // define a new trigger
	"CREATE TYPE":                      true, // define a new data type
	"CREATE USER":                      true, // define a new database role
	"CREATE USER MAPPING":              true, // define a new mapping of a user to a foreign server
	"CREATE VIEW":                      true, // define a new view
	"DEALLOCATE":                       true, // deallocate a prepared statement
	"DECLARE":                          true, // define a cursor
	"DELETE":                           true, // delete rows of a table
	"DISCARD":                          true, // discard session state
	"DO":                               true, // execute an anonymous code block
	"DROP ACCESS METHOD":               true, // remove an access method
	"DROP AGGREGATE":                   true, // remove an aggregate function
	"DROP CAST":                        true, // remove a cast
	"DROP COLLATION":                   true, // remove a collation
	"DROP CONVERSION":                  true, // remove a conversion
	"DROP DATABASE":                    true, // remove a database
	"DROP DOMAIN":                      true, // remove a domain
	"DROP EVENT TRIGGER":               true, // remove an event trigger
	"DROP EXTENSION":                   true, // remove an extension
	"DROP FOREIGN DATA WRAPPER":        true, // remove a foreign-data wrapper
	"DROP FOREIGN TABLE":               true, // remove a foreign table
	"DROP FUNCTION":                    true, // remove a function
	"DROP GROUP":                       true, // remove a database role
	"DROP INDEX":                       true, // remove an index
	"DROP LANGUAGE":                    true, // remove a procedural language
	"DROP MATERIALIZED VIEW":           true, // remove a materialized view
	"DROP OPERATOR":                    true, // remove an operator
	"DROP OPERATOR CLASS":              true, // remove an operator class
	"DROP OPERATOR FAMILY":             true, // remove an operator family
	"DROP OWNED":                       true, // remove database objects owned by a database role
	"DROP POLICY":                      true, // remove a row level security policy from a table
	"DROP ROLE":                        true, // remove a database role
	"DROP RULE":                        true, // remove a rewrite rule
	"DROP SCHEMA":                      true, // remove a schema
	"DROP SEQUENCE":                    true, // remove a sequence
	"DROP SERVER":                      true, // remove a foreign server descriptor
	"DROP TABLE":                       true, // remove a table
	"DROP TABLESPACE":                  true, // remove a tablespace
	"DROP TEXT SEARCH CONFIGURATION":   true, // remove a text search configuration
	"DROP TEXT SEARCH DICTIONARY":      true, // remove a text search dictionary
	"DROP TEXT SEARCH PARSER":          true, // remove a text search parser
	"DROP TEXT SEARCH TEMPLATE":        true, // remove a text search template
	"DROP TRANSFORM":                   true, // remove a transform
	"DROP TRIGGER":                     true, // remove a trigger
	"DROP TYPE":                        true, // remove a data type
	"DROP USER":                        true, // remove a database role
	"DROP USER MAPPING":                true, // remove a user mapping for a foreign server
	"DROP VIEW":                        true, // remove a view
	"END":                              true, // commit the current transaction
	"EXECUTE":                          true, // execute a prepared statement
	"GRANT":                            true, // define access privileges
	"IMPORT FOREIGN SCHEMA":            true, // import table definitions from a foreign server
	"INSERT":                           true, // create new rows in a table
	"LISTEN":                           true, // listen for a notification
	"LOAD":                             true, // load a shared library file
	"LOCK":                             true, // lock a table
	"MOVE":                             true, // position a cursor
	"NOTIFY":                           true, // generate a notification
	"PREPARE":                          true, // prepare a statement for execution
	"PREPARE TRANSACTION":              true, // prepare the current transaction for two-phase commit
	"REASSIGN OWNED":                   true, // change the ownership of database objects owned by a database role
	"REFRESH MATERIALIZED VIEW":        true, // replace the contents of a materialized view
	"REINDEX":                          true, // rebuild indexes
	"RELEASE SAVEPOINT":                true, // destroy a previously defined savepoint
	"RESET":                            true, // restore the value of a run-time parameter to the default value
	"REVOKE":                           true, // remove access privileges
	"ROLLBACK PREPARED":                true, // cancel a transaction that was earlier prepared for two-phase commit
	"ROLLBACK TO SAVEPOINT":            true, // roll back to a savepoint
	"ROLLBACK":                         true, // abort the current transaction
	"SAVEPOINT":                        true, // define a new savepoint within the current transaction
	"SECURITY LABEL":                   true, // define or change a security label applied to an object
	"SELECT INTO":                      true, // define a new table from the results of a query
	"SET CONSTRAINTS":                  true, // set constraint check timing for the current transaction
	"SET ROLE":                         true, // set the current user identifier of the current session
	"SET SESSION AUTHORIZATION":        true, // set the session user identifier and the current user identifier of the current session
	"SET TRANSACTION":                  true, // set the characteristics of the current transaction
	"SET":                              true, // change a run-time parameter
	"START TRANSACTION":                true, // start a transaction block
	"TRUNCATE":                         true, // empty a table or set of tables
	"UNLISTEN":                         true, // stop listening for a notification
	"UPDATE":                           true, // update rows of a table
	"VACUUM":                           true, // garbage-collect and optionally analyze a database
}

// QueryExecType is the default way to determine the "EXEC" prefix for a SQL
// query and whether or not it should be Exec'd or Query'd.
func QueryExecType(prefix, sqlstr string) (string, bool) {
	if prefix == "" {
		return "EXEC", false
	}

	s := strings.Split(prefix, " ")
	if len(s) > 0 {
		// check query map
		if _, ok := queryMap[s[0]]; ok {
			typ := s[0]
			switch {
			case typ == "SELECT" && len(s) >= 2 && s[1] == "INTO":
				return "SELECT INTO", false
			case typ == "PRAGMA":
				return typ, !strings.ContainsRune(sqlstr, '=')
			}
			return typ, true
		}

		// find longest match
		for i := len(s); i > 0; i-- {
			typ := strings.Join(s[:i], " ")
			if _, ok := execMap[typ]; ok {
				return typ, false
			}
		}
	}

	return s[0], false
}
<commit_msg>Adding DESC and DESCRIBE to query types<commit_after>package drivers

import "strings"

var queryMap = map[string]bool{
	"WITH":   true,
	"PRAGMA": true,

	"EXPLAIN":  true, // show the execution plan of a statement
	"DESCRIBE": true, // describe (mysql)
	"DESC":     true, // describe (mysql)
	"FETCH":    true, // retrieve rows from a query using a cursor
	"SELECT":   true, // retrieve rows from a table or view
	"SHOW":     true, // show the value of a run-time parameter
	"VALUES":   true, // compute a set of rows

	"EXEC": true, // execute a stored procedure that returns rows (not postgres)
}

var execMap = map[string]bool{
	"USE": true,

	"ABORT":                            true, // abort the current transaction
	"ALTER AGGREGATE":                  true, // change the definition of an aggregate function
	"ALTER COLLATION":                  true, // change the definition of a collation
	"ALTER CONVERSION":                 true, // change the definition of a conversion
	"ALTER DATABASE":                   true, // change a database
	"ALTER DEFAULT PRIVILEGES":         true, // define default access privileges
	"ALTER DOMAIN":                     true, // change the definition of a domain
	"ALTER EVENT TRIGGER":              true, // change the definition of an event trigger
	"ALTER EXTENSION":                  true, // change the definition of an extension
	"ALTER FOREIGN DATA WRAPPER":       true, // change the definition of a foreign-data wrapper
	"ALTER FOREIGN TABLE":              true, // change the definition of a foreign table
	"ALTER FUNCTION":                   true, // change the definition of a function
	"ALTER GROUP":                      true, // change role name or membership
	"ALTER INDEX":                      true, // change the definition of an index
	"ALTER LANGUAGE":                   true, // change the definition of a procedural language
	"ALTER LARGE OBJECT":               true, // change the definition of a large object
	"ALTER MATERIALIZED VIEW":          true, // change the definition of a materialized view
	"ALTER OPERATOR":                   true, // change the definition of an operator
	"ALTER OPERATOR CLASS":             true, // change the definition of an operator class
	"ALTER OPERATOR FAMILY":            true, // change the definition of an operator family
	"ALTER POLICY":                     true, // change the definition of a row level security policy
	"ALTER ROLE":                       true, // change a database role
	"ALTER RULE":                       true, // change the definition of a rule
	"ALTER SCHEMA":                     true, // change the definition of a schema
	"ALTER SEQUENCE":                   true, // change the definition of a sequence generator
	"ALTER SERVER":                     true, // change the definition of a foreign server
	"ALTER SYSTEM":                     true, // change a server configuration parameter
	"ALTER TABLE":                      true, // change the definition of a table
	"ALTER TABLESPACE":                 true, // change the definition of a tablespace
	"ALTER TEXT SEARCH CONFIGURATION":  true, // change the definition of a text search configuration
	"ALTER TEXT SEARCH DICTIONARY":     true, // change the definition of a text search dictionary
	"ALTER TEXT SEARCH PARSER":         true, // change the definition of a text search parser
	"ALTER TEXT SEARCH TEMPLATE":       true, // change the definition of a text search template
	"ALTER TRIGGER":                    true, // change the definition of a trigger
	"ALTER TYPE":                       true, // change the definition of a type
	"ALTER USER":                       true, // change a database role
	"ALTER USER MAPPING":               true, // change the definition of a user mapping
	"ALTER VIEW":                       true, // change the definition of a view
	"ANALYZE":                          true, // collect statistics about a database
	"BEGIN":                            true, // start a transaction block
	"CHECKPOINT":                       true, // force a transaction log checkpoint
	"CLOSE":                            true, // close a cursor
	"CLUSTER":                          true, // cluster a table according to an index
	"COMMENT":                          true, // define or change the comment of an object
	"COMMIT":                           true, // commit the current transaction
	"COMMIT PREPARED":                  true, // commit a transaction that was earlier prepared for two-phase commit
	"COPY":                             true, // copy data between a file and a table
	"CREATE ACCESS METHOD":             true, // define a new access method
	"CREATE AGGREGATE":                 true, // define a new aggregate function
	"CREATE CAST":                      true, // define a new cast
	"CREATE COLLATION":                 true, // define a new collation
	"CREATE CONVERSION":                true, // define a new encoding conversion
	"CREATE DATABASE":                  true, // create a new database
	"CREATE DOMAIN":                    true, // define a new domain
	"CREATE EVENT TRIGGER":             true, // define a new event trigger
	"CREATE EXTENSION":                 true, // install an extension
	"CREATE FOREIGN DATA WRAPPER":      true, // define a new foreign-data wrapper
	"CREATE FOREIGN TABLE":             true, // define a new foreign table
	"CREATE FUNCTION":                  true, // define a new function
	"CREATE GROUP":                     true, // define a new database role
	"CREATE INDEX":                     true, // define a new index
	"CREATE LANGUAGE":                  true, // define a new procedural language
	"CREATE MATERIALIZED VIEW":         true, // define a new materialized view
	"CREATE OPERATOR":                  true, // define a new operator
	"CREATE OPERATOR CLASS":            true, // define a new operator class
	"CREATE OPERATOR FAMILY":           true, // define a new operator family
	"CREATE POLICY":                    true, // define a new row level security policy for a table
	"CREATE ROLE":                      true, // define a new database role
	"CREATE RULE":                      true, // define a new rewrite rule
	"CREATE SCHEMA":                    true, // define a new schema
	"CREATE SEQUENCE":                  true, // define a new sequence generator
	"CREATE SERVER":                    true, // define a new foreign server
	"CREATE TABLE":                     true, // define a new table
	"CREATE TABLE AS":                  true, // define a new table from the results of a query
	"CREATE TABLESPACE":                true, // define a new tablespace
	"CREATE TEXT SEARCH CONFIGURATION": true, // define a new text search configuration
	"CREATE TEXT SEARCH DICTIONARY":    true, // define a new text search dictionary
	"CREATE TEXT SEARCH PARSER":        true, // define a new text search parser
	"CREATE TEXT SEARCH TEMPLATE":      true, // define a new text search template
	"CREATE TRANSFORM":                 true, // define a new transform
	"CREATE TRIGGER":                   true, // define a new trigger
	"CREATE TYPE":                      true, // define a new data type
	"CREATE USER":                      true, // define a new database role
	"CREATE USER MAPPING":              true, // define a new mapping of a user to a foreign server
	"CREATE VIEW":                      true, // define a new view
	"DEALLOCATE":                       true, // deallocate a prepared statement
	"DECLARE":                          true, // define a cursor
	"DELETE":                           true, // delete rows of a table
	"DISCARD":                          true, // discard session state
	"DO":                               true, // execute an anonymous code block
	"DROP ACCESS METHOD":               true, // remove an access method
	"DROP AGGREGATE":                   true, // remove an aggregate function
	"DROP CAST":                        true, // remove a cast
	"DROP COLLATION":                   true, // remove a collation
	"DROP CONVERSION":                  true, // remove a conversion
	"DROP DATABASE":                    true, // remove a database
	"DROP DOMAIN":                      true, // remove a domain
	"DROP EVENT TRIGGER":               true, // remove an event trigger
	"DROP EXTENSION":                   true, // remove an extension
	"DROP FOREIGN DATA WRAPPER":        true, // remove a foreign-data wrapper
	"DROP FOREIGN TABLE":               true, // remove a foreign table
	"DROP FUNCTION":                    true, // remove a function
	"DROP GROUP":                       true, // remove a database role
	"DROP INDEX":                       true, // remove an index
	"DROP LANGUAGE":                    true, // remove a procedural language
	"DROP MATERIALIZED VIEW":           true, // remove a materialized view
	"DROP OPERATOR":                    true, // remove an operator
	"DROP OPERATOR CLASS":              true, // remove an operator class
	"DROP OPERATOR FAMILY":             true, // remove an operator family
	"DROP OWNED":                       true, // remove database objects owned by a database role
	"DROP POLICY":                      true, // remove a row level security policy from a table
	"DROP ROLE":                        true, // remove a database role
	"DROP RULE":                        true, // remove a rewrite rule
	"DROP SCHEMA":                      true, // remove a schema
	"DROP SEQUENCE":                    true, // remove a sequence
	"DROP SERVER":                      true, // remove a foreign server descriptor
	"DROP TABLE":                       true, // remove a table
	"DROP TABLESPACE":                  true, // remove a tablespace
	"DROP TEXT SEARCH CONFIGURATION":   true, // remove a text search configuration
	"DROP TEXT SEARCH DICTIONARY":      true, // remove a text search dictionary
	"DROP TEXT SEARCH PARSER":          true, // remove a text search parser
	"DROP TEXT SEARCH TEMPLATE":        true, // remove a text search template
	"DROP TRANSFORM":                   true, // remove a transform
	"DROP TRIGGER":                     true, // remove a trigger
	"DROP TYPE":                        true, // remove a data type
	"DROP USER":                        true, // remove a database role
	"DROP USER MAPPING":                true, // remove a user mapping for a foreign server
	"DROP VIEW":                        true, // remove a view
	"END":                              true, // commit the current transaction
	"EXECUTE":                          true, // execute a prepared statement
	"GRANT":                            true, // define access privileges
	"IMPORT FOREIGN SCHEMA":            true, // import table definitions from a foreign server
	"INSERT":                           true, // create new rows in a table
	"LISTEN":                           true, // listen for a notification
	"LOAD":                             true, // load a shared library file
	"LOCK":                             true, // lock a table
	"MOVE":                             true, // position a cursor
	"NOTIFY":                           true, // generate a notification
	"PREPARE":                          true, // prepare a statement for execution
	"PREPARE TRANSACTION":              true, // prepare the current transaction for two-phase commit
	"REASSIGN OWNED":                   true, // change the ownership of database objects owned by a database role
	"REFRESH MATERIALIZED VIEW":        true, // replace the contents of a materialized view
	"REINDEX":                          true, // rebuild indexes
	"RELEASE SAVEPOINT":                true, // destroy a previously defined savepoint
	"RESET":                            true, // restore the value of a run-time parameter to the default value
	"REVOKE":                           true, // remove access privileges
	"ROLLBACK PREPARED":                true, // cancel a transaction that was earlier prepared for two-phase commit
	"ROLLBACK TO SAVEPOINT":            true, // roll back to a savepoint
	"ROLLBACK":                         true, // abort the current transaction
	"SAVEPOINT":                        true, // define a new savepoint within the current transaction
	"SECURITY LABEL":                   true, // define or change a security label applied to an object
	"SELECT INTO":                      true, // define a new table from the results of a query
	"SET CONSTRAINTS":                  true, // set constraint check timing for the current transaction
	"SET ROLE":                         true, // set the current user identifier of the current session
	"SET SESSION AUTHORIZATION":        true, // set the session user identifier and the current user identifier of the current session
	"SET TRANSACTION":                  true, // set the characteristics of the current transaction
	"SET":                              true, // change a run-time parameter
	"START TRANSACTION":                true, // start a transaction block
	"TRUNCATE":                         true, // empty a table or set of tables
	"UNLISTEN":                         true, // stop listening for a notification
	"UPDATE":                           true, // update rows of a table
	"VACUUM":                           true, // garbage-collect and optionally analyze a database
}

// QueryExecType is the default way to determine the "EXEC" prefix for a SQL
// query and whether or not it should be Exec'd or Query'd.
func QueryExecType(prefix, sqlstr string) (string, bool) {
	if prefix == "" {
		return "EXEC", false
	}

	s := strings.Split(prefix, " ")
	if len(s) > 0 {
		// check query map
		if _, ok := queryMap[s[0]]; ok {
			typ := s[0]
			switch {
			case typ == "SELECT" && len(s) >= 2 && s[1] == "INTO":
				return "SELECT INTO", false
			case typ == "PRAGMA":
				return typ, !strings.ContainsRune(sqlstr, '=')
			}
			return typ, true
		}

		// find longest match
		for i := len(s); i > 0; i-- {
			typ := strings.Join(s[:i], " ")
			if _, ok := execMap[typ]; ok {
				return typ, false
			}
		}
	}

	return s[0], false
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 turbobytes
//
// This file is part of geoipdb, a library of GeoIP related helper functions
// for TurboBytes stack.
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package geoipdb_test

import (
	"net"
	"testing"

	"github.com/turbobytes/geoipdb"
)

const host = "www.turbobytes.com"

var ip string
var asnCymru string

func TestInitIp(t *testing.T) {
	ips, err := net.LookupIP(host)
	if err != nil {
		t.Fatalf("failed to lookup ip addresses for '%s': %s", host, err)
	}
	if len(ips) < 1 {
		t.Fatalf("ip address lookup for '%s' returned empty", host)
	}
	ip = ips[0].String()
	t.Logf("using ip %s (%s) for tests", ip, host)
}

var gh geoipdb.Handler

func TestCreateHandler(t *testing.T) {
	var err error
	gh, err = geoipdb.NewHandler()
	if err != nil {
		t.Fatalf("geoipdb.New failed: %s", err)
	}
}

func TestLibGeoipLookup(t *testing.T) {
	asn, asnDescr := gh.LibGeoipLookup(ip)
	if asn == "" {
		t.Fatalf("ASN of ip '%s' is unknown by libgeoip", ip)
	}
	if asnCymru == "" {
		asnCymru = asn
	}
	t.Logf("libgeoip results: %s %s", asn, asnDescr)
}

func TestIpInfoLookup(t *testing.T) {
	asn, asnDescr, err := gh.IpInfoLookup(ip)
	if err != nil {
		t.Fatalf("IpInfoLookup failed: %s", err)
	}
	if asnCymru == "" {
		asnCymru = asn
	}
	t.Logf("ipinfo.io results: %s %s", asn, asnDescr)
}

func TestCymruDnsLookup(t *testing.T) {
	asnDescr, err := gh.CymruDnsLookup(asnCymru)
	if err != nil {
		t.Fatalf("CymruDnsLookup failed for '%s': %s", asnCymru, err)
	}
	t.Logf("CymruDnsLookup results: %s", asnDescr)
}

func TestLookupAsn(t *testing.T) {
	asn, asnDescr, err := gh.LookupAsn(ip)
	if err != nil {
		t.Fatalf("LookupAsn failed for %s: %s", ip, err)
	}
	t.Logf("LookupAsn results: %s %s", asn, asnDescr)
}
<commit_msg>Using 8.8.8.8 as the test IP address.<commit_after>// Copyright (c) 2016 turbobytes
//
// This file is part of geoipdb, a library of GeoIP related helper functions
// for TurboBytes stack.
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package geoipdb_test

import (
	"testing"

	"github.com/turbobytes/geoipdb"
)

// Well known IP address for testing lookups
const ip = "8.8.8.8"

// Results of some ASN lookups
var (
	asnLibGeo string
	asnIpInfo string
)

func TestInitIp(t *testing.T) {
	t.Logf("using ip '%s' for tests", ip)
}

var gh geoipdb.Handler

func TestCreateHandler(t *testing.T) {
	var err error
	gh, err = geoipdb.NewHandler()
	if err != nil {
		t.Fatalf("geoipdb.New failed: %s", err)
	}
}

func TestLibGeoipLookup(t *testing.T) {
	var asnDescr string
	asnLibGeo, asnDescr = gh.LibGeoipLookup(ip)
	if asnLibGeo == "" {
		t.Fatalf("ASN of ip '%s' is unknown by libgeoip", ip)
	}
	t.Logf("libgeoip results for %s: %s %s", ip, asnLibGeo, asnDescr)
}

func TestIpInfoLookup(t *testing.T) {
	var err error
	var asnDescr string
	asnIpInfo, asnDescr, err = gh.IpInfoLookup(ip)
	if err != nil {
		t.Fatalf("IpInfoLookup failed: %s", err)
	}
	t.Logf("ipinfo.io results for %s: %s %s", ip, asnIpInfo, asnDescr)
}

func TestCymruDnsLookup(t *testing.T) {
	if asnLibGeo != "" {
		asnDescr, err := gh.CymruDnsLookup(asnLibGeo)
		if err != nil {
			t.Fatalf("CymruDnsLookup failed for '%s': %s", asnLibGeo, err)
		}
		t.Logf("CymruDnsLookup results for %s: %s", asnLibGeo, asnDescr)
	}
	if asnIpInfo != "" && asnIpInfo != asnLibGeo {
		asnDescr, err := gh.CymruDnsLookup(asnIpInfo)
		if err != nil {
			t.Fatalf("CymruDnsLookup failed for '%s': %s", asnIpInfo, err)
		}
		t.Logf("CymruDnsLookup results for %s: %s", asnIpInfo, asnDescr)
	}
}

func TestLookupAsn(t *testing.T) {
	asn, asnDescr, err := gh.LookupAsn(ip)
	if err != nil {
		t.Fatalf("LookupAsn failed for %s: %s", ip, err)
	}
	t.Logf("LookupAsn results: %s %s", asn, asnDescr)
}
<|endoftext|>


<commit_before>package dsl

import (
	"goa.design/goa/v3/eval"
	"goa.design/goa/v3/expr"
)

var (
	// ErrorResultIdentifier is the result type identifier used for error
	// responses.
	ErrorResultIdentifier = expr.ErrorResultIdentifier

	// ErrorResult is the built-in result type for error responses.
	ErrorResult = expr.ErrorResult
)

// Type defines a user type. A user type has a unique name and may be an alias
// to an existing type or may describe a completely new type using a list of
// attributes (object fields). Attribute types may themselves be user type.
// When a user type is defined as an alias to another type it may define
// additional validations - for example if a user type which is an alias of
// String may define a validation pattern that all instances of the type
// must match.
//
// Type is a top level definition.
//
// Type takes two or three arguments: the first argument is the name of the type.
// The name must be unique. The second argument is either another type or a
// function. If the second argument is a type then there may be a function passed
// as third argument.
//
// Example:
//
//     // simple alias
//     var MyString = Type("MyString", String)
//
//     // alias with description and additional validation
//     var Hostname = Type("Hostname", String, func() {
//         Description("A host name")
//         Format(FormatHostname)
//     })
//
//     // new type
//     var SumPayload = Type("SumPayload", func() {
//         Description("Type sent to add method")
//
//         Attribute("a", String)                 // string attribute "a"
//         Attribute("b", Int32, "operand")       // attribute with description
//         Attribute("operands", ArrayOf(Int32))  // array attribute
//         Attribute("ops", MapOf(String, Int32)) // map attribute
//         Attribute("c", SumMod)                 // attribute using user type
//         Attribute("len", Int64, func() {       // attribute with validation
//             Minimum(1)
//         })
//
//         Required("a")                          // Required attributes
//         Required("b", "c")
//     })
//
func Type(name string, args ...interface{}) expr.UserType {
	if len(args) > 2 {
		eval.ReportError("too many arguments")
		return nil
	}
	if t := expr.Root.UserType(name); t != nil {
		eval.ReportError("type %#v defined twice", name)
		return nil
	}

	if _, ok := eval.Current().(eval.TopExpr); !ok {
		eval.IncompatibleDSL()
		return nil
	}

	var (
		base expr.DataType
		fn   func()
	)
	if len(args) == 0 {
		// Make Type behave like Attribute
		args = []interface{}{expr.String}
	}
	switch a := args[0].(type) {
	case expr.DataType:
		base = a
		if len(args) == 2 {
			d, ok := args[1].(func())
			if !ok {
				eval.ReportError("third argument must be a function")
				return nil
			}
			fn = d
		}
	case func():
		base = &expr.Object{}
		fn = a
		if len(args) == 2 {
			eval.ReportError("only one argument allowed when it is a function")
			return nil
		}
	default:
		eval.InvalidArgError("type or function", args[0])
		return nil
	}

	t := &expr.UserTypeExpr{
		TypeName:      name,
		AttributeExpr: &expr.AttributeExpr{Type: base, DSLFunc: fn},
	}
	expr.Root.Types = append(expr.Root.Types, t)
	return t
}

// ArrayOf creates an array type from its element type.
//
// ArrayOf may be used wherever types can.
// The first argument of ArrayOf is the type of the array elements specified by
// name or by reference.
// The second argument of ArrayOf is an optional function that defines
// validations for the array elements.
//
// Examples:
//
//    var Names = ArrayOf(String, func() {
//        Pattern("[a-zA-Z]+") // Validates elements of the array
//    })
//
//    var Account = Type("Account", func() {
//        Attribute("bottles", ArrayOf(Bottle), "Account bottles", func() {
//            MinLength(1) // Validates array as a whole
//        })
//    })
//
// Note: CollectionOf and ArrayOf both return array types. CollectionOf returns
// a result type where ArrayOf returns a user type. In general you want to use
// CollectionOf if the argument is a result type and ArrayOf if it is a user
// type.
func ArrayOf(v interface{}, fn ...func()) *expr.Array {
	var t expr.DataType
	var ok bool
	t, ok = v.(expr.DataType)
	if !ok {
		if name, ok := v.(string); ok {
			t = expr.Root.UserType(name)
		}
	}
	// never return nil to avoid panics, errors are reported after DSL execution
	if t == nil {
		eval.ReportError("invalid ArrayOf argument: not a type and not a known user type name")
		return &expr.Array{ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if len(fn) > 1 {
		eval.ReportError("ArrayOf: too many arguments")
		return &expr.Array{ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	at := expr.AttributeExpr{Type: t}
	if len(fn) == 1 {
		eval.Execute(fn[0], &at)
	}
	return &expr.Array{ElemType: &at}
}

// MapOf creates a map from its key and element types.
//
// MapOf may be used wherever types can.
// MapOf takes two arguments: the key and value types either by name of by reference.
//
// Example:
//
//    var ReviewByID = MapOf(Int64, String, func() {
//        Key(func() {
//            Minimum(1)           // Validates keys of the map
//        })
//        Elem(func() {
//            Pattern("[a-zA-Z]+") // Validates values of the map
//        })
//    })
//
//    var Review = Type("Review", func() {
//        Attribute("ratings", MapOf(Bottle, Int32), "Bottle ratings")
//    })
//
func MapOf(k, v interface{}, fn ...func()) *expr.Map {
	var tk, tv expr.DataType
	var ok bool
	tk, ok = k.(expr.DataType)
	if !ok {
		if name, ok := k.(string); ok {
			tk = expr.Root.UserType(name)
		}
	}
	tv, ok = v.(expr.DataType)
	if !ok {
		if name, ok := v.(string); ok {
			tv = expr.Root.UserType(name)
		}
	}
	// never return nil to avoid panics, errors are reported after DSL execution
	if tk == nil {
		eval.ReportError("invalid MapOf key argument: not a type and not a known user type name")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if expr.IsMap(tk) {
		eval.ReportError("invalid MapOf key type: key type must be a primitive, array, or user type")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if tv == nil {
		eval.ReportError("invalid MapOf value argument: not a type and not a known user type name")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if len(fn) > 1 {
		eval.ReportError("MapOf: too many arguments")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	kat := expr.AttributeExpr{Type: tk}
	vat := expr.AttributeExpr{Type: tv}
	m := &expr.Map{KeyType: &kat, ElemType: &vat}
	if len(fn) == 1 {
		mat := expr.AttributeExpr{Type: m}
		eval.Execute(fn[0], &mat)
	}
	return m
}

// Key makes it possible to specify validations for map keys.
//
// Example:
//
//    Attribute("map", MapOf(String, Int), func() {
//        Key(func() {
//            Format(FormatDateTime) // map keys are timestamps
//        })
//    })
//
func Key(fn func()) {
	at, ok := eval.Current().(*expr.AttributeExpr)
	if !ok {
		eval.IncompatibleDSL()
		return
	}
	if m, ok := at.Type.(*expr.Map); ok {
		eval.Execute(fn, m.KeyType)
		return
	}
	eval.IncompatibleDSL()
}

// Elem makes it possible to specify validations for array and map values.
//
// Example:
//
//    Attribute("array", ArrayOf(Int), func() {
//        Elem(func() {
//            Enum(1, 2, 3, 4, 5) // list possible values for array elements
//        })
//    })
//
//    Attribute("map", MapOf(String, Int), func() {
//        Elem(func() {
//            Minimum(1)
//            Maximum(100)
//        })
//    })
//
func Elem(fn func()) {
	at, ok := eval.Current().(*expr.AttributeExpr)
	if !ok {
		eval.IncompatibleDSL()
		return
	}
	switch e := at.Type.(type) {
	case *expr.Array:
		eval.Execute(fn, e.ElemType)
	case *expr.Map:
		eval.Execute(fn, e.ElemType)
	default:
		eval.IncompatibleDSL()
	}
}
<commit_msg>Fix documentation for Map validation<commit_after>package dsl

import (
	"goa.design/goa/v3/eval"
	"goa.design/goa/v3/expr"
)

var (
	// ErrorResultIdentifier is the result type identifier used for error
	// responses.
	ErrorResultIdentifier = expr.ErrorResultIdentifier

	// ErrorResult is the built-in result type for error responses.
	ErrorResult = expr.ErrorResult
)

// Type defines a user type. A user type has a unique name and may be an alias
// to an existing type or may describe a completely new type using a list of
// attributes (object fields). Attribute types may themselves be user type.
// When a user type is defined as an alias to another type it may define
// additional validations - for example if a user type which is an alias of
// String may define a validation pattern that all instances of the type
// must match.
//
// Type is a top level definition.
//
// Type takes two or three arguments: the first argument is the name of the type.
// The name must be unique. The second argument is either another type or a
// function. If the second argument is a type then there may be a function passed
// as third argument.
//
// Example:
//
//     // simple alias
//     var MyString = Type("MyString", String)
//
//     // alias with description and additional validation
//     var Hostname = Type("Hostname", String, func() {
//         Description("A host name")
//         Format(FormatHostname)
//     })
//
//     // new type
//     var SumPayload = Type("SumPayload", func() {
//         Description("Type sent to add method")
//
//         Attribute("a", String)                 // string attribute "a"
//         Attribute("b", Int32, "operand")       // attribute with description
//         Attribute("operands", ArrayOf(Int32))  // array attribute
//         Attribute("ops", MapOf(String, Int32)) // map attribute
//         Attribute("c", SumMod)                 // attribute using user type
//         Attribute("len", Int64, func() {       // attribute with validation
//             Minimum(1)
//         })
//
//         Required("a")                          // Required attributes
//         Required("b", "c")
//     })
//
func Type(name string, args ...interface{}) expr.UserType {
	if len(args) > 2 {
		eval.ReportError("too many arguments")
		return nil
	}
	if t := expr.Root.UserType(name); t != nil {
		eval.ReportError("type %#v defined twice", name)
		return nil
	}

	if _, ok := eval.Current().(eval.TopExpr); !ok {
		eval.IncompatibleDSL()
		return nil
	}

	var (
		base expr.DataType
		fn   func()
	)
	if len(args) == 0 {
		// Make Type behave like Attribute
		args = []interface{}{expr.String}
	}
	switch a := args[0].(type) {
	case expr.DataType:
		base = a
		if len(args) == 2 {
			d, ok := args[1].(func())
			if !ok {
				eval.ReportError("third argument must be a function")
				return nil
			}
			fn = d
		}
	case func():
		base = &expr.Object{}
		fn = a
		if len(args) == 2 {
			eval.ReportError("only one argument allowed when it is a function")
			return nil
		}
	default:
		eval.InvalidArgError("type or function", args[0])
		return nil
	}

	t := &expr.UserTypeExpr{
		TypeName:      name,
		AttributeExpr: &expr.AttributeExpr{Type: base, DSLFunc: fn},
	}
	expr.Root.Types = append(expr.Root.Types, t)
	return t
}

// ArrayOf creates an array type from its element type.
//
// ArrayOf may be used wherever types can.
// The first argument of ArrayOf is the type of the array elements specified by
// name or by reference.
// The second argument of ArrayOf is an optional function that defines
// validations for the array elements.
//
// Examples:
//
//    var Names = ArrayOf(String, func() {
//        Pattern("[a-zA-Z]+") // Validates elements of the array
//    })
//
//    var Account = Type("Account", func() {
//        Attribute("bottles", ArrayOf(Bottle), "Account bottles", func() {
//            MinLength(1) // Validates array as a whole
//        })
//    })
//
// Note: CollectionOf and ArrayOf both return array types. CollectionOf returns
// a result type where ArrayOf returns a user type. In general you want to use
// CollectionOf if the argument is a result type and ArrayOf if it is a user
// type.
func ArrayOf(v interface{}, fn ...func()) *expr.Array {
	var t expr.DataType
	var ok bool
	t, ok = v.(expr.DataType)
	if !ok {
		if name, ok := v.(string); ok {
			t = expr.Root.UserType(name)
		}
	}
	// never return nil to avoid panics, errors are reported after DSL execution
	if t == nil {
		eval.ReportError("invalid ArrayOf argument: not a type and not a known user type name")
		return &expr.Array{ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if len(fn) > 1 {
		eval.ReportError("ArrayOf: too many arguments")
		return &expr.Array{ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	at := expr.AttributeExpr{Type: t}
	if len(fn) == 1 {
		eval.Execute(fn[0], &at)
	}
	return &expr.Array{ElemType: &at}
}

// MapOf creates a map from its key and element types.
//
// MapOf may be used wherever types can.
// MapOf takes two arguments: the key and value types either by name of by reference.
//
// Example:
//
//    var Review = Type("Review", func() {
//        Attribute("ratings", MapOf(Bottle, Int32), "Bottle ratings", func() {
//            Elem(func() {
//                Minimum(1)
//                Maximum(5)
//            })
//        })
//    })
//
func MapOf(k, v interface{}, fn ...func()) *expr.Map {
	var tk, tv expr.DataType
	var ok bool
	tk, ok = k.(expr.DataType)
	if !ok {
		if name, ok := k.(string); ok {
			tk = expr.Root.UserType(name)
		}
	}
	tv, ok = v.(expr.DataType)
	if !ok {
		if name, ok := v.(string); ok {
			tv = expr.Root.UserType(name)
		}
	}
	// never return nil to avoid panics, errors are reported after DSL execution
	if tk == nil {
		eval.ReportError("invalid MapOf key argument: not a type and not a known user type name")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if expr.IsMap(tk) {
		eval.ReportError("invalid MapOf key type: key type must be a primitive, array, or user type")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if tv == nil {
		eval.ReportError("invalid MapOf value argument: not a type and not a known user type name")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if len(fn) > 1 {
		eval.ReportError("MapOf: too many arguments")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	kat := expr.AttributeExpr{Type: tk}
	vat := expr.AttributeExpr{Type: tv}
	m := &expr.Map{KeyType: &kat, ElemType: &vat}
	if len(fn) == 1 {
		mat := expr.AttributeExpr{Type: m}
		eval.Execute(fn[0], &mat)
	}
	return m
}

// Key makes it possible to specify validations for map keys.
//
// Example:
//
//    Attribute("map", MapOf(String, Int), func() {
//        Key(func() {
//            Format(FormatDateTime) // map keys are timestamps
//        })
//    })
//
func Key(fn func()) {
	at, ok := eval.Current().(*expr.AttributeExpr)
	if !ok {
		eval.IncompatibleDSL()
		return
	}
	if m, ok := at.Type.(*expr.Map); ok {
		eval.Execute(fn, m.KeyType)
		return
	}
	eval.IncompatibleDSL()
}

// Elem makes it possible to specify validations for array and map values.
//
// Example:
//
//    Attribute("array", ArrayOf(Int), func() {
//        Elem(func() {
//            Enum(1, 2, 3, 4, 5) // list possible values for array elements
//        })
//    })
//
//    Attribute("map", MapOf(String, Int), func() {
//        Elem(func() {
//            Minimum(1)
//            Maximum(100)
//        })
//    })
//
func Elem(fn func()) {
	at, ok := eval.Current().(*expr.AttributeExpr)
	if !ok {
		eval.IncompatibleDSL()
		return
	}
	switch e := at.Type.(type) {
	case *expr.Array:
		eval.Execute(fn, e.ElemType)
	case *expr.Map:
		eval.Execute(fn, e.ElemType)
	default:
		eval.IncompatibleDSL()
	}
}
<|endoftext|>


<commit_before>package mpo

import (
	"errors"
	"image"
	"image/color"
)

type colorType int

const (
	REDBLUE colorType = iota
	BLUERED

	REDGREEN
	GREENRED
)

func (m *Mpo) ConvertToAnaglyph(colType colorType) (image.Image, error) {
	if len(m.Images) != 2 {
		return nil, errors.New("Anaglph conversion only supports 2 image MPOs")
	}

	left := m.Images[0]
	right := m.Images[1]

	if !left.Bounds().Eq(right.Bounds()) {
		return nil, errors.New("MPO images must be the same size to convert to anaglyph")
	}

	img := image.NewRGBA(left.Bounds())

	for x := 0; x <= left.Bounds().Max.X; x++ {
		for y := 0; y <= left.Bounds().Max.Y; y++ {
			lr, lg, lb, _ := left.At(x, y).RGBA()
			rr, rg, rb, _ := right.At(x, y).RGBA()

			lgs := (((float32(lr) / 65535) * .229) * 65535) +
				(((float32(lg) / 65535) * .587) * 65535) +
				(((float32(lb) / 65535) * .144) * 65535)

			rgs := (((float32(rr) / 65535) * .229) * 65535) +
				(((float32(rg) / 65535) * .587) * 65535) +
				(((float32(rb) / 65535) * .144) * 65535)

			var c color.RGBA64
			switch colType {
			case REDBLUE:
				c = color.RGBA64{
					R: uint16(lgs),
					G: uint16(rg),
					B: uint16(rb),
					A: 65535,
				}
			case BLUERED:
				c = color.RGBA64{
					R: uint16(rgs),
					G: uint16(lg),
					B: uint16(lb),
					A: 65535,
				}
			default:
				return nil, errors.New("Unsupported color type")
			}

			img.Set(x, y, c)
		}
	}

	return img, nil
}
<commit_msg>Cyan more accurate than blue + camel casing<commit_after>package mpo

import (
	"errors"
	"image"
	"image/color"
)

type colorType int

const (
	RedCyan colorType = iota
	CyanRed

	RedGreen
	GreenRed
)

func (m *Mpo) ConvertToAnaglyph(colType colorType) (image.Image, error) {
	if len(m.Images) != 2 {
		return nil, errors.New("Anaglph conversion only supports 2 image MPOs")
	}

	left := m.Images[0]
	right := m.Images[1]

	if !left.Bounds().Eq(right.Bounds()) {
		return nil, errors.New("MPO images must be the same size to convert to anaglyph")
	}

	img := image.NewRGBA(left.Bounds())

	for x := 0; x <= left.Bounds().Max.X; x++ {
		for y := 0; y <= left.Bounds().Max.Y; y++ {
			lr, lg, lb, _ := left.At(x, y).RGBA()
			rr, rg, rb, _ := right.At(x, y).RGBA()

			lgs := (((float32(lr) / 65535) * .229) * 65535) +
				(((float32(lg) / 65535) * .587) * 65535) +
				(((float32(lb) / 65535) * .144) * 65535)

			rgs := (((float32(rr) / 65535) * .229) * 65535) +
				(((float32(rg) / 65535) * .587) * 65535) +
				(((float32(rb) / 65535) * .144) * 65535)

			var c color.RGBA64
			switch colType {
			case RedCyan:
				c = color.RGBA64{
					R: uint16(lgs),
					G: uint16(rg),
					B: uint16(rb),
					A: 65535,
				}
			case CyanRed:
				c = color.RGBA64{
					R: uint16(rgs),
					G: uint16(lg),
					B: uint16(lb),
					A: 65535,
				}
			default:
				return nil, errors.New("Unsupported color type")
			}

			img.Set(x, y, c)
		}
	}

	return img, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package scheduledjob

/*
I did not use watch or expectations.  Those add a lot of corner cases, and we aren't
expecting a large volume of jobs or scheduledJobs.  (We are favoring correctness
over scalability.  If we find a single controller thread is too slow because
there are a lot of Jobs or ScheduledJobs, we we can parallelize by Namespace.
If we find the load on the API server is too high, we can use a watch and
UndeltaStore.)

Just periodically list jobs and SJs, and then reconcile them.

*/

import (
	"fmt"
	"time"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/batch"
	"k8s.io/kubernetes/pkg/client/record"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/controller/job"
	"k8s.io/kubernetes/pkg/runtime"
	utilerrors "k8s.io/kubernetes/pkg/util/errors"
	"k8s.io/kubernetes/pkg/util/metrics"
	utilruntime "k8s.io/kubernetes/pkg/util/runtime"
	"k8s.io/kubernetes/pkg/util/wait"
)

// Utilities for dealing with Jobs and ScheduledJobs and time.

type ScheduledJobController struct {
	kubeClient *client.Client
	jobControl jobControlInterface
	sjControl  sjControlInterface
	podControl podControlInterface
	recorder   record.EventRecorder
}

func NewScheduledJobController(kubeClient *client.Client) *ScheduledJobController {
	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartLogging(glog.Infof)
	// TODO: remove the wrapper when every clients have moved to use the clientset.
	eventBroadcaster.StartRecordingToSink(kubeClient.Events(""))

	if kubeClient != nil && kubeClient.GetRateLimiter() != nil {
		metrics.RegisterMetricAndTrackRateLimiterUsage("scheduledjob_controller", kubeClient.GetRateLimiter())
	}

	jm := &ScheduledJobController{
		kubeClient: kubeClient,
		jobControl: realJobControl{KubeClient: kubeClient},
		sjControl:  &realSJControl{KubeClient: kubeClient},
		podControl: &realPodControl{KubeClient: kubeClient},
		recorder:   eventBroadcaster.NewRecorder(api.EventSource{Component: "scheduled-job-controller"}),
	}

	return jm
}

func NewScheduledJobControllerFromClient(kubeClient *client.Client) *ScheduledJobController {
	jm := NewScheduledJobController(kubeClient)
	return jm
}

// Run the main goroutine responsible for watching and syncing jobs.
func (jm *ScheduledJobController) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	glog.Infof("Starting ScheduledJob Manager")
	// Check things every 10 second.
	go wait.Until(jm.SyncAll, 10*time.Second, stopCh)
	<-stopCh
	glog.Infof("Shutting down ScheduledJob Manager")
}

// SyncAll lists all the ScheduledJobs and Jobs and reconciles them.
func (jm *ScheduledJobController) SyncAll() {
	sjl, err := jm.kubeClient.Batch().ScheduledJobs(api.NamespaceAll).List(api.ListOptions{})
	if err != nil {
		glog.Errorf("Error listing scheduledjobs: %v", err)
		return
	}
	sjs := sjl.Items
	glog.Infof("Found %d scheduledjobs", len(sjs))

	jl, err := jm.kubeClient.Batch().Jobs(api.NamespaceAll).List(api.ListOptions{})
	if err != nil {
		glog.Errorf("Error listing jobs")
		return
	}
	js := jl.Items
	glog.Infof("Found %d jobs", len(js))

	jobsBySj := groupJobsByParent(sjs, js)
	glog.Infof("Found %d groups", len(jobsBySj))

	for _, sj := range sjs {
		SyncOne(sj, jobsBySj[sj.UID], time.Now(), jm.jobControl, jm.sjControl, jm.podControl, jm.recorder)
	}
}

// SyncOne reconciles a ScheduledJob with a list of any Jobs that it created.
// All known jobs created by "sj" should be included in "js".
// The current time is passed in to facilitate testing.
// It has no receiver, to facilitate testing.
func SyncOne(sj batch.ScheduledJob, js []batch.Job, now time.Time, jc jobControlInterface, sjc sjControlInterface, pc podControlInterface, recorder record.EventRecorder) {
	nameForLog := fmt.Sprintf("%s/%s", sj.Namespace, sj.Name)

	for _, j := range js {
		found := inActiveList(sj, j.ObjectMeta.UID)
		if !found {
			recorder.Eventf(&sj, api.EventTypeWarning, "UnexpectedJob", "Saw a job that the controller did not create or forgot: %v", j.Name)
			// We found a job object that has us as the parent, but it is not in our Active list.
			// This could happen if we crashed right after creating the Job and before updating the status,
			// or if our jobs list is newer than our sj status after a relist, or if someone intentionally created
			// a job that they wanted us to adopt.

			// TODO: maybe handle the adoption case?  Concurrency/suspend rules will not apply in that case, obviously, since we can't
			// stop users from creating jobs if they have permission.  It is assumed that if a
			// user has permission to create a job within a namespace, then they have permission to make any scheduledJob
			// in the same namespace "adopt" that job.  ReplicaSets and their Pods work the same way.
			// TBS: how to update sj.Status.LastScheduleTime if the adopted job is newer than any we knew about?
		} else {
			if job.IsJobFinished(&j) {
				deleteFromActiveList(&sj, j.ObjectMeta.UID)
				// TODO: event to call out failure vs success.
				recorder.Eventf(&sj, api.EventTypeNormal, "SawCompletedJob", "Saw completed job: %v", j.Name)
			}
		}
	}
	updatedSJ, err := sjc.UpdateStatus(&sj)
	if err != nil {
		glog.Errorf("Unable to update status for %s (rv = %s): %v", nameForLog, sj.ResourceVersion, err)
	}
	sj = *updatedSJ

	if sj.Spec.Suspend != nil && *sj.Spec.Suspend {
		glog.V(4).Infof("Not starting job for %s because it is suspended", nameForLog)
		return
	}
	times, err := getRecentUnmetScheduleTimes(sj, now)
	if err != nil {
		glog.Errorf("Cannot determine if %s needs to be started: %v", nameForLog, err)
	}
	// TODO: handle multiple unmet start times, from oldest to newest, updating status as needed.
	if len(times) == 0 {
		glog.V(4).Infof("No unmet start times for %s", nameForLog)
		return
	}
	if len(times) > 1 {
		glog.Errorf("Multiple unmet start times for %s so only starting last one", nameForLog)
	}
	scheduledTime := times[len(times)-1]
	tooLate := false
	if sj.Spec.StartingDeadlineSeconds != nil {
		tooLate = scheduledTime.Add(time.Second * time.Duration(*sj.Spec.StartingDeadlineSeconds)).Before(now)
	}
	if tooLate {
		glog.Errorf("Missed starting window for %s", nameForLog)
		// TODO: generate an event for a miss.  Use a warning level event because it indicates a
		// problem with the controller (restart or long queue), and is not expected by user either.
		// Since we don't set LastScheduleTime when not scheduling, we are going to keep noticing
		// the miss every cycle.  In order to avoid sending multiple events, and to avoid processing
		// the sj again and again, we could set a Status.LastMissedTime when we notice a miss.
		// Then, when we call getRecentUnmetScheduleTimes, we can take max(creationTimestamp,
		// Status.LastScheduleTime, Status.LastMissedTime), and then so we won't generate
		// and event the next time we process it, and also so the user looking at the status
		// can see easily that there was a missed execution.
		return
	}
	if sj.Spec.ConcurrencyPolicy == batch.ForbidConcurrent && len(sj.Status.Active) > 0 {
		// Regardless which source of information we use for the set of active jobs,
		// there is some risk that we won't see an active job when there is one.
		// (because we haven't seen the status update to the SJ or the created pod).
		// So it is theoretically possible to have concurrency with Forbid.
		// As long the as the invokations are "far enough apart in time", this usually won't happen.
		//
		// TODO: for Forbid, we could use the same name for every execution, as a lock.
		// With replace, we could use a name that is deterministic per execution time.
		// But that would mean that you could not inspect prior successes or failures of Forbid jobs.
		glog.V(4).Infof("Not starting job for %s because of prior execution still running and concurrency policy is Forbid.", nameForLog)
		return
	}
	if sj.Spec.ConcurrencyPolicy == batch.ReplaceConcurrent {
		for _, j := range sj.Status.Active {
			// TODO: this should be replaced with server side job deletion
			// currently this mimics JobReaper from pkg/kubectl/stop.go
			glog.V(4).Infof("Deleting job %s of %s s that was still running at next scheduled start time", j.Name, nameForLog)
			job, err := jc.GetJob(j.Namespace, j.Name)
			if err != nil {
				recorder.Eventf(&sj, api.EventTypeWarning, "FailedGet", "Get job: %v", err)
				return
			}
			// scale job down to 0
			if *job.Spec.Parallelism != 0 {
				zero := int32(0)
				job.Spec.Parallelism = &zero
				job, err = jc.UpdateJob(job.Namespace, job)
				if err != nil {
					recorder.Eventf(&sj, api.EventTypeWarning, "FailedUpdate", "Update job: %v", err)
					return
				}
			}
			// remove all pods...
			selector, _ := unversioned.LabelSelectorAsSelector(job.Spec.Selector)
			options := api.ListOptions{LabelSelector: selector}
			podList, err := pc.ListPods(job.Namespace, options)
			if err != nil {
				recorder.Eventf(&sj, api.EventTypeWarning, "FailedList", "List job-pods: %v", err)
			}
			errList := []error{}
			for _, pod := range podList.Items {
				if err := pc.DeletePod(pod.Namespace, pod.Name); err != nil {
					// ignores the error when the pod isn't found
					if !errors.IsNotFound(err) {
						errList = append(errList, err)
					}
				}
			}
			if len(errList) != 0 {
				recorder.Eventf(&sj, api.EventTypeWarning, "FailedDelete", "Deleted job-pods: %v", utilerrors.NewAggregate(errList))
				return
			}
			// ... and the job itself
			if err := jc.DeleteJob(job.Namespace, job.Name); err != nil {
				recorder.Eventf(&sj, api.EventTypeWarning, "FailedDelete", "Deleted job: %v", err)
				return
			}
			recorder.Eventf(&sj, api.EventTypeNormal, "SuccessfulDelete", "Deleted job %v", j.Name)
		}
	}

	jobReq, err := getJobFromTemplate(&sj, scheduledTime)
	if err != nil {
		glog.Errorf("Unable to make Job from template in %s: %v", nameForLog, err)
		return
	}
	jobResp, err := jc.CreateJob(sj.Namespace, jobReq)
	if err != nil {
		recorder.Eventf(&sj, api.EventTypeWarning, "FailedCreate", "Error creating job: %v", err)
		return
	}
	recorder.Eventf(&sj, api.EventTypeNormal, "SuccessfulCreate", "Created job %v", jobResp.Name)

	// ------------------------------------------------------------------ //

	// If this process restarts at this point (after posting a job, but
	// before updating the status), then we might try to start the job on
	// the next time.  Actually, if we relist the SJs and Jobs on the next
	// iteration of SyncAll, we might not see our own status update, and
	// then post one again.  So, we need to use the job name as a lock to
	// prevent us from making the job twice (name the job with hash of its
	// scheduled time).

	// Add the just-started job to the status list.
	ref, err := getRef(jobResp)
	if err != nil {
		glog.V(2).Infof("Unable to make object reference for job for %s", nameForLog)
	} else {
		sj.Status.Active = append(sj.Status.Active, *ref)
	}
	sj.Status.LastScheduleTime = &unversioned.Time{Time: scheduledTime}
	if _, err := sjc.UpdateStatus(&sj); err != nil {
		glog.Infof("Unable to update status for %s (rv = %s): %v", nameForLog, sj.ResourceVersion, err)
	}

	return
}

func getRef(object runtime.Object) (*api.ObjectReference, error) {
	return api.GetReference(object)
}
<commit_msg>Fix the bug that SJ sees finished jobs as unexpected<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package scheduledjob

/*
I did not use watch or expectations.  Those add a lot of corner cases, and we aren't
expecting a large volume of jobs or scheduledJobs.  (We are favoring correctness
over scalability.  If we find a single controller thread is too slow because
there are a lot of Jobs or ScheduledJobs, we we can parallelize by Namespace.
If we find the load on the API server is too high, we can use a watch and
UndeltaStore.)

Just periodically list jobs and SJs, and then reconcile them.

*/

import (
	"fmt"
	"time"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/batch"
	"k8s.io/kubernetes/pkg/client/record"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/controller/job"
	"k8s.io/kubernetes/pkg/runtime"
	utilerrors "k8s.io/kubernetes/pkg/util/errors"
	"k8s.io/kubernetes/pkg/util/metrics"
	utilruntime "k8s.io/kubernetes/pkg/util/runtime"
	"k8s.io/kubernetes/pkg/util/wait"
)

// Utilities for dealing with Jobs and ScheduledJobs and time.

type ScheduledJobController struct {
	kubeClient *client.Client
	jobControl jobControlInterface
	sjControl  sjControlInterface
	podControl podControlInterface
	recorder   record.EventRecorder
}

func NewScheduledJobController(kubeClient *client.Client) *ScheduledJobController {
	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartLogging(glog.Infof)
	// TODO: remove the wrapper when every clients have moved to use the clientset.
	eventBroadcaster.StartRecordingToSink(kubeClient.Events(""))

	if kubeClient != nil && kubeClient.GetRateLimiter() != nil {
		metrics.RegisterMetricAndTrackRateLimiterUsage("scheduledjob_controller", kubeClient.GetRateLimiter())
	}

	jm := &ScheduledJobController{
		kubeClient: kubeClient,
		jobControl: realJobControl{KubeClient: kubeClient},
		sjControl:  &realSJControl{KubeClient: kubeClient},
		podControl: &realPodControl{KubeClient: kubeClient},
		recorder:   eventBroadcaster.NewRecorder(api.EventSource{Component: "scheduled-job-controller"}),
	}

	return jm
}

func NewScheduledJobControllerFromClient(kubeClient *client.Client) *ScheduledJobController {
	jm := NewScheduledJobController(kubeClient)
	return jm
}

// Run the main goroutine responsible for watching and syncing jobs.
func (jm *ScheduledJobController) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	glog.Infof("Starting ScheduledJob Manager")
	// Check things every 10 second.
	go wait.Until(jm.SyncAll, 10*time.Second, stopCh)
	<-stopCh
	glog.Infof("Shutting down ScheduledJob Manager")
}

// SyncAll lists all the ScheduledJobs and Jobs and reconciles them.
func (jm *ScheduledJobController) SyncAll() {
	sjl, err := jm.kubeClient.Batch().ScheduledJobs(api.NamespaceAll).List(api.ListOptions{})
	if err != nil {
		glog.Errorf("Error listing scheduledjobs: %v", err)
		return
	}
	sjs := sjl.Items
	glog.Infof("Found %d scheduledjobs", len(sjs))

	jl, err := jm.kubeClient.Batch().Jobs(api.NamespaceAll).List(api.ListOptions{})
	if err != nil {
		glog.Errorf("Error listing jobs")
		return
	}
	js := jl.Items
	glog.Infof("Found %d jobs", len(js))

	jobsBySj := groupJobsByParent(sjs, js)
	glog.Infof("Found %d groups", len(jobsBySj))

	for _, sj := range sjs {
		SyncOne(sj, jobsBySj[sj.UID], time.Now(), jm.jobControl, jm.sjControl, jm.podControl, jm.recorder)
	}
}

// SyncOne reconciles a ScheduledJob with a list of any Jobs that it created.
// All known jobs created by "sj" should be included in "js".
// The current time is passed in to facilitate testing.
// It has no receiver, to facilitate testing.
func SyncOne(sj batch.ScheduledJob, js []batch.Job, now time.Time, jc jobControlInterface, sjc sjControlInterface, pc podControlInterface, recorder record.EventRecorder) {
	nameForLog := fmt.Sprintf("%s/%s", sj.Namespace, sj.Name)

	for i := range js {
		j := js[i]
		found := inActiveList(sj, j.ObjectMeta.UID)
		if !found && !job.IsJobFinished(&j) {
			recorder.Eventf(&sj, api.EventTypeWarning, "UnexpectedJob", "Saw a job that the controller did not create or forgot: %v", j.Name)
			// We found an unfinished job that has us as the parent, but it is not in our Active list.
			// This could happen if we crashed right after creating the Job and before updating the status,
			// or if our jobs list is newer than our sj status after a relist, or if someone intentionally created
			// a job that they wanted us to adopt.

			// TODO: maybe handle the adoption case?  Concurrency/suspend rules will not apply in that case, obviously, since we can't
			// stop users from creating jobs if they have permission.  It is assumed that if a
			// user has permission to create a job within a namespace, then they have permission to make any scheduledJob
			// in the same namespace "adopt" that job.  ReplicaSets and their Pods work the same way.
			// TBS: how to update sj.Status.LastScheduleTime if the adopted job is newer than any we knew about?
		} else if found && job.IsJobFinished(&j) {
			deleteFromActiveList(&sj, j.ObjectMeta.UID)
			// TODO: event to call out failure vs success.
			recorder.Eventf(&sj, api.EventTypeNormal, "SawCompletedJob", "Saw completed job: %v", j.Name)
		}
	}
	updatedSJ, err := sjc.UpdateStatus(&sj)
	if err != nil {
		glog.Errorf("Unable to update status for %s (rv = %s): %v", nameForLog, sj.ResourceVersion, err)
	}
	sj = *updatedSJ

	if sj.Spec.Suspend != nil && *sj.Spec.Suspend {
		glog.V(4).Infof("Not starting job for %s because it is suspended", nameForLog)
		return
	}
	times, err := getRecentUnmetScheduleTimes(sj, now)
	if err != nil {
		glog.Errorf("Cannot determine if %s needs to be started: %v", nameForLog, err)
	}
	// TODO: handle multiple unmet start times, from oldest to newest, updating status as needed.
	if len(times) == 0 {
		glog.V(4).Infof("No unmet start times for %s", nameForLog)
		return
	}
	if len(times) > 1 {
		glog.Errorf("Multiple unmet start times for %s so only starting last one", nameForLog)
	}
	scheduledTime := times[len(times)-1]
	tooLate := false
	if sj.Spec.StartingDeadlineSeconds != nil {
		tooLate = scheduledTime.Add(time.Second * time.Duration(*sj.Spec.StartingDeadlineSeconds)).Before(now)
	}
	if tooLate {
		glog.Errorf("Missed starting window for %s", nameForLog)
		// TODO: generate an event for a miss.  Use a warning level event because it indicates a
		// problem with the controller (restart or long queue), and is not expected by user either.
		// Since we don't set LastScheduleTime when not scheduling, we are going to keep noticing
		// the miss every cycle.  In order to avoid sending multiple events, and to avoid processing
		// the sj again and again, we could set a Status.LastMissedTime when we notice a miss.
		// Then, when we call getRecentUnmetScheduleTimes, we can take max(creationTimestamp,
		// Status.LastScheduleTime, Status.LastMissedTime), and then so we won't generate
		// and event the next time we process it, and also so the user looking at the status
		// can see easily that there was a missed execution.
		return
	}
	if sj.Spec.ConcurrencyPolicy == batch.ForbidConcurrent && len(sj.Status.Active) > 0 {
		// Regardless which source of information we use for the set of active jobs,
		// there is some risk that we won't see an active job when there is one.
		// (because we haven't seen the status update to the SJ or the created pod).
		// So it is theoretically possible to have concurrency with Forbid.
		// As long the as the invokations are "far enough apart in time", this usually won't happen.
		//
		// TODO: for Forbid, we could use the same name for every execution, as a lock.
		// With replace, we could use a name that is deterministic per execution time.
		// But that would mean that you could not inspect prior successes or failures of Forbid jobs.
		glog.V(4).Infof("Not starting job for %s because of prior execution still running and concurrency policy is Forbid.", nameForLog)
		return
	}
	if sj.Spec.ConcurrencyPolicy == batch.ReplaceConcurrent {
		for _, j := range sj.Status.Active {
			// TODO: this should be replaced with server side job deletion
			// currently this mimics JobReaper from pkg/kubectl/stop.go
			glog.V(4).Infof("Deleting job %s of %s s that was still running at next scheduled start time", j.Name, nameForLog)
			job, err := jc.GetJob(j.Namespace, j.Name)
			if err != nil {
				recorder.Eventf(&sj, api.EventTypeWarning, "FailedGet", "Get job: %v", err)
				return
			}
			// scale job down to 0
			if *job.Spec.Parallelism != 0 {
				zero := int32(0)
				job.Spec.Parallelism = &zero
				job, err = jc.UpdateJob(job.Namespace, job)
				if err != nil {
					recorder.Eventf(&sj, api.EventTypeWarning, "FailedUpdate", "Update job: %v", err)
					return
				}
			}
			// remove all pods...
			selector, _ := unversioned.LabelSelectorAsSelector(job.Spec.Selector)
			options := api.ListOptions{LabelSelector: selector}
			podList, err := pc.ListPods(job.Namespace, options)
			if err != nil {
				recorder.Eventf(&sj, api.EventTypeWarning, "FailedList", "List job-pods: %v", err)
			}
			errList := []error{}
			for _, pod := range podList.Items {
				if err := pc.DeletePod(pod.Namespace, pod.Name); err != nil {
					// ignores the error when the pod isn't found
					if !errors.IsNotFound(err) {
						errList = append(errList, err)
					}
				}
			}
			if len(errList) != 0 {
				recorder.Eventf(&sj, api.EventTypeWarning, "FailedDelete", "Deleted job-pods: %v", utilerrors.NewAggregate(errList))
				return
			}
			// ... and the job itself
			if err := jc.DeleteJob(job.Namespace, job.Name); err != nil {
				recorder.Eventf(&sj, api.EventTypeWarning, "FailedDelete", "Deleted job: %v", err)
				return
			}
			recorder.Eventf(&sj, api.EventTypeNormal, "SuccessfulDelete", "Deleted job %v", j.Name)
		}
	}

	jobReq, err := getJobFromTemplate(&sj, scheduledTime)
	if err != nil {
		glog.Errorf("Unable to make Job from template in %s: %v", nameForLog, err)
		return
	}
	jobResp, err := jc.CreateJob(sj.Namespace, jobReq)
	if err != nil {
		recorder.Eventf(&sj, api.EventTypeWarning, "FailedCreate", "Error creating job: %v", err)
		return
	}
	recorder.Eventf(&sj, api.EventTypeNormal, "SuccessfulCreate", "Created job %v", jobResp.Name)

	// ------------------------------------------------------------------ //

	// If this process restarts at this point (after posting a job, but
	// before updating the status), then we might try to start the job on
	// the next time.  Actually, if we relist the SJs and Jobs on the next
	// iteration of SyncAll, we might not see our own status update, and
	// then post one again.  So, we need to use the job name as a lock to
	// prevent us from making the job twice (name the job with hash of its
	// scheduled time).

	// Add the just-started job to the status list.
	ref, err := getRef(jobResp)
	if err != nil {
		glog.V(2).Infof("Unable to make object reference for job for %s", nameForLog)
	} else {
		sj.Status.Active = append(sj.Status.Active, *ref)
	}
	sj.Status.LastScheduleTime = &unversioned.Time{Time: scheduledTime}
	if _, err := sjc.UpdateStatus(&sj); err != nil {
		glog.Infof("Unable to update status for %s (rv = %s): %v", nameForLog, sj.ResourceVersion, err)
	}

	return
}

func getRef(object runtime.Object) (*api.ObjectReference, error) {
	return api.GetReference(object)
}
<|endoftext|>


<commit_before>package management

import (
	"fmt"
	"os"
	"reflect"
	"strings"

	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/types/config"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	Amazonec2driver    = "amazonec2"
	Azuredriver        = "azure"
	DigitalOceandriver = "digitalocean"
	ExoscaleDriver     = "exoscale"
	HarvesterDriver    = "harvester"
	Linodedriver       = "linode"
	OCIDriver          = "oci"
	OTCDriver          = "otc"
	OpenstackDriver    = "openstack"
	PacketDriver       = "packet"
	PhoenixNAPDriver   = "pnap"
	RackspaceDriver    = "rackspace"
	SoftLayerDriver    = "softlayer"
	Vmwaredriver       = "vmwarevsphere"
	GoogleDriver       = "google"
)

var DriverData = map[string]map[string][]string{
	Amazonec2driver:    {"publicCredentialFields": []string{"accessKey"}, "privateCredentialFields": []string{"secretKey"}},
	Azuredriver:        {"publicCredentialFields": []string{"clientId", "subscriptionId", "tenantId", "environment"}, "privateCredentialFields": []string{"clientSecret"}, "optionalCredentialFields": []string{"tenantId"}},
	DigitalOceandriver: {"privateCredentialFields": []string{"accessToken"}},
	ExoscaleDriver:     {"privateCredentialFields": []string{"apiSecretKey"}},
	HarvesterDriver:    {"publicCredentialFields": []string{"clusterType", "clusterId"}, "privateCredentialFields": []string{"kubeconfigContent"}, "optionalCredentialFields": []string{"clusterId"}},
	Linodedriver:       {"privateCredentialFields": []string{"token"}, "passwordFields": []string{"rootPass"}},
	OCIDriver:          {"publicCredentialFields": []string{"tenancyId", "userId", "fingerprint"}, "privateCredentialFields": []string{"privateKeyContents"}, "passwordFields": []string{"privateKeyPassphrase"}},
	OTCDriver:          {"privateCredentialFields": []string{"accessKeySecret"}},
	OpenstackDriver:    {"privateCredentialFields": []string{"password"}},
	PacketDriver:       {"privateCredentialFields": []string{"apiKey"}},
	PhoenixNAPDriver:   {"publicCredentialFields": []string{"clientIdentifier"}, "privateCredentialFields": []string{"clientSecret"}},
	RackspaceDriver:    {"privateCredentialFields": []string{"apiKey"}},
	SoftLayerDriver:    {"privateCredentialFields": []string{"apiKey"}},
	Vmwaredriver:       {"publicCredentialFields": []string{"username", "vcenter", "vcenterPort"}, "privateCredentialFields": []string{"password"}},
	GoogleDriver:       {"privateCredentialFields": []string{"authEncodedJson"}},
}

var driverDefaults = map[string]map[string]string{
	HarvesterDriver: {"clusterType": "imported"},
	Vmwaredriver:    {"vcenterPort": "443"},
}

type machineDriverCompare struct {
	builtin            bool
	addCloudCredential bool
	url                string
	uiURL              string
	checksum           string
	name               string
	whitelist          []string
	annotations        map[string]string
}

func addMachineDrivers(management *config.ManagementContext) error {
	if err := addMachineDriver("pinganyunecs", "https://drivers.rancher.cn/node-driver-pinganyun/0.3.0/docker-machine-driver-pinganyunecs-linux.tgz", "https://drivers.rancher.cn/node-driver-pinganyun/0.3.0/component.js", "f84ccec11c2c1970d76d30150916933efe8ca49fe4c422c8954fc37f71273bb5", []string{"drivers.rancher.cn"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("aliyunecs", "https://drivers.rancher.cn/node-driver-aliyun/1.0.4/docker-machine-driver-aliyunecs.tgz", "", "5990d40d71c421a85563df9caf069466f300cd75723effe4581751b0de9a6a0e", []string{"ecs.aliyuncs.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(Amazonec2driver, "local://", "", "",
		[]string{"iam.amazonaws.com", "iam.us-gov.amazonaws.com", "iam.%.amazonaws.com.cn", "ec2.%.amazonaws.com", "ec2.%.amazonaws.com.cn", "eks.%.amazonaws.com", "eks.%.amazonaws.com.cn", "kms.%.amazonaws.com", "kms.%.amazonaws.com.cn"},
		true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(Azuredriver, "local://", "", "", nil, true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("cloudca", "https://github.com/cloud-ca/docker-machine-driver-cloudca/files/2446837/docker-machine-driver-cloudca_v2.0.0_linux-amd64.zip", "https://objects-east.cloud.ca/v1/5ef827605f884961b94881e928e7a250/ui-driver-cloudca/v2.1.2/component.js", "2a55efd6d62d5f7fd27ce877d49596f4", []string{"objects-east.cloud.ca"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("cloudscale", "https://github.com/cloudscale-ch/docker-machine-driver-cloudscale/releases/download/v1.2.0/docker-machine-driver-cloudscale_v1.2.0_linux_amd64.tar.gz", "https://objects.rma.cloudscale.ch/cloudscale-rancher-v2-ui-driver/component.js", "e33fbd6c2f87b1c470bcb653cc8aa50baf914a9d641a2f18f86a07c398cfb544", []string{"objects.rma.cloudscale.ch"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(DigitalOceandriver, "local://", "", "", []string{"api.digitalocean.com"}, true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(ExoscaleDriver, "local://", "", "", []string{"api.exoscale.ch"}, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(GoogleDriver, "local://", "", "", nil, false, true, true, management); err != nil {
		return err
	}
	if err := addMachineDriver(HarvesterDriver, "https://releases.rancher.com/harvester-node-driver/v0.3.2/docker-machine-driver-harvester-amd64.tar.gz", "", "f113d826b8bee79abf60f1026b4076befdf735c6675889240c75da6b30f09971", []string{"releases.rancher.com"}, false, false, false, management); err != nil {
		return err
	}
	linodeBuiltin := true
	if dl := os.Getenv("CATTLE_DEV_MODE"); dl != "" {
		linodeBuiltin = false
	}
	if err := addMachineDriver(Linodedriver, "https://github.com/linode/docker-machine-driver-linode/releases/download/v0.1.8/docker-machine-driver-linode_linux-amd64.zip", "/assets/rancher-ui-driver-linode/component.js", "b31b6a504c59ee758d2dda83029fe4a85b3f5601e22dfa58700a5e6c8f450dc7", []string{"api.linode.com"}, linodeBuiltin, linodeBuiltin, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OCIDriver, "https://github.com/rancher-plugins/rancher-machine-driver-oci/releases/download/v1.1.0/docker-machine-driver-oci-linux", "", "c19b109050e56a451f93c3390cc9d015da985a19d21bd0def858430be3324fd2", []string{"*.oraclecloud.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OpenstackDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OTCDriver, "https://github.com/rancher-plugins/docker-machine-driver-otc/releases/download/v2019.5.7/docker-machine-driver-otc", "", "3f793ebb0ebd9477b9166ec542f77e25", nil, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(PacketDriver, "https://github.com/packethost/docker-machine-driver-packet/releases/download/v0.2.2/docker-machine-driver-packet_linux-amd64.zip", "https://packethost.github.io/ui-driver-packet/1.0.2/component.js", "e03c6bc9406c811e03e9bc2c39f43e6cc8c02d1615bd0e0b8ee1b38be6fe201c", []string{"api.packet.net", "packethost.github.io"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(PhoenixNAPDriver, "https://github.com/phoenixnap/docker-machine-driver-pnap/releases/download/v0.1.0/docker-machine-driver-pnap_0.1.0_linux_amd64.zip", "", "5f25a7fbcaca0710b7290216464ca8433fa3d683b59d5e4e674bef2d0a3ff6c7", []string{"api.securedservers.com", "api.phoenixnap.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(RackspaceDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(SoftLayerDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	return addMachineDriver(Vmwaredriver, "local://", "", "", nil, true, true, false, management)
}

func addMachineDriver(name, url, uiURL, checksum string, whitelist []string, active, builtin, addCloudCredential bool, management *config.ManagementContext) error {
	lister := management.Management.NodeDrivers("").Controller().Lister()
	cli := management.Management.NodeDrivers("")
	m, _ := lister.Get("", name)
	// annotations can have keys cred and password, values []string to be considered as a part of cloud credential
	annotations := map[string]string{}
	if m != nil {
		for k, v := range m.Annotations {
			annotations[k] = v
		}
	}
	for key, fields := range DriverData[name] {
		annotations[key] = strings.Join(fields, ",")
	}
	defaults := []string{}
	for key, val := range driverDefaults[name] {
		defaults = append(defaults, fmt.Sprintf("%s:%s", key, val))
	}
	if len(defaults) > 0 {
		annotations["defaults"] = strings.Join(defaults, ",")
	}
	if m != nil {
		old := machineDriverCompare{
			builtin:            m.Spec.Builtin,
			addCloudCredential: m.Spec.AddCloudCredential,
			url:                m.Spec.URL,
			uiURL:              m.Spec.UIURL,
			checksum:           m.Spec.Checksum,
			name:               m.Spec.DisplayName,
			whitelist:          m.Spec.WhitelistDomains,
			annotations:        m.Annotations,
		}
		new := machineDriverCompare{
			builtin:            builtin,
			addCloudCredential: addCloudCredential,
			url:                url,
			uiURL:              uiURL,
			checksum:           checksum,
			name:               name,
			whitelist:          whitelist,
			annotations:        annotations,
		}
		if !reflect.DeepEqual(new, old) {
			logrus.Infof("Updating node driver %v", name)
			m.Spec.Builtin = builtin
			m.Spec.AddCloudCredential = addCloudCredential
			m.Spec.URL = url
			m.Spec.UIURL = uiURL
			m.Spec.Checksum = checksum
			m.Spec.DisplayName = name
			m.Spec.WhitelistDomains = whitelist
			m.Annotations = annotations
			_, err := cli.Update(m)
			return err
		}
		return nil
	}

	logrus.Infof("Creating node driver %v", name)
	_, err := cli.Create(&v3.NodeDriver{
		ObjectMeta: v1.ObjectMeta{
			Name:        name,
			Annotations: annotations,
		},
		Spec: v32.NodeDriverSpec{
			Active:             active,
			Builtin:            builtin,
			AddCloudCredential: addCloudCredential,
			URL:                url,
			UIURL:              uiURL,
			DisplayName:        name,
			Checksum:           checksum,
			WhitelistDomains:   whitelist,
		},
	})

	return err
}
<commit_msg>update URLs and sha256 for Equinix Metal (Packet) drivers<commit_after>package management

import (
	"fmt"
	"os"
	"reflect"
	"strings"

	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/types/config"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	Amazonec2driver    = "amazonec2"
	Azuredriver        = "azure"
	DigitalOceandriver = "digitalocean"
	ExoscaleDriver     = "exoscale"
	HarvesterDriver    = "harvester"
	Linodedriver       = "linode"
	OCIDriver          = "oci"
	OTCDriver          = "otc"
	OpenstackDriver    = "openstack"
	PacketDriver       = "packet"
	PhoenixNAPDriver   = "pnap"
	RackspaceDriver    = "rackspace"
	SoftLayerDriver    = "softlayer"
	Vmwaredriver       = "vmwarevsphere"
	GoogleDriver       = "google"
)

var DriverData = map[string]map[string][]string{
	Amazonec2driver:    {"publicCredentialFields": []string{"accessKey"}, "privateCredentialFields": []string{"secretKey"}},
	Azuredriver:        {"publicCredentialFields": []string{"clientId", "subscriptionId", "tenantId", "environment"}, "privateCredentialFields": []string{"clientSecret"}, "optionalCredentialFields": []string{"tenantId"}},
	DigitalOceandriver: {"privateCredentialFields": []string{"accessToken"}},
	ExoscaleDriver:     {"privateCredentialFields": []string{"apiSecretKey"}},
	HarvesterDriver:    {"publicCredentialFields": []string{"clusterType", "clusterId"}, "privateCredentialFields": []string{"kubeconfigContent"}, "optionalCredentialFields": []string{"clusterId"}},
	Linodedriver:       {"privateCredentialFields": []string{"token"}, "passwordFields": []string{"rootPass"}},
	OCIDriver:          {"publicCredentialFields": []string{"tenancyId", "userId", "fingerprint"}, "privateCredentialFields": []string{"privateKeyContents"}, "passwordFields": []string{"privateKeyPassphrase"}},
	OTCDriver:          {"privateCredentialFields": []string{"accessKeySecret"}},
	OpenstackDriver:    {"privateCredentialFields": []string{"password"}},
	PacketDriver:       {"privateCredentialFields": []string{"apiKey"}},
	PhoenixNAPDriver:   {"publicCredentialFields": []string{"clientIdentifier"}, "privateCredentialFields": []string{"clientSecret"}},
	RackspaceDriver:    {"privateCredentialFields": []string{"apiKey"}},
	SoftLayerDriver:    {"privateCredentialFields": []string{"apiKey"}},
	Vmwaredriver:       {"publicCredentialFields": []string{"username", "vcenter", "vcenterPort"}, "privateCredentialFields": []string{"password"}},
	GoogleDriver:       {"privateCredentialFields": []string{"authEncodedJson"}},
}

var driverDefaults = map[string]map[string]string{
	HarvesterDriver: {"clusterType": "imported"},
	Vmwaredriver:    {"vcenterPort": "443"},
}

type machineDriverCompare struct {
	builtin            bool
	addCloudCredential bool
	url                string
	uiURL              string
	checksum           string
	name               string
	whitelist          []string
	annotations        map[string]string
}

func addMachineDrivers(management *config.ManagementContext) error {
	if err := addMachineDriver("pinganyunecs", "https://drivers.rancher.cn/node-driver-pinganyun/0.3.0/docker-machine-driver-pinganyunecs-linux.tgz", "https://drivers.rancher.cn/node-driver-pinganyun/0.3.0/component.js", "f84ccec11c2c1970d76d30150916933efe8ca49fe4c422c8954fc37f71273bb5", []string{"drivers.rancher.cn"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("aliyunecs", "https://drivers.rancher.cn/node-driver-aliyun/1.0.4/docker-machine-driver-aliyunecs.tgz", "", "5990d40d71c421a85563df9caf069466f300cd75723effe4581751b0de9a6a0e", []string{"ecs.aliyuncs.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(Amazonec2driver, "local://", "", "",
		[]string{"iam.amazonaws.com", "iam.us-gov.amazonaws.com", "iam.%.amazonaws.com.cn", "ec2.%.amazonaws.com", "ec2.%.amazonaws.com.cn", "eks.%.amazonaws.com", "eks.%.amazonaws.com.cn", "kms.%.amazonaws.com", "kms.%.amazonaws.com.cn"},
		true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(Azuredriver, "local://", "", "", nil, true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("cloudca", "https://github.com/cloud-ca/docker-machine-driver-cloudca/files/2446837/docker-machine-driver-cloudca_v2.0.0_linux-amd64.zip", "https://objects-east.cloud.ca/v1/5ef827605f884961b94881e928e7a250/ui-driver-cloudca/v2.1.2/component.js", "2a55efd6d62d5f7fd27ce877d49596f4", []string{"objects-east.cloud.ca"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("cloudscale", "https://github.com/cloudscale-ch/docker-machine-driver-cloudscale/releases/download/v1.2.0/docker-machine-driver-cloudscale_v1.2.0_linux_amd64.tar.gz", "https://objects.rma.cloudscale.ch/cloudscale-rancher-v2-ui-driver/component.js", "e33fbd6c2f87b1c470bcb653cc8aa50baf914a9d641a2f18f86a07c398cfb544", []string{"objects.rma.cloudscale.ch"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(DigitalOceandriver, "local://", "", "", []string{"api.digitalocean.com"}, true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(ExoscaleDriver, "local://", "", "", []string{"api.exoscale.ch"}, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(GoogleDriver, "local://", "", "", nil, false, true, true, management); err != nil {
		return err
	}
	if err := addMachineDriver(HarvesterDriver, "https://releases.rancher.com/harvester-node-driver/v0.3.2/docker-machine-driver-harvester-amd64.tar.gz", "", "f113d826b8bee79abf60f1026b4076befdf735c6675889240c75da6b30f09971", []string{"releases.rancher.com"}, false, false, false, management); err != nil {
		return err
	}
	linodeBuiltin := true
	if dl := os.Getenv("CATTLE_DEV_MODE"); dl != "" {
		linodeBuiltin = false
	}
	if err := addMachineDriver(Linodedriver, "https://github.com/linode/docker-machine-driver-linode/releases/download/v0.1.8/docker-machine-driver-linode_linux-amd64.zip", "/assets/rancher-ui-driver-linode/component.js", "b31b6a504c59ee758d2dda83029fe4a85b3f5601e22dfa58700a5e6c8f450dc7", []string{"api.linode.com"}, linodeBuiltin, linodeBuiltin, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OCIDriver, "https://github.com/rancher-plugins/rancher-machine-driver-oci/releases/download/v1.1.0/docker-machine-driver-oci-linux", "", "c19b109050e56a451f93c3390cc9d015da985a19d21bd0def858430be3324fd2", []string{"*.oraclecloud.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OpenstackDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OTCDriver, "https://github.com/rancher-plugins/docker-machine-driver-otc/releases/download/v2019.5.7/docker-machine-driver-otc", "", "3f793ebb0ebd9477b9166ec542f77e25", nil, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(PacketDriver, "https://github.com/equinix/docker-machine-driver-metal/releases/download/v0.6.0/docker-machine-driver-metal_linux-amd64.zip", "https://rancher-drivers.equinixmetal.net/1.0.2/component.js", "fad5e551a35d2ef2db742b07ca6d61bb9c9b574d322d3000f0c557d5fb90a734", []string{"api.packet.net", "api.equinix.com", "rancher-drivers.equinixmetal.net"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(PhoenixNAPDriver, "https://github.com/phoenixnap/docker-machine-driver-pnap/releases/download/v0.1.0/docker-machine-driver-pnap_0.1.0_linux_amd64.zip", "", "5f25a7fbcaca0710b7290216464ca8433fa3d683b59d5e4e674bef2d0a3ff6c7", []string{"api.securedservers.com", "api.phoenixnap.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(RackspaceDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(SoftLayerDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	return addMachineDriver(Vmwaredriver, "local://", "", "", nil, true, true, false, management)
}

func addMachineDriver(name, url, uiURL, checksum string, whitelist []string, active, builtin, addCloudCredential bool, management *config.ManagementContext) error {
	lister := management.Management.NodeDrivers("").Controller().Lister()
	cli := management.Management.NodeDrivers("")
	m, _ := lister.Get("", name)
	// annotations can have keys cred and password, values []string to be considered as a part of cloud credential
	annotations := map[string]string{}
	if m != nil {
		for k, v := range m.Annotations {
			annotations[k] = v
		}
	}
	for key, fields := range DriverData[name] {
		annotations[key] = strings.Join(fields, ",")
	}
	defaults := []string{}
	for key, val := range driverDefaults[name] {
		defaults = append(defaults, fmt.Sprintf("%s:%s", key, val))
	}
	if len(defaults) > 0 {
		annotations["defaults"] = strings.Join(defaults, ",")
	}
	if m != nil {
		old := machineDriverCompare{
			builtin:            m.Spec.Builtin,
			addCloudCredential: m.Spec.AddCloudCredential,
			url:                m.Spec.URL,
			uiURL:              m.Spec.UIURL,
			checksum:           m.Spec.Checksum,
			name:               m.Spec.DisplayName,
			whitelist:          m.Spec.WhitelistDomains,
			annotations:        m.Annotations,
		}
		new := machineDriverCompare{
			builtin:            builtin,
			addCloudCredential: addCloudCredential,
			url:                url,
			uiURL:              uiURL,
			checksum:           checksum,
			name:               name,
			whitelist:          whitelist,
			annotations:        annotations,
		}
		if !reflect.DeepEqual(new, old) {
			logrus.Infof("Updating node driver %v", name)
			m.Spec.Builtin = builtin
			m.Spec.AddCloudCredential = addCloudCredential
			m.Spec.URL = url
			m.Spec.UIURL = uiURL
			m.Spec.Checksum = checksum
			m.Spec.DisplayName = name
			m.Spec.WhitelistDomains = whitelist
			m.Annotations = annotations
			_, err := cli.Update(m)
			return err
		}
		return nil
	}

	logrus.Infof("Creating node driver %v", name)
	_, err := cli.Create(&v3.NodeDriver{
		ObjectMeta: v1.ObjectMeta{
			Name:        name,
			Annotations: annotations,
		},
		Spec: v32.NodeDriverSpec{
			Active:             active,
			Builtin:            builtin,
			AddCloudCredential: addCloudCredential,
			URL:                url,
			UIURL:              uiURL,
			DisplayName:        name,
			Checksum:           checksum,
			WhitelistDomains:   whitelist,
		},
	})

	return err
}
<|endoftext|>