Repository: firecracker-microvm/firecracker-containerd
Pull request #605 (2022-04-11 23:45:26)

Update HTTP resolver to return metrics port
Signed-off-by: Gavin Inglis <giinglis@amazon.com>

*Issue #, if available:*

#602 task 1

*Description of changes:*

These changes update the Response of the HTTP resolver to include the
port for pulling metrics from a remote snapshotter. They also include initial update to configuration.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.



Patch:
diff --git a/snapshotter/app/service.go b/snapshotter/app/service.go
index 8dac1ed..e05bc86 100644
--- a/snapshotter/app/service.go
+++ b/snapshotter/app/service.go
@@ -17,6 +17,7 @@ import (
 	"context"
 	"fmt"
 	"net"
+	"net/url"
 	"os"
 	"os/signal"
 	"strconv"
@@ -134,17 +135,33 @@ func initSnapshotter(ctx context.Context, config config.Config) (snapshots.Snaps
 		if err != nil {
 			return nil, err
 		}
-		host, portstr, err := net.SplitHostPort(response.Address)
+		u, err := url.Parse(response.Address)
 		if err != nil {
 			return nil, err
 		}
-		port, err := strconv.ParseUint(portstr, base10, bits32)
+		host := u.Hostname()
+		port, err := strconv.ParseUint(response.SnapshotterPort, base10, bits32)
 		if err != nil {
 			return nil, err
 		}
 		snapshotterDialer := func(ctx context.Context, namespace string) (net.Conn, error) {
 			return vsock.DialContext(ctx, host, uint32(port), vsock.WithLogger(log.G(ctx)))
 		}
+
+		if config.Snapshotter.Metrics.Enable {
+			metricsPort, err := strconv.ParseUint(response.MetricsPort, base10, bits32)
+			if err != nil {
+				return nil, err
+			}
+
+			// TODO (ginglis13) metricsDialer func to be defined here using metricsPort. It will dial
+			// the same host but connect via its own port. The metrics proxy will be configured in NewProxySnapshotter
+			// task 2 of https://github.com/firecracker-microvm/firecracker-containerd/issues/602
+			_ = func(ctx context.Context, _ string) (net.Conn, error) {
+				return vsock.DialContext(ctx, host, uint32(metricsPort), vsock.WithLogger(log.G(ctx)))
+			}
+		}
+
 		return proxy.NewProxySnapshotter(ctx, host, snapshotterDialer)
 	}
 
diff --git a/snapshotter/config/config.go b/snapshotter/config/config.go
index 9cac4e7..fd047f4 100644
--- a/snapshotter/config/config.go
+++ b/snapshotter/config/config.go
@@ -33,6 +33,7 @@ type Config struct {
 type snapshotter struct {
 	Listener listener `toml:"listener"`
 	Proxy    proxy    `toml:"proxy"`
+	Metrics  metrics  `toml:"metrics"`
 }
 
 type listener struct {
@@ -57,6 +58,10 @@ type debug struct {
 	LogLevel string `toml:"logLevel" default:"info"`
 }
 
+type metrics struct {
+	Enable bool `toml:"enable" default:"false"`
+}
+
 // Load parses application configuration from a specified file path.
 func Load(filePath string) (Config, error) {
 	file, err := os.Open(filePath)
diff --git a/snapshotter/config/config.toml.example b/snapshotter/config/config.toml.example
index 792651f..5110600 100644
--- a/snapshotter/config/config.toml.example
+++ b/snapshotter/config/config.toml.example
@@ -6,5 +6,8 @@
   type = "http"
   address = "127.0.0.1:10001"
 
+[snapshotter.metrics]
+  enable = true
+
 [debug]
   logLevel = "info"
diff --git a/snapshotter/demux/proxy/address/resolver.go b/snapshotter/demux/proxy/address/resolver.go
index b9bc780..1bc3f9f 100644
--- a/snapshotter/demux/proxy/address/resolver.go
+++ b/snapshotter/demux/proxy/address/resolver.go
@@ -21,10 +21,13 @@ type Response struct {
 	Network string `json:"network"`
 
 	// Network address used in net.Dial.
-	// Assumes the host port forms documented in net.SplitHostPort.
-	//
-	// Reference: https://pkg.go.dev/net#SplitHostPort
 	Address string `json:"address"`
+
+	// SnapshotterPort is the port used in vsock.DialContext for sending snapshotter API requests to the remote snapshotter.
+	SnapshotterPort string `json:"snapshotter_port"`
+
+	// MetricsPort is the port used in vsock.DialContext for sending metrics requests to the remote snapshotter.
+	MetricsPort string `json:"metrics_port"`
 }
 
 // Resolver for the proxy network address.


Test patch:
diff --git a/snapshotter/config/config_test.go b/snapshotter/config/config_test.go
index bd54727..6ecbef3 100644
--- a/snapshotter/config/config_test.go
+++ b/snapshotter/config/config_test.go
@@ -62,6 +62,9 @@ func defaultConfig() error {
 				Network: "unix",
 				Address: "/var/lib/demux-snapshotter/snapshotter.sock",
 			},
+			Metrics: metrics{
+				Enable: false,
+			},
 		},
 		Debug: debug{
 			LogLevel: "info",
@@ -79,6 +82,8 @@ func parseExampleConfig() error {
 	  [snapshotter.proxy.address.resolver]
 	    type = "http"
 	    address = "localhost:10001"
+	  [snapshotter.metrics]
+        enable = true
 
 	[debug]
 	  logLevel = "debug"
@@ -97,6 +102,9 @@ func parseExampleConfig() error {
 					},
 				},
 			},
+			Metrics: metrics{
+				Enable: true,
+			},
 		},
 		Debug: debug{
 			LogLevel: "debug",
@@ -116,7 +124,7 @@ func parseConfig(input []byte, expected Config) error {
 		return errors.New("expected no error on parse")
 	}
 	if actual != expected {
-		return fmt.Errorf("expected %s actual %s", expected, actual)
+		return fmt.Errorf("expected %v actual %v", expected, actual)
 	}
 	return nil
 }
diff --git a/snapshotter/demux/proxy/address/http_resolver_test.go b/snapshotter/demux/proxy/address/http_resolver_test.go
index 4e7b64b..94b60c6 100644
--- a/snapshotter/demux/proxy/address/http_resolver_test.go
+++ b/snapshotter/demux/proxy/address/http_resolver_test.go
@@ -58,7 +58,7 @@ func returnErrorOnJSONParserError() error {
 }
 
 func happyPath() error {
-	reader := mockReader{response: []byte(`{"network": "unix", "address": "/path/to/snapshotter.vsock"}`), err: io.EOF}
+	reader := mockReader{response: []byte(`{"network": "unix", "address": "/path/to/snapshotter.vsock", "snapshotter_port": "10000", "metrics_port": "10001"}`), err: io.EOF}
 	client := mockClient{getError: nil, getResponse: http.Response{Body: &reader}}
 	uut := HTTPResolver{url: "localhost:10001", client: &client}
 
@@ -73,6 +73,12 @@ func happyPath() error {
 	if actual.Address != "/path/to/snapshotter.vsock" {
 		return fmt.Errorf("Expected address '/path/to/snapshotter.vsock' but actual %s", actual.Address)
 	}
+	if actual.SnapshotterPort != "10000" {
+		return fmt.Errorf("Expected metrics port '10000' but actual %s", actual.MetricsPort)
+	}
+	if actual.MetricsPort != "10001" {
+		return fmt.Errorf("Expected metrics port '10001' but actual %s", actual.MetricsPort)
+	}
 	return nil
 }