blob: a119666f745db2509b6df4059415707101ea26c0 [file] [log] [blame]
Serge Bazanskic00318e2021-03-03 12:39:24 +01001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
Serge Bazanski68ca5ee2021-04-27 16:09:16 +020017package memory
Serge Bazanskic00318e2021-03-03 12:39:24 +010018
19import (
20 "context"
21 "fmt"
22 "net"
23 "time"
Serge Bazanski68ca5ee2021-04-27 16:09:16 +020024
25 "source.monogon.dev/metropolis/pkg/event"
Serge Bazanskic00318e2021-03-03 12:39:24 +010026)
27
28// NetworkStatus is example data that will be stored in a Value.
29type NetworkStatus struct {
30 ExternalAddress net.IP
Serge Bazanski68ca5ee2021-04-27 16:09:16 +020031 DefaultGateway net.IP
Serge Bazanskic00318e2021-03-03 12:39:24 +010032}
33
34// NetworkStatusWatcher is a typesafe wrapper around a Watcher.
35type NetworkStatusWatcher struct {
Serge Bazanski68ca5ee2021-04-27 16:09:16 +020036 watcher event.Watcher
Serge Bazanskic00318e2021-03-03 12:39:24 +010037}
38
39// Get wraps Watcher.Get and performs type assertion.
40func (s *NetworkStatusWatcher) Get(ctx context.Context) (*NetworkStatus, error) {
41 val, err := s.watcher.Get(ctx)
42 if err != nil {
43 return nil, err
44 }
45 ns := val.(NetworkStatus)
46 return &ns, nil
47}
48
49// NetworkService is a fake/example network service that is responsible for
50// communicating the newest information about a machine's network configuration
51// to consumers/watchers.
52type NetworkService struct {
Serge Bazanski68ca5ee2021-04-27 16:09:16 +020053 Provider Value
Serge Bazanskic00318e2021-03-03 12:39:24 +010054}
55
56// Watch is a thin wrapper around Value.Watch that returns the typesafe
57// NetworkStatusWatcher wrapper.
58func (s *NetworkService) Watch() NetworkStatusWatcher {
59 watcher := s.Provider.Watch()
60 return NetworkStatusWatcher{
61 watcher: watcher,
62 }
63}
64
65// Run pretends to execute the network service's main logic loop, in which it
66// pretends to have received an IP address over DHCP, and communicates that to
67// consumers/watchers.
68func (s *NetworkService) Run(ctx context.Context) {
69 s.Provider.Set(NetworkStatus{
70 ExternalAddress: nil,
Serge Bazanski68ca5ee2021-04-27 16:09:16 +020071 DefaultGateway: nil,
Serge Bazanskic00318e2021-03-03 12:39:24 +010072 })
73
74 select {
Serge Bazanski68ca5ee2021-04-27 16:09:16 +020075 case <-time.After(100 * time.Millisecond):
Serge Bazanskic00318e2021-03-03 12:39:24 +010076 case <-ctx.Done():
Serge Bazanski68ca5ee2021-04-27 16:09:16 +020077 return
Serge Bazanskic00318e2021-03-03 12:39:24 +010078 }
79
80 fmt.Printf("NS: Got DHCP Lease\n")
81 s.Provider.Set(NetworkStatus{
82 ExternalAddress: net.ParseIP("203.0.113.24"),
Serge Bazanski68ca5ee2021-04-27 16:09:16 +020083 DefaultGateway: net.ParseIP("203.0.113.1"),
Serge Bazanskic00318e2021-03-03 12:39:24 +010084 })
85
86 select {
Serge Bazanski68ca5ee2021-04-27 16:09:16 +020087 case <-time.After(100 * time.Millisecond):
Serge Bazanskic00318e2021-03-03 12:39:24 +010088 case <-ctx.Done():
89 return
90 }
91
92 fmt.Printf("NS: DHCP Address changed\n")
93 s.Provider.Set(NetworkStatus{
94 ExternalAddress: net.ParseIP("203.0.113.103"),
Serge Bazanski68ca5ee2021-04-27 16:09:16 +020095 DefaultGateway: net.ParseIP("203.0.113.1"),
Serge Bazanskic00318e2021-03-03 12:39:24 +010096 })
97
98 time.Sleep(100 * time.Millisecond)
99}
100
101// ExampleValue_full demonstrates a typical usecase for Event Values, in which
102// a mock network service lets watchers know that the machine on which the code
103// is running has received a new network configuration.
104// It also shows the typical boilerplate required in order to wrap a Value (eg.
105// MemoryValue) within a typesafe wrapper.
106func ExampleValue_full() {
107 ctx, ctxC := context.WithCancel(context.Background())
108 defer ctxC()
109
110 // Create a fake NetworkService.
111 ns := NetworkService{}
112
113 // Run an /etc/hosts updater. It will watch for updates from the NetworkService
114 // about the current IP address of the node.
115 go func() {
116 w := ns.Watch()
117 for {
118 status, err := w.Get(ctx)
119 if err != nil {
120 break
121 }
122 if status.ExternalAddress == nil {
123 continue
124 }
125 // Pretend to write /etc/hosts with the newest ExternalAddress.
126 // In production code, you would also check for whether ExternalAddress has
127 // changed from the last written value, if writing to /etc/hosts is expensive.
128 fmt.Printf("/etc/hosts: foo.example.com is now %s\n", status.ExternalAddress.String())
129 }
130 }()
131
132 // Run fake network service.
133 ns.Run(ctx)
134
135 // Output:
136 // NS: Got DHCP Lease
137 // /etc/hosts: foo.example.com is now 203.0.113.24
138 // NS: DHCP Address changed
139 // /etc/hosts: foo.example.com is now 203.0.113.103
140}