@@ -114,7 +114,7 @@ type DNSHostsSetter interface {
114
114
115
115
// UpdatesHandler is anything that expects a stream of workspace update diffs.
116
116
type UpdatesHandler interface {
117
- Update (* proto. WorkspaceUpdate )error
117
+ Update (WorkspaceUpdate )error
118
118
}
119
119
120
120
// ControlProtocolClients represents an abstract interface to the tailnet control plane via a set
@@ -871,43 +871,51 @@ type TunnelAllWorkspaceUpdatesController struct {
871
871
updater * tunnelUpdater
872
872
}
873
873
874
- type workspace struct {
875
- id uuid.UUID
876
- name string
877
- agents map [uuid.UUID ]agent
874
+ type Workspace struct {
875
+ ID uuid.UUID
876
+ Name string
877
+ Status proto.Workspace_Status
878
+
879
+ ownerUsername string
880
+ agents map [uuid.UUID ]* Agent
878
881
}
879
882
880
- // addAllDNSNames adds names for all of its agents to the given map of names
881
- func (w workspace )addAllDNSNames (names map [dnsname.FQDN ][]netip.Addr ,owner string )error {
882
- for _ ,a := range w .agents {
883
+ // updateDNSNames updates the DNS names for all agents in the workspace.
884
+ func (w * Workspace )updateDNSNames ()error {
885
+ for id ,a := range w .agents {
886
+ names := make (map [dnsname.FQDN ][]netip.Addr )
883
887
// TODO: technically, DNS labels cannot start with numbers, but the rules are often not
884
888
// strictly enforced.
885
- fqdn ,err := dnsname .ToFQDN (fmt .Sprintf ("%s.%s.me.coder." ,a .name ,w .name ))
886
- if err != nil {
887
- return err
888
- }
889
- names [fqdn ]= []netip.Addr {CoderServicePrefix .AddrFromUUID (a .id )}
890
- fqdn ,err = dnsname .ToFQDN (fmt .Sprintf ("%s.%s.%s.coder." ,a .name ,w .name ,owner ))
889
+ fqdn ,err := dnsname .ToFQDN (fmt .Sprintf ("%s.%s.me.coder." ,a .Name ,w .Name ))
891
890
if err != nil {
892
891
return err
893
892
}
894
- names [fqdn ]= []netip.Addr {CoderServicePrefix .AddrFromUUID (a .id )}
895
- }
896
- if len (w .agents )== 1 {
897
- fqdn ,err := dnsname .ToFQDN (fmt .Sprintf ("%s.coder." ,w .name ))
893
+ names [fqdn ]= []netip.Addr {CoderServicePrefix .AddrFromUUID (a .ID )}
894
+ fqdn ,err = dnsname .ToFQDN (fmt .Sprintf ("%s.%s.%s.coder." ,a .Name ,w .Name ,w .ownerUsername ))
898
895
if err != nil {
899
896
return err
900
897
}
901
- for _ ,a := range w .agents {
902
- names [fqdn ]= []netip.Addr {CoderServicePrefix .AddrFromUUID (a .id )}
898
+ names [fqdn ]= []netip.Addr {CoderServicePrefix .AddrFromUUID (a .ID )}
899
+ if len (w .agents )== 1 {
900
+ fqdn ,err := dnsname .ToFQDN (fmt .Sprintf ("%s.coder." ,w .Name ))
901
+ if err != nil {
902
+ return err
903
+ }
904
+ for _ ,a := range w .agents {
905
+ names [fqdn ]= []netip.Addr {CoderServicePrefix .AddrFromUUID (a .ID )}
906
+ }
903
907
}
908
+ a .Hosts = names
909
+ w .agents [id ]= a
904
910
}
905
911
return nil
906
912
}
907
913
908
- type agent struct {
909
- id uuid.UUID
910
- name string
914
+ type Agent struct {
915
+ ID uuid.UUID
916
+ Name string
917
+ WorkspaceID uuid.UUID
918
+ Hosts map [dnsname.FQDN ][]netip.Addr
911
919
}
912
920
913
921
func (t * TunnelAllWorkspaceUpdatesController )New (client WorkspaceUpdatesClient )CloserWaiter {
@@ -922,40 +930,43 @@ func (t *TunnelAllWorkspaceUpdatesController) New(client WorkspaceUpdatesClient)
922
930
updateHandler :t .updateHandler ,
923
931
ownerUsername :t .ownerUsername ,
924
932
recvLoopDone :make (chan struct {}),
925
- workspaces :make (map [uuid.UUID ]* workspace ),
933
+ workspaces :make (map [uuid.UUID ]* Workspace ),
926
934
}
927
935
t .updater = updater
928
936
go t .updater .recvLoop ()
929
937
return t .updater
930
938
}
931
939
932
- func (t * TunnelAllWorkspaceUpdatesController )CurrentState ()* proto. WorkspaceUpdate {
940
+ func (t * TunnelAllWorkspaceUpdatesController )CurrentState ()( WorkspaceUpdate , error ) {
933
941
t .mu .Lock ()
934
942
defer t .mu .Unlock ()
935
943
if t .updater == nil {
936
- return nil
944
+ return WorkspaceUpdate {}, xerrors . New ( "no updater" )
937
945
}
938
946
t .updater .Lock ()
939
947
defer t .updater .Unlock ()
940
- out := & proto.WorkspaceUpdate {
941
- UpsertedWorkspaces :make ([]* proto.Workspace ,0 ,len (t .updater .workspaces )),
942
- UpsertedAgents :make ([]* proto.Agent ,0 ,len (t .updater .workspaces )),
948
+ out := WorkspaceUpdate {
949
+ UpsertedWorkspaces :make ([]* Workspace ,0 ,len (t .updater .workspaces )),
950
+ UpsertedAgents :make ([]* Agent ,0 ,len (t .updater .workspaces )),
951
+ DeletedWorkspaces :make ([]* Workspace ,0 ),
952
+ DeletedAgents :make ([]* Agent ,0 ),
943
953
}
944
954
for _ ,w := range t .updater .workspaces {
945
- upw := & proto. Workspace {
946
- Id :UUIDToByteSlice ( w . id ) ,
947
- Name :w . name ,
948
- }
949
- out . UpsertedWorkspaces = append ( out . UpsertedWorkspaces , upw )
955
+ out . UpsertedWorkspaces = append ( out . UpsertedWorkspaces , & Workspace {
956
+ ID :w . ID ,
957
+ Name :w . Name ,
958
+ Status : w . Status ,
959
+ } )
950
960
for _ ,a := range w .agents {
951
- out .UpsertedAgents = append (out .UpsertedAgents ,& proto.Agent {
952
- Id :UUIDToByteSlice (a .id ),
953
- Name :a .name ,
954
- WorkspaceId :UUIDToByteSlice (w .id ),
961
+ out .UpsertedAgents = append (out .UpsertedAgents ,& Agent {
962
+ ID :a .ID ,
963
+ Name :a .Name ,
964
+ WorkspaceID :a .WorkspaceID ,
965
+ Hosts :maps .Clone (a .Hosts ),
955
966
})
956
967
}
957
968
}
958
- return out
969
+ return out , nil
959
970
}
960
971
961
972
type tunnelUpdater struct {
@@ -969,7 +980,7 @@ type tunnelUpdater struct {
969
980
recvLoopDone chan struct {}
970
981
971
982
sync.Mutex
972
- workspaces map [uuid.UUID ]* workspace
983
+ workspaces map [uuid.UUID ]* Workspace
973
984
closed bool
974
985
}
975
986
@@ -1031,20 +1042,78 @@ func (t *tunnelUpdater) recvLoop() {
1031
1042
}
1032
1043
}
1033
1044
1045
+ type WorkspaceUpdate struct {
1046
+ UpsertedWorkspaces []* Workspace
1047
+ UpsertedAgents []* Agent
1048
+ DeletedWorkspaces []* Workspace
1049
+ DeletedAgents []* Agent
1050
+ }
1051
+
1052
+ func (w * WorkspaceUpdate )Clone ()WorkspaceUpdate {
1053
+ clone := WorkspaceUpdate {
1054
+ UpsertedWorkspaces :make ([]* Workspace ,len (w .UpsertedWorkspaces )),
1055
+ UpsertedAgents :make ([]* Agent ,len (w .UpsertedAgents )),
1056
+ DeletedWorkspaces :make ([]* Workspace ,len (w .DeletedWorkspaces )),
1057
+ DeletedAgents :make ([]* Agent ,len (w .DeletedAgents )),
1058
+ }
1059
+ for i ,ws := range w .UpsertedWorkspaces {
1060
+ clone .UpsertedWorkspaces [i ]= & Workspace {
1061
+ ID :ws .ID ,
1062
+ Name :ws .Name ,
1063
+ Status :ws .Status ,
1064
+ }
1065
+ }
1066
+ for i ,a := range w .UpsertedAgents {
1067
+ clone .UpsertedAgents [i ]= & Agent {
1068
+ ID :a .ID ,
1069
+ Name :a .Name ,
1070
+ WorkspaceID :a .WorkspaceID ,
1071
+ Hosts :maps .Clone (a .Hosts ),
1072
+ }
1073
+ }
1074
+ for i ,ws := range w .DeletedWorkspaces {
1075
+ clone .DeletedWorkspaces [i ]= & Workspace {
1076
+ ID :ws .ID ,
1077
+ Name :ws .Name ,
1078
+ Status :ws .Status ,
1079
+ }
1080
+ }
1081
+ for i ,a := range w .DeletedAgents {
1082
+ clone .DeletedAgents [i ]= & Agent {
1083
+ ID :a .ID ,
1084
+ Name :a .Name ,
1085
+ WorkspaceID :a .WorkspaceID ,
1086
+ Hosts :maps .Clone (a .Hosts ),
1087
+ }
1088
+ }
1089
+ return clone
1090
+ }
1091
+
1034
1092
func (t * tunnelUpdater )handleUpdate (update * proto.WorkspaceUpdate )error {
1035
1093
t .Lock ()
1036
1094
defer t .Unlock ()
1095
+
1096
+ currentUpdate := WorkspaceUpdate {
1097
+ UpsertedWorkspaces : []* Workspace {},
1098
+ UpsertedAgents : []* Agent {},
1099
+ DeletedWorkspaces : []* Workspace {},
1100
+ DeletedAgents : []* Agent {},
1101
+ }
1102
+
1037
1103
for _ ,uw := range update .UpsertedWorkspaces {
1038
1104
workspaceID ,err := uuid .FromBytes (uw .Id )
1039
1105
if err != nil {
1040
1106
return xerrors .Errorf ("failed to parse workspace ID: %w" ,err )
1041
1107
}
1042
- w := workspace {
1043
- id :workspaceID ,
1044
- name :uw .Name ,
1045
- agents :make (map [uuid.UUID ]agent ),
1108
+ w := & Workspace {
1109
+ ID :workspaceID ,
1110
+ Name :uw .Name ,
1111
+ Status :uw .Status ,
1112
+ ownerUsername :t .ownerUsername ,
1113
+ agents :make (map [uuid.UUID ]* Agent ),
1046
1114
}
1047
1115
t .upsertWorkspace (w )
1116
+ currentUpdate .UpsertedWorkspaces = append (currentUpdate .UpsertedWorkspaces ,w )
1048
1117
}
1049
1118
1050
1119
// delete agents before deleting workspaces, since the agents have workspace ID references
@@ -1057,17 +1126,22 @@ func (t *tunnelUpdater) handleUpdate(update *proto.WorkspaceUpdate) error {
1057
1126
if err != nil {
1058
1127
return xerrors .Errorf ("failed to parse workspace ID: %w" ,err )
1059
1128
}
1060
- err = t .deleteAgent (workspaceID ,agentID )
1129
+ deletedAgent , err : =t .deleteAgent (workspaceID ,agentID )
1061
1130
if err != nil {
1062
1131
return xerrors .Errorf ("failed to delete agent: %w" ,err )
1063
1132
}
1133
+ currentUpdate .DeletedAgents = append (currentUpdate .DeletedAgents ,deletedAgent )
1064
1134
}
1065
1135
for _ ,dw := range update .DeletedWorkspaces {
1066
1136
workspaceID ,err := uuid .FromBytes (dw .Id )
1067
1137
if err != nil {
1068
1138
return xerrors .Errorf ("failed to parse workspace ID: %w" ,err )
1069
1139
}
1070
- t .deleteWorkspace (workspaceID )
1140
+ deletedWorkspace ,err := t .deleteWorkspace (workspaceID )
1141
+ if err != nil {
1142
+ return xerrors .Errorf ("failed to delete workspace: %w" ,err )
1143
+ }
1144
+ currentUpdate .DeletedWorkspaces = append (currentUpdate .DeletedWorkspaces ,deletedWorkspace )
1071
1145
}
1072
1146
1073
1147
// upsert agents last, after all workspaces have been added and deleted, since agents reference
@@ -1081,17 +1155,18 @@ func (t *tunnelUpdater) handleUpdate(update *proto.WorkspaceUpdate) error {
1081
1155
if err != nil {
1082
1156
return xerrors .Errorf ("failed to parse workspace ID: %w" ,err )
1083
1157
}
1084
- a := agent { name :ua .Name ,id :agentID }
1158
+ a := & Agent { Name :ua .Name ,ID :agentID , WorkspaceID : workspaceID }
1085
1159
err = t .upsertAgent (workspaceID ,a )
1086
1160
if err != nil {
1087
1161
return xerrors .Errorf ("failed to upsert agent: %w" ,err )
1088
1162
}
1163
+ currentUpdate .UpsertedAgents = append (currentUpdate .UpsertedAgents ,a )
1089
1164
}
1090
1165
allAgents := t .allAgentIDs ()
1091
1166
t .coordCtrl .SyncDestinations (allAgents )
1167
+ dnsNames := t .updateDNSNames ()
1092
1168
if t .dnsHostsSetter != nil {
1093
1169
t .logger .Debug (context .Background (),"updating dns hosts" )
1094
- dnsNames := t .allDNSNames ()
1095
1170
err := t .dnsHostsSetter .SetDNSHosts (dnsNames )
1096
1171
if err != nil {
1097
1172
return xerrors .Errorf ("failed to set DNS hosts: %w" ,err )
@@ -1101,43 +1176,55 @@ func (t *tunnelUpdater) handleUpdate(update *proto.WorkspaceUpdate) error {
1101
1176
}
1102
1177
if t .updateHandler != nil {
1103
1178
t .logger .Debug (context .Background (),"calling update handler" )
1104
- err := t .updateHandler .Update (update )
1179
+ err := t .updateHandler .Update (currentUpdate . Clone () )
1105
1180
if err != nil {
1106
1181
t .logger .Error (context .Background (),"failed to call update handler" ,slog .Error (err ))
1107
1182
}
1108
1183
}
1109
1184
return nil
1110
1185
}
1111
1186
1112
- func (t * tunnelUpdater )upsertWorkspace (w workspace ) {
1113
- old ,ok := t .workspaces [w .id ]
1187
+ func (t * tunnelUpdater )upsertWorkspace (w * Workspace ) * Workspace {
1188
+ old ,ok := t .workspaces [w .ID ]
1114
1189
if ! ok {
1115
- t .workspaces [w .id ]= & w
1116
- return
1190
+ t .workspaces [w .ID ]= w
1191
+ return w
1117
1192
}
1118
- old .name = w .name
1193
+ old .Name = w .Name
1194
+ old .Status = w .Status
1195
+ old .ownerUsername = w .ownerUsername
1196
+ return w
1119
1197
}
1120
1198
1121
- func (t * tunnelUpdater )deleteWorkspace (id uuid.UUID ) {
1199
+ func (t * tunnelUpdater )deleteWorkspace (id uuid.UUID ) (* Workspace ,error ) {
1200
+ w ,ok := t .workspaces [id ]
1201
+ if ! ok {
1202
+ return nil ,xerrors .Errorf ("workspace %s not found" ,id )
1203
+ }
1122
1204
delete (t .workspaces ,id )
1205
+ return w ,nil
1123
1206
}
1124
1207
1125
- func (t * tunnelUpdater )upsertAgent (workspaceID uuid.UUID ,a agent )error {
1208
+ func (t * tunnelUpdater )upsertAgent (workspaceID uuid.UUID ,a * Agent )error {
1126
1209
w ,ok := t .workspaces [workspaceID ]
1127
1210
if ! ok {
1128
1211
return xerrors .Errorf ("workspace %s not found" ,workspaceID )
1129
1212
}
1130
- w .agents [a .id ]= a
1213
+ w .agents [a .ID ]= a
1131
1214
return nil
1132
1215
}
1133
1216
1134
- func (t * tunnelUpdater )deleteAgent (workspaceID ,id uuid.UUID )error {
1217
+ func (t * tunnelUpdater )deleteAgent (workspaceID ,id uuid.UUID )( * Agent , error ) {
1135
1218
w ,ok := t .workspaces [workspaceID ]
1136
1219
if ! ok {
1137
- return xerrors .Errorf ("workspace %s not found" ,workspaceID )
1220
+ return nil ,xerrors .Errorf ("workspace %s not found" ,workspaceID )
1221
+ }
1222
+ a ,ok := w .agents [id ]
1223
+ if ! ok {
1224
+ return nil ,xerrors .Errorf ("agent %s not found in workspace %s" ,id ,workspaceID )
1138
1225
}
1139
1226
delete (w .agents ,id )
1140
- return nil
1227
+ return a , nil
1141
1228
}
1142
1229
1143
1230
func (t * tunnelUpdater )allAgentIDs () []uuid.UUID {
@@ -1150,19 +1237,25 @@ func (t *tunnelUpdater) allAgentIDs() []uuid.UUID {
1150
1237
return out
1151
1238
}
1152
1239
1153
- func (t * tunnelUpdater )allDNSNames ()map [dnsname.FQDN ][]netip.Addr {
1240
+ // updateDNSNames updates the DNS names for all workspaces in the tunnelUpdater.
1241
+ func (t * tunnelUpdater )updateDNSNames ()map [dnsname.FQDN ][]netip.Addr {
1154
1242
names := make (map [dnsname.FQDN ][]netip.Addr )
1155
1243
for _ ,w := range t .workspaces {
1156
- err := w .addAllDNSNames ( names , t . ownerUsername )
1244
+ err := w .updateDNSNames ( )
1157
1245
if err != nil {
1158
1246
// This should never happen in production, because converting the FQDN only fails
1159
1247
// if names are too long, and we put strict length limits on agent, workspace, and user
1160
1248
// names.
1161
1249
t .logger .Critical (context .Background (),
1162
1250
"failed to include DNS name(s)" ,
1163
- slog .F ("workspace_id" ,w .id ),
1251
+ slog .F ("workspace_id" ,w .ID ),
1164
1252
slog .Error (err ))
1165
1253
}
1254
+ for _ ,a := range w .agents {
1255
+ for name ,addrs := range a .Hosts {
1256
+ names [name ]= addrs
1257
+ }
1258
+ }
1166
1259
}
1167
1260
return names
1168
1261
}