Repository: open-telemetry/opamp-go
Pull request #217 (2023-12-14 07:36:20)

feat: allow to get the network address server is listening on
Add an `Addr()` method to fetch the network address Server is listening on. I think this may be useful for the supervisor to start a server with 0 port to allocate an ephemeral port and get the allocated port for rendering an agent config.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) Report
Attention: `129 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`efddaa2`)](https://app.codecov.io/gh/open-telemetry/opamp-go/commit/efddaa260895e7ebb2431deeefef3a3380d8d902?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) 76.11% compared to head [(`0e36a9d`)](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) 72.50%.
> Report is 31 commits behind head on main.

| [Files](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) | Patch % | Lines |
|---|---|---|
| [internal/certs.go](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-aW50ZXJuYWwvY2VydHMuZ28=) | 0.00% | [118 Missing :warning: ](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) |
| [client/internal/httpsender.go](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-Y2xpZW50L2ludGVybmFsL2h0dHBzZW5kZXIuZ28=) | 88.00% | [2 Missing and 1 partial :warning: ](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) |
| [client/internal/mockserver.go](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-Y2xpZW50L2ludGVybmFsL21vY2tzZXJ2ZXIuZ28=) | 85.71% | [2 Missing and 1 partial :warning: ](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) |
| [server/httpconnection.go](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-c2VydmVyL2h0dHBjb25uZWN0aW9uLmdv) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) |
| [client/internal/clientcommon.go](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-Y2xpZW50L2ludGVybmFsL2NsaWVudGNvbW1vbi5nbw==) | 90.90% | [1 Missing :warning: ](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) |
| [client/internal/packagessyncer.go](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-Y2xpZW50L2ludGVybmFsL3BhY2thZ2Vzc3luY2VyLmdv) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) |
| [server/serverimpl.go](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-c2VydmVyL3NlcnZlcmltcGwuZ28=) | 95.65% | [0 Missing and 1 partial :warning: ](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #217      +/-   ##
==========================================
- Coverage   76.11%   72.50%   -3.62%     
==========================================
  Files          24       25       +1     
  Lines        1834     2022     +188     
==========================================
+ Hits         1396     1466      +70     
- Misses        326      446     +120     
+ Partials      112      110       -2     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/open-telemetry/opamp-go/pull/217?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry).


Patch:
diff --git a/server/server.go b/server/server.go
index d90db7b..929f867 100644
--- a/server/server.go
+++ b/server/server.go
@@ -62,4 +62,9 @@ type OpAMPServer interface {
 	// Stop accepting new connections and close all current connections. This should
 	// block until all connections are closed.
 	Stop(ctx context.Context) error
+
+	// Addr returns the network address Server is listening on. Nil if not started.
+	// Typically used to fetch the port when ListenEndpoint's port is specified as 0 to
+	// allocate an ephemeral port.
+	Addr() net.Addr
 }
diff --git a/server/serverimpl.go b/server/serverimpl.go
index 1c6a3f3..27a9587 100644
--- a/server/serverimpl.go
+++ b/server/serverimpl.go
@@ -40,6 +40,9 @@ type server struct {
 	// The listening HTTP Server after successful Start() call. Nil if Start()
 	// is not called or was not successful.
 	httpServer *http.Server
+
+	// The network address Server is listening on. Nil if not started.
+	addr net.Addr
 }
 
 var _ OpAMPServer = (*server)(nil)
@@ -118,6 +121,7 @@ func (s *server) startHttpServer(listenAddr string, serveFunc func(l net.Listene
 	if err != nil {
 		return err
 	}
+	s.addr = ln.Addr()
 
 	// Begin serving connections in the background.
 	go func() {
@@ -143,6 +147,10 @@ func (s *server) Stop(ctx context.Context) error {
 	return nil
 }
 
+func (s *server) Addr() net.Addr {
+	return s.addr
+}
+
 func (s *server) httpHandler(w http.ResponseWriter, req *http.Request) {
 	var connectionCallbacks serverTypes.ConnectionCallbacks
 	if s.settings.Callbacks != nil {


Test patch:
diff --git a/server/serverimpl_test.go b/server/serverimpl_test.go
index 04827d3..de08ccb 100644
--- a/server/serverimpl_test.go
+++ b/server/serverimpl_test.go
@@ -57,6 +57,48 @@ func TestServerStartStop(t *testing.T) {
 	assert.NoError(t, err)
 }
 
+func TestServerAddrWithNonZeroPort(t *testing.T) {
+	srv := New(&sharedinternal.NopLogger{})
+	require.NotNil(t, srv)
+
+	// Nil if not started
+	assert.Nil(t, srv.Addr())
+
+	addr := testhelpers.GetAvailableLocalAddress()
+
+	err := srv.Start(StartSettings{
+		ListenEndpoint: addr,
+		ListenPath:     "/",
+	})
+	assert.NoError(t, err)
+
+	assert.Equal(t, addr, srv.Addr().String())
+
+	err = srv.Stop(context.Background())
+	assert.NoError(t, err)
+}
+
+func TestServerAddrWithZeroPort(t *testing.T) {
+	srv := New(&sharedinternal.NopLogger{})
+	require.NotNil(t, srv)
+
+	// Nil if not started
+	assert.Nil(t, srv.Addr())
+
+	err := srv.Start(StartSettings{
+		ListenEndpoint: "127.0.0.1:0",
+		ListenPath:     "/",
+	})
+	assert.NoError(t, err)
+
+	// should be listening on an non-zero ephemeral port
+	assert.NotEqual(t, "127.0.0.1:0", srv.Addr().String())
+	assert.Regexp(t, `^127.0.0.1:\d+`, srv.Addr().String())
+
+	err = srv.Stop(context.Background())
+	assert.NoError(t, err)
+}
+
 func TestServerStartRejectConnection(t *testing.T) {
 	callbacks := CallbacksStruct{
 		OnConnectingFunc: func(request *http.Request) types.ConnectionResponse {