blob: a119666f745db2509b6df4059415707101ea26c0 [file] [log] [blame]
// 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
}