m/pkg/event: move MemoryValue to subpackage

This keeps metropolis/pkg/event as a pure interface package, and
moves the memory-backed implementation to a subpackage.

Test Plan: Refactor, coevered by tests.

X-Origin-Diff: phab/D764
GitOrigin-RevId: 1337bf55a7752293791b3efe8648bbf5f6e6e9e1
diff --git a/metropolis/pkg/event/memory/example_test.go b/metropolis/pkg/event/memory/example_test.go
new file mode 100644
index 0000000..a119666
--- /dev/null
+++ b/metropolis/pkg/event/memory/example_test.go
@@ -0,0 +1,140 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 memory
+
+import (
+	"context"
+	"fmt"
+	"net"
+	"time"
+
+	"source.monogon.dev/metropolis/pkg/event"
+)
+
+// NetworkStatus is example data that will be stored in a Value.
+type NetworkStatus struct {
+	ExternalAddress net.IP
+	DefaultGateway  net.IP
+}
+
+// NetworkStatusWatcher is a typesafe wrapper around a Watcher.
+type NetworkStatusWatcher struct {
+	watcher event.Watcher
+}
+
+// Get wraps Watcher.Get and performs type assertion.
+func (s *NetworkStatusWatcher) Get(ctx context.Context) (*NetworkStatus, error) {
+	val, err := s.watcher.Get(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ns := val.(NetworkStatus)
+	return &ns, nil
+}
+
+// NetworkService is a fake/example network service that is responsible for
+// communicating the newest information about a machine's network configuration
+// to consumers/watchers.
+type NetworkService struct {
+	Provider Value
+}
+
+// Watch is a thin wrapper around Value.Watch that returns the typesafe
+// NetworkStatusWatcher wrapper.
+func (s *NetworkService) Watch() NetworkStatusWatcher {
+	watcher := s.Provider.Watch()
+	return NetworkStatusWatcher{
+		watcher: watcher,
+	}
+}
+
+// Run pretends to execute the network service's main logic loop, in which it
+// pretends to have received an IP address over DHCP, and communicates that to
+// consumers/watchers.
+func (s *NetworkService) Run(ctx context.Context) {
+	s.Provider.Set(NetworkStatus{
+		ExternalAddress: nil,
+		DefaultGateway:  nil,
+	})
+
+	select {
+	case <-time.After(100 * time.Millisecond):
+	case <-ctx.Done():
+		return
+	}
+
+	fmt.Printf("NS: Got DHCP Lease\n")
+	s.Provider.Set(NetworkStatus{
+		ExternalAddress: net.ParseIP("203.0.113.24"),
+		DefaultGateway:  net.ParseIP("203.0.113.1"),
+	})
+
+	select {
+	case <-time.After(100 * time.Millisecond):
+	case <-ctx.Done():
+		return
+	}
+
+	fmt.Printf("NS: DHCP Address changed\n")
+	s.Provider.Set(NetworkStatus{
+		ExternalAddress: net.ParseIP("203.0.113.103"),
+		DefaultGateway:  net.ParseIP("203.0.113.1"),
+	})
+
+	time.Sleep(100 * time.Millisecond)
+}
+
+// ExampleValue_full demonstrates a typical usecase for Event Values, in which
+// a mock network service lets watchers know that the machine on which the code
+// is running has received a new network configuration.
+// It also shows the typical boilerplate required in order to wrap a Value (eg.
+// MemoryValue) within a typesafe wrapper.
+func ExampleValue_full() {
+	ctx, ctxC := context.WithCancel(context.Background())
+	defer ctxC()
+
+	// Create a fake NetworkService.
+	ns := NetworkService{}
+
+	// Run an /etc/hosts updater. It will watch for updates from the NetworkService
+	// about the current IP address of the node.
+	go func() {
+		w := ns.Watch()
+		for {
+			status, err := w.Get(ctx)
+			if err != nil {
+				break
+			}
+			if status.ExternalAddress == nil {
+				continue
+			}
+			// Pretend to write /etc/hosts with the newest ExternalAddress.
+			// In production code, you would also check for whether ExternalAddress has
+			// changed from the last written value, if writing to /etc/hosts is expensive.
+			fmt.Printf("/etc/hosts: foo.example.com is now %s\n", status.ExternalAddress.String())
+		}
+	}()
+
+	// Run fake network service.
+	ns.Run(ctx)
+
+	// Output:
+	// NS: Got DHCP Lease
+	// /etc/hosts: foo.example.com is now 203.0.113.24
+	// NS: DHCP Address changed
+	// /etc/hosts: foo.example.com is now 203.0.113.103
+}