You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2018/03/01 19:49:53 UTC

qpid-proton git commit: PROTON-1696 [go] reliable SASL config and test initialization

Repository: qpid-proton
Updated Branches:
  refs/heads/master 1b9c09582 -> 817609a54


PROTON-1696 [go] reliable SASL config and test initialization

- lock access to global SASL config variables to ensure no races.
- panic if SASL config is called too late (lost config)
- do SASL init before starting tests to avoid ordering and parallel test
  problems with on-demand initialization.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/817609a5
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/817609a5
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/817609a5

Branch: refs/heads/master
Commit: 817609a5476aea7219e432a04962e8ba194a8ad0
Parents: 1b9c095
Author: Alan Conway <ac...@redhat.com>
Authored: Thu Mar 1 14:11:23 2018 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Thu Mar 1 14:48:27 2018 -0500

----------------------------------------------------------------------
 .../src/qpid.apache.org/electron/auth_test.go   | 92 +++++++++++---------
 .../src/qpid.apache.org/electron/connection.go  | 65 +++++++++-----
 2 files changed, 92 insertions(+), 65 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/817609a5/proton-c/bindings/go/src/qpid.apache.org/electron/auth_test.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/electron/auth_test.go b/proton-c/bindings/go/src/qpid.apache.org/electron/auth_test.go
