github.com/solo-io/unik@v0.0.0-20190717152701-a58d3e8e33b7/instance-listener/main.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "errors" 6 "flag" 7 "fmt" 8 "io/ioutil" 9 "log" 10 "net" 11 "net/http" 12 "os" 13 "strings" 14 "sync" 15 "time" 16 ) 17 18 const statefile = "/data/statefile.json" 19 20 type state struct { 21 MacIpMap map[string]string `json:"Ips"` 22 MacEnvMap map[string]map[string]string `json:"Envs"` 23 } 24 25 func main() { 26 args := os.Args 27 for i, arg := range args { 28 log.Printf("arg %v: %s", i, arg) 29 } 30 dataPrefix := flag.String("prefix", "unik_", "prefix for data sent via udp (for identification purposes") 31 enablePersistence := flag.Bool("enablePersistence", true, "assume a persistent volume is mounted to /data") 32 flag.Parse() 33 for i, arg := range flag.Args() { 34 35 log.Printf("flagarg %v: %s", i, arg) 36 } 37 if *dataPrefix == "unik_" { 38 log.Printf("ERROR: must provide -prefix") 39 return 40 } 41 if *dataPrefix == "" { 42 log.Printf("ERROR: -prefix cannot be \"\"") 43 return 44 } 45 if !*enablePersistence { 46 os.MkdirAll("/data", 0755) 47 } 48 ipMapLock := sync.RWMutex{} 49 envMapLock := sync.RWMutex{} 50 saveLock := sync.Mutex{} 51 var s state 52 s.MacIpMap = make(map[string]string) 53 s.MacEnvMap = make(map[string]map[string]string) 54 55 data, err := ioutil.ReadFile(statefile) 56 if err != nil { 57 log.Printf("could not read statefile, maybe this is first boot: " + err.Error()) 58 } else { 59 if err := json.Unmarshal(data, &s); err != nil { 60 log.Printf("failed to parse state json: " + err.Error()) 61 } 62 } 63 64 listenerIp, listenerIpMask, err := getLocalIp() 65 if err != nil { 66 log.Printf("ERROR: failed to get local ip: %v", err) 67 return 68 } 69 70 log.Printf("Starting unik discovery (udp heartbeat broadcast) with ip %s", listenerIp.String()) 71 info := []byte(*dataPrefix + ":" + listenerIp.String()) 72 BROADCAST_IPv4 := reverseMask(listenerIp, listenerIpMask) 73 if listenerIpMask == nil { 74 log.Printf("ERROR: listener-ip: %v; listener-ip-mask: %v; could not calculate broadcast address", listenerIp, listenerIpMask) 75 return 76 } 77 socket, err := net.DialUDP("udp4", nil, &net.UDPAddr{ 78 IP: BROADCAST_IPv4, 79 Port: 9967, 80 }) 81 if err != nil { 82 log.Printf(fmt.Sprintf("ERROR: broadcast-ip: %v; failed to dial udp broadcast connection", BROADCAST_IPv4)) 83 return 84 } 85 go func() { 86 log.Printf("broadcasting...") 87 for { 88 _, err = socket.Write(info) 89 if err != nil { 90 log.Printf("ERROR: broadcast-ip: %v; failed writing to broadcast udp socket: "+err.Error(), BROADCAST_IPv4) 91 return 92 } 93 time.Sleep(5000 * time.Millisecond) 94 } 95 }() 96 m := http.NewServeMux() 97 m.HandleFunc("/register", func(res http.ResponseWriter, req *http.Request) { 98 if req.Method != "POST" { 99 res.WriteHeader(http.StatusNotFound) 100 return 101 } 102 splitAddr := strings.Split(req.RemoteAddr, ":") 103 if len(splitAddr) < 1 { 104 log.Printf("req.RemoteAddr: %v, could not parse remote addr into ip/port combination", req.RemoteAddr) 105 return 106 } 107 instanceIp := splitAddr[0] 108 macAddress := req.URL.Query().Get("mac_address") 109 log.Printf("Instance registered") 110 log.Printf("ip: %v", instanceIp) 111 log.Printf("ip: %v", macAddress) 112 //mac address = the instance id in vsphere/vbox 113 go func() { 114 ipMapLock.Lock() 115 defer ipMapLock.Unlock() 116 s.MacIpMap[macAddress] = instanceIp 117 go save(s, saveLock) 118 }() 119 envMapLock.RLock() 120 defer envMapLock.RUnlock() 121 env, ok := s.MacEnvMap[macAddress] 122 if !ok { 123 env = make(map[string]string) 124 log.Printf("mac: %v", macAddress) 125 log.Printf("env: %v", s.MacEnvMap) 126 log.Printf("no env set for instance, replying with empty map") 127 } 128 data, err := json.Marshal(env) 129 if err != nil { 130 log.Printf("could not marshal env to json: " + err.Error()) 131 return 132 } 133 log.Printf("responding with data: %s", data) 134 fmt.Fprintf(res, "%s", data) 135 }) 136 m.HandleFunc("/set_instance_env", func(res http.ResponseWriter, req *http.Request) { 137 if req.Method != "POST" { 138 res.WriteHeader(http.StatusNotFound) 139 return 140 } 141 macAddress := req.URL.Query().Get("mac_address") 142 data, err := ioutil.ReadAll(req.Body) 143 if err != nil { 144 res.WriteHeader(http.StatusInternalServerError) 145 res.Write([]byte(err.Error())) 146 return 147 } 148 defer req.Body.Close() 149 var env map[string]string 150 if err := json.Unmarshal(data, &env); err != nil { 151 res.WriteHeader(http.StatusInternalServerError) 152 res.Write([]byte(err.Error())) 153 return 154 } 155 log.Printf("Env set for instance") 156 log.Printf("mac: %v", macAddress) 157 log.Printf("env: %v", env) 158 envMapLock.Lock() 159 defer envMapLock.Unlock() 160 s.MacEnvMap[macAddress] = env 161 go save(s, saveLock) 162 res.WriteHeader(http.StatusAccepted) 163 }) 164 m.HandleFunc("/instances", func(res http.ResponseWriter, req *http.Request) { 165 if req.Method != "GET" { 166 res.WriteHeader(http.StatusNotFound) 167 return 168 } 169 ipMapLock.RLock() 170 defer ipMapLock.RUnlock() 171 data, err := json.Marshal(s.MacIpMap) 172 if err != nil { 173 res.WriteHeader(http.StatusInternalServerError) 174 res.Write([]byte(err.Error())) 175 } 176 res.Write(data) 177 }) 178 log.Printf("listening on port 3000") 179 http.ListenAndServe(":3000", m) 180 } 181 182 func getLocalIp() (net.IP, net.IPMask, error) { 183 ifaces, err := net.Interfaces() 184 if err != nil { 185 return net.IP{}, net.IPMask{}, errors.New("retrieving network interfaces" + err.Error()) 186 } 187 for _, iface := range ifaces { 188 log.Printf("found an interface: %v\n", iface) 189 addrs, err := iface.Addrs() 190 if err != nil { 191 continue 192 } 193 for _, addr := range addrs { 194 log.Printf("inspecting address: %v", addr) 195 switch v := addr.(type) { 196 case *net.IPNet: 197 if !v.IP.IsLoopback() && v.IP.IsGlobalUnicast() && v.IP.To4() != nil && v.Mask != nil { 198 return v.IP.To4(),v.Mask, nil 199 } 200 } 201 } 202 } 203 return net.IP{}, net.IPMask{}, errors.New("failed to find ip on ifaces: " + fmt.Sprintf("%v", ifaces)) 204 } 205 206 // ReverseMask returns the result of masking the IP address ip with mask. 207 func reverseMask(ip net.IP, mask net.IPMask) net.IP { 208 n := len(ip) 209 if n != len(mask) { 210 return nil 211 } 212 out := make(net.IP, n) 213 for i := 0; i < n; i++ { 214 out[i] = ip[i] | (^mask[i]) 215 } 216 return out 217 } 218 219 func save(s state, l sync.Mutex) { 220 if err := func() error { 221 l.Lock() 222 defer l.Unlock() 223 data, err := json.Marshal(s) 224 if err != nil { 225 return err 226 } 227 if err := ioutil.WriteFile(statefile, data, 0644); err != nil { 228 return err 229 } 230 return nil 231 }(); err != nil { 232 log.Printf("failed to save state file %s", statefile) 233 } 234 }