index 9fa9fa2..77b033c 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/electron/auth_test.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/electron/auth_test.go
@@ -48,7 +48,6 @@ func testAuthClientServer(t *testing.T, copts []ConnectionOption, sopts []Connec
 }
 
 func TestAuthAnonymous(t *testing.T) {
-	configureSASL()
 	got, err := testAuthClientServer(t,
 		[]ConnectionOption{User("fred"), VirtualHost("vhost"), SASLAllowInsecure(true)},
 		[]ConnectionOption{SASLAllowedMechs("ANONYMOUS"), SASLAllowInsecure(true)})
@@ -57,10 +56,7 @@ func TestAuthAnonymous(t *testing.T) {
 }
 
 func TestAuthPlain(t *testing.T) {
-	if !SASLExtended() {
-		t.Skip()
-	}
-	fatalIf(t, configureSASL())
+	extendedSASL.startTest(t)
 	got, err := testAuthClientServer(t,
 		[]ConnectionOption{SASLAllowInsecure(true), SASLAllowedMechs("PLAIN"), User("fred@proton"), Password([]byte("xxx"))},
 		[]ConnectionOption{SASLAllowInsecure(true), SASLAllowedMechs("PLAIN")})
@@ -69,10 +65,7 @@ func TestAuthPlain(t *testing.T) {
 }
 
 func TestAuthBadPass(t *testing.T) {
-	if !SASLExtended() {
-		t.Skip()
-	}
-	fatalIf(t, configureSASL())
+	extendedSASL.startTest(t)
 	_, err := testAuthClientServer(t,
 		[]ConnectionOption{SASLAllowInsecure(true), SASLAllowedMechs("PLAIN"), User("fred@proton"), Password([]byte("yyy"))},
 		[]ConnectionOption{SASLAllowInsecure(true), SASLAllowedMechs("PLAIN")})
@@ -82,10 +75,7 @@ func TestAuthBadPass(t *testing.T) {
 }
 
 func TestAuthBadUser(t *testing.T) {
-	if !SASLExtended() {
-		t.Skip()
-	}
-	fatalIf(t, configureSASL())
+	extendedSASL.startTest(t)
 	_, err := testAuthClientServer(t,
 		[]ConnectionOption{SASLAllowInsecure(true), SASLAllowedMechs("PLAIN"), User("foo@bar"), Password([]byte("yyy"))},
 		[]ConnectionOption{SASLAllowInsecure(true), SASLAllowedMechs("PLAIN")})
@@ -94,44 +84,60 @@ func TestAuthBadUser(t *testing.T) {
 	}
 }
 
-var confDir string
-var confErr error
+type extendedSASLState struct {
+	err error
+	dir string
+}
 
-func configureSASL() error {
-	if confDir != "" || confErr != nil {
-		return confErr
-	}
-	confDir, confErr = ioutil.TempDir("", "")
-	if confErr != nil {
-		return confErr
+func (s *extendedSASLState) setup() {
+	if SASLExtended() {
+		if s.dir, s.err = ioutil.TempDir("", ""); s.err == nil {
+			GlobalSASLConfigDir(s.dir)
+			GlobalSASLConfigName("test")
+			conf := filepath.Join(s.dir, "test.conf")
+			db := filepath.Join(s.dir, "proton.sasldb")
+			saslpasswd := os.Getenv("SASLPASSWD")
+			if saslpasswd == "" {
+				saslpasswd = "saslpasswd2"
+			}
+			cmd := exec.Command(saslpasswd, "-c", "-p", "-f", db, "-u", "proton", "fred")
+			cmd.Stdin = strings.NewReader("xxx") // Password
+			if _, s.err = cmd.CombinedOutput(); s.err == nil {
+				confStr := fmt.Sprintf(`
+sasldb_path: %s
+mech_list: EXTERNAL DIGEST-MD5 SCRAM-SHA-1 CRAM-MD5 PLAIN ANONYMOUS
+`, db)
+				s.err = ioutil.WriteFile(conf, []byte(confStr), os.ModePerm)
+			}
+		}
 	}
+	// Note we don't do anything with s.err now, tests that need the
+	// extended SASL config will fail if s.err != nil. If no such tests
+	// are run then it is not an error that we couldn't set it up.
+}
 
-	GlobalSASLConfigDir(confDir)
-	GlobalSASLConfigName("test")
-	conf := filepath.Join(confDir, "test.conf")
-
-	db := filepath.Join(confDir, "proton.sasldb")
-        saslpasswd := os.Getenv("SASLPASSWD");
-        if saslpasswd == "" {
-            saslpasswd = "saslpasswd2"
-        }
-	cmd := exec.Command(saslpasswd, "-c", "-p", "-f", db, "-u", "proton", "fred")
-	cmd.Stdin = strings.NewReader("xxx") // Password
-	if out, err := cmd.CombinedOutput(); err != nil {
-		confErr = fmt.Errorf("saslpasswd2 failed: %s\n%s", err, out)
-		return confErr
+func (s extendedSASLState) teardown() {
+	if s.dir != "" {
+		_ = os.RemoveAll(s.dir)
 	}
-	confStr := "sasldb_path: " + db + "\nmech_list: EXTERNAL DIGEST-MD5 SCRAM-SHA-1 CRAM-MD5 PLAIN ANONYMOUS\n"
-	if err := ioutil.WriteFile(conf, []byte(confStr), os.ModePerm); err != nil {
-		confErr = fmt.Errorf("write conf file %s failed: %s", conf, err)
+}
+
+func (s extendedSASLState) startTest(t *testing.T) {
+	if SASLExtended() {
+		fatalIf(t, extendedSASL.err)
+	} else {
+		t.Skip()
 	}
-	return confErr
 }
 
+var extendedSASL extendedSASLState
+
 func TestMain(m *testing.M) {
+	// Do global SASL setup/teardown in main.
+	// Doing it on-demand makes the tests fragile to parallel test runs and
+	// changes in test ordering.
+	extendedSASL.setup()
 	status := m.Run()
-	if confDir != "" {
-		_ = os.RemoveAll(confDir)
-	}
+	extendedSASL.teardown()
 	os.Exit(status)
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/817609a5/proton-c/bindings/go/src/qpid.apache.org/electron/connection.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/electron/connection.go b/proton-c/bindings/go/src/qpid.apache.org/electron/connection.go
index 731e64d..9c0ef31 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/electron/connection.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/electron/connection.go
@@ -196,8 +196,7 @@ func NewConnection(conn net.Conn, opts ...ConnectionOption) (*connection, error)
 		c.container = NewContainer("").(*container)
 	}
 	c.pConnection.SetContainer(c.container.Id())
-	globalSASLInit(c.engine)
-
+	saslConfig.setup(c.engine)
 	c.endpoint.init(c.engine.String())
 	go c.run()
 	return c, nil
@@ -356,13 +355,52 @@ func Heartbeat(delay time.Duration) ConnectionOption {
 	return func(c *connection) { c.engine.Transport().SetIdleTimeout(2 * delay) }
 }
 
+type saslConfigState struct {
+	lock        sync.Mutex
+	name        string
+	dir         string
+	initialized bool
+}
+
+func (s *saslConfigState) set(target *string, value string) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	if s.initialized {
+		panic("SASL configuration cannot be changed after a Connection has been created")
+	}
+	*target = value
+}
+
+// Apply the global SASL configuration the first time a proton.Engine needs it
+//
+// TODO aconway 2016-09-15: Current pn_sasl C impl config is broken, so all we
+// can realistically offer is global configuration. Later if/when the pn_sasl C
+// impl is fixed we can offer per connection over-rides.
+func (s *saslConfigState) setup(eng *proton.Engine) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	if !s.initialized {
+		s.initialized = true
+		sasl := eng.Transport().SASL()
+		if s.name != "" {
+			sasl.ConfigName(saslConfig.name)
+		}
+		if s.dir != "" {
+			sasl.ConfigPath(saslConfig.dir)
+		}
+	}
+}
+
+var saslConfig saslConfigState
+
 // GlobalSASLConfigDir sets the SASL configuration directory for every
 // Connection created in this process. If not called, the default is determined
 // by your SASL installation.
 //
 // You can set SASLAllowInsecure and SASLAllowedMechs on individual connections.
 //
-func GlobalSASLConfigDir(dir string) { globalSASLConfigDir = dir }
+// Must be called at most once, before any connections are created.
+func GlobalSASLConfigDir(dir string) { saslConfig.set(&saslConfig.dir, dir) }
 
 // GlobalSASLConfigName sets the SASL configuration name for every Connection
 // created in this process. If not called the default is "proton-server".
@@ -372,7 +410,8 @@ func GlobalSASLConfigDir(dir string) { globalSASLConfigDir = dir }
 //
 // You can set SASLAllowInsecure and SASLAllowedMechs on individual connections.
 //
-func GlobalSASLConfigName(dir string) { globalSASLConfigName = dir }
+// Must be called at most once, before any connections are created.
+func GlobalSASLConfigName(name string) { saslConfig.set(&saslConfig.name, name) }
 
 // Do we support extended SASL negotiation?
 // All implementations of Proton support ANONYMOUS and EXTERNAL on both
@@ -382,24 +421,6 @@ func GlobalSASLConfigName(dir string) { globalSASLConfigName = dir }
 // to support other mechanisms beyond these basic ones.
 func SASLExtended() bool { return proton.SASLExtended() }
 
-var (
-	globalSASLConfigName string
-	globalSASLConfigDir  string
-)
-
-// TODO aconway 2016-09-15: Current pn_sasl C impl config is broken, so all we
-// can realistically offer is global configuration. Later if/when the pn_sasl C
-// impl is fixed we can offer per connection over-rides.
-func globalSASLInit(eng *proton.Engine) {
-	sasl := eng.Transport().SASL()
-	if globalSASLConfigName != "" {
-		sasl.ConfigName(globalSASLConfigName)
-	}
-	if globalSASLConfigDir != "" {
-		sasl.ConfigPath(globalSASLConfigDir)
-	}
-}
-
 // Dial is shorthand for using net.Dial() then NewConnection()
 // See net.Dial() for the meaning of the network, address arguments.
 func Dial(network, address string, opts ...ConnectionOption) (c Connection, err error) {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org