github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/dispatcher/assignments.go (about) 1 package dispatcher 2 3 import ( 4 "fmt" 5 6 "github.com/docker/swarmkit/api" 7 "github.com/docker/swarmkit/api/equality" 8 "github.com/docker/swarmkit/api/validation" 9 "github.com/docker/swarmkit/identity" 10 "github.com/docker/swarmkit/manager/drivers" 11 "github.com/docker/swarmkit/manager/state/store" 12 "github.com/sirupsen/logrus" 13 ) 14 15 type typeAndID struct { 16 id string 17 objType api.ResourceType 18 } 19 20 type assignmentSet struct { 21 dp *drivers.DriverProvider 22 tasksMap map[string]*api.Task 23 tasksUsingDependency map[typeAndID]map[string]struct{} 24 changes map[typeAndID]*api.AssignmentChange 25 log *logrus.Entry 26 } 27 28 func newAssignmentSet(log *logrus.Entry, dp *drivers.DriverProvider) *assignmentSet { 29 return &assignmentSet{ 30 dp: dp, 31 changes: make(map[typeAndID]*api.AssignmentChange), 32 tasksMap: make(map[string]*api.Task), 33 tasksUsingDependency: make(map[typeAndID]map[string]struct{}), 34 log: log, 35 } 36 } 37 38 func assignSecret(a *assignmentSet, readTx store.ReadTx, mapKey typeAndID, t *api.Task) { 39 if _, exists := a.tasksUsingDependency[mapKey]; !exists { 40 a.tasksUsingDependency[mapKey] = make(map[string]struct{}) 41 } 42 secret, doNotReuse, err := a.secret(readTx, t, mapKey.id) 43 if err != nil { 44 a.log.WithFields(logrus.Fields{ 45 "resource.type": "secret", 46 "secret.id": mapKey.id, 47 "error": err, 48 }).Debug("failed to fetch secret") 49 return 50 } 51 // If the secret should not be reused for other tasks, give it a unique ID for the task to allow different values for different tasks. 52 if doNotReuse { 53 // Give the secret a new ID and mark it as internal 54 originalSecretID := secret.ID 55 taskSpecificID := identity.CombineTwoIDs(originalSecretID, t.ID) 56 secret.ID = taskSpecificID 57 secret.Internal = true 58 // Create a new mapKey with the new ID and insert it into the dependencies map for the task. 59 // This will make the changes map contain an entry with the new ID rather than the original one. 60 mapKey = typeAndID{objType: mapKey.objType, id: secret.ID} 61 a.tasksUsingDependency[mapKey] = make(map[string]struct{}) 62 a.tasksUsingDependency[mapKey][t.ID] = struct{}{} 63 } 64 a.changes[mapKey] = &api.AssignmentChange{ 65 Assignment: &api.Assignment{ 66 Item: &api.Assignment_Secret{ 67 Secret: secret, 68 }, 69 }, 70 Action: api.AssignmentChange_AssignmentActionUpdate, 71 } 72 } 73 74 func assignConfig(a *assignmentSet, readTx store.ReadTx, mapKey typeAndID) { 75 a.tasksUsingDependency[mapKey] = make(map[string]struct{}) 76 config := store.GetConfig(readTx, mapKey.id) 77 if config == nil { 78 a.log.WithFields(logrus.Fields{ 79 "resource.type": "config", 80 "config.id": mapKey.id, 81 }).Debug("config not found") 82 return 83 } 84 a.changes[mapKey] = &api.AssignmentChange{ 85 Assignment: &api.Assignment{ 86 Item: &api.Assignment_Config{ 87 Config: config, 88 }, 89 }, 90 Action: api.AssignmentChange_AssignmentActionUpdate, 91 } 92 } 93 94 func (a *assignmentSet) addTaskDependencies(readTx store.ReadTx, t *api.Task) { 95 for _, resourceRef := range t.Spec.ResourceReferences { 96 mapKey := typeAndID{objType: resourceRef.ResourceType, id: resourceRef.ResourceID} 97 if len(a.tasksUsingDependency[mapKey]) == 0 { 98 switch resourceRef.ResourceType { 99 case api.ResourceType_SECRET: 100 assignSecret(a, readTx, mapKey, t) 101 case api.ResourceType_CONFIG: 102 assignConfig(a, readTx, mapKey) 103 default: 104 a.log.WithField( 105 "resource.type", resourceRef.ResourceType, 106 ).Debug("invalid resource type for a task dependency, skipping") 107 continue 108 } 109 } 110 a.tasksUsingDependency[mapKey][t.ID] = struct{}{} 111 } 112 113 var secrets []*api.SecretReference 114 container := t.Spec.GetContainer() 115 if container != nil { 116 secrets = container.Secrets 117 } 118 119 for _, secretRef := range secrets { 120 secretID := secretRef.SecretID 121 mapKey := typeAndID{objType: api.ResourceType_SECRET, id: secretID} 122 123 // This checks for the presence of each task in the dependency map for the 124 // secret. This is currently only done for secrets since the other types of 125 // dependencies do not support driver plugins. Arguably, the same task would 126 // not have the same secret as a dependency more than once, but this check 127 // makes sure the task only gets the secret assigned once. 128 if _, exists := a.tasksUsingDependency[mapKey][t.ID]; !exists { 129 assignSecret(a, readTx, mapKey, t) 130 } 131 a.tasksUsingDependency[mapKey][t.ID] = struct{}{} 132 } 133 134 var configs []*api.ConfigReference 135 if container != nil { 136 configs = container.Configs 137 } 138 for _, configRef := range configs { 139 configID := configRef.ConfigID 140 mapKey := typeAndID{objType: api.ResourceType_CONFIG, id: configID} 141 142 if len(a.tasksUsingDependency[mapKey]) == 0 { 143 assignConfig(a, readTx, mapKey) 144 } 145 a.tasksUsingDependency[mapKey][t.ID] = struct{}{} 146 } 147 } 148 149 func (a *assignmentSet) releaseDependency(mapKey typeAndID, assignment *api.Assignment, taskID string) bool { 150 delete(a.tasksUsingDependency[mapKey], taskID) 151 if len(a.tasksUsingDependency[mapKey]) != 0 { 152 return false 153 } 154 // No tasks are using the dependency anymore 155 delete(a.tasksUsingDependency, mapKey) 156 a.changes[mapKey] = &api.AssignmentChange{ 157 Assignment: assignment, 158 Action: api.AssignmentChange_AssignmentActionRemove, 159 } 160 return true 161 } 162 163 func (a *assignmentSet) releaseTaskDependencies(t *api.Task) bool { 164 var modified bool 165 166 for _, resourceRef := range t.Spec.ResourceReferences { 167 var assignment *api.Assignment 168 switch resourceRef.ResourceType { 169 case api.ResourceType_SECRET: 170 assignment = &api.Assignment{ 171 Item: &api.Assignment_Secret{ 172 Secret: &api.Secret{ID: resourceRef.ResourceID}, 173 }, 174 } 175 case api.ResourceType_CONFIG: 176 assignment = &api.Assignment{ 177 Item: &api.Assignment_Config{ 178 Config: &api.Config{ID: resourceRef.ResourceID}, 179 }, 180 } 181 default: 182 a.log.WithField( 183 "resource.type", resourceRef.ResourceType, 184 ).Debug("invalid resource type for a task dependency, skipping") 185 continue 186 } 187 188 mapKey := typeAndID{objType: resourceRef.ResourceType, id: resourceRef.ResourceID} 189 if a.releaseDependency(mapKey, assignment, t.ID) { 190 modified = true 191 } 192 } 193 194 container := t.Spec.GetContainer() 195 196 var secrets []*api.SecretReference 197 if container != nil { 198 secrets = container.Secrets 199 } 200 201 for _, secretRef := range secrets { 202 secretID := secretRef.SecretID 203 mapKey := typeAndID{objType: api.ResourceType_SECRET, id: secretID} 204 assignment := &api.Assignment{ 205 Item: &api.Assignment_Secret{ 206 Secret: &api.Secret{ID: secretID}, 207 }, 208 } 209 if a.releaseDependency(mapKey, assignment, t.ID) { 210 modified = true 211 } 212 } 213 214 var configs []*api.ConfigReference 215 if container != nil { 216 configs = container.Configs 217 } 218 219 for _, configRef := range configs { 220 configID := configRef.ConfigID 221 mapKey := typeAndID{objType: api.ResourceType_CONFIG, id: configID} 222 assignment := &api.Assignment{ 223 Item: &api.Assignment_Config{ 224 Config: &api.Config{ID: configID}, 225 }, 226 } 227 if a.releaseDependency(mapKey, assignment, t.ID) { 228 modified = true 229 } 230 } 231 232 return modified 233 } 234 235 func (a *assignmentSet) addOrUpdateTask(readTx store.ReadTx, t *api.Task) bool { 236 // We only care about tasks that are ASSIGNED or higher. 237 if t.Status.State < api.TaskStateAssigned { 238 return false 239 } 240 241 if oldTask, exists := a.tasksMap[t.ID]; exists { 242 // States ASSIGNED and below are set by the orchestrator/scheduler, 243 // not the agent, so tasks in these states need to be sent to the 244 // agent even if nothing else has changed. 245 if equality.TasksEqualStable(oldTask, t) && t.Status.State > api.TaskStateAssigned { 246 // this update should not trigger a task change for the agent 247 a.tasksMap[t.ID] = t 248 // If this task got updated to a final state, let's release 249 // the dependencies that are being used by the task 250 if t.Status.State > api.TaskStateRunning { 251 // If releasing the dependencies caused us to 252 // remove something from the assignment set, 253 // mark one modification. 254 return a.releaseTaskDependencies(t) 255 } 256 return false 257 } 258 } else if t.Status.State <= api.TaskStateRunning { 259 // If this task wasn't part of the assignment set before, and it's <= RUNNING 260 // add the dependencies it references to the assignment. 261 // Task states > RUNNING are worker reported only, are never created in 262 // a > RUNNING state. 263 a.addTaskDependencies(readTx, t) 264 } 265 a.tasksMap[t.ID] = t 266 a.changes[typeAndID{objType: api.ResourceType_TASK, id: t.ID}] = &api.AssignmentChange{ 267 Assignment: &api.Assignment{ 268 Item: &api.Assignment_Task{ 269 Task: t, 270 }, 271 }, 272 Action: api.AssignmentChange_AssignmentActionUpdate, 273 } 274 return true 275 } 276 277 func (a *assignmentSet) removeTask(t *api.Task) bool { 278 if _, exists := a.tasksMap[t.ID]; !exists { 279 return false 280 } 281 282 a.changes[typeAndID{objType: api.ResourceType_TASK, id: t.ID}] = &api.AssignmentChange{ 283 Assignment: &api.Assignment{ 284 Item: &api.Assignment_Task{ 285 Task: &api.Task{ID: t.ID}, 286 }, 287 }, 288 Action: api.AssignmentChange_AssignmentActionRemove, 289 } 290 291 delete(a.tasksMap, t.ID) 292 293 // Release the dependencies being used by this task. 294 // Ignoring the return here. We will always mark this as a 295 // modification, since a task is being removed. 296 a.releaseTaskDependencies(t) 297 return true 298 } 299 300 func (a *assignmentSet) message() api.AssignmentsMessage { 301 var message api.AssignmentsMessage 302 for _, change := range a.changes { 303 message.Changes = append(message.Changes, change) 304 } 305 306 // The the set of changes is reinitialized to prepare for formation 307 // of the next message. 308 a.changes = make(map[typeAndID]*api.AssignmentChange) 309 310 return message 311 } 312 313 // secret populates the secret value from raft store. For external secrets, the value is populated 314 // from the secret driver. The function returns: a secret object; an indication of whether the value 315 // is to be reused across tasks; and an error if the secret is not found in the store, if the secret 316 // driver responds with one or if the payload does not pass validation. 317 func (a *assignmentSet) secret(readTx store.ReadTx, task *api.Task, secretID string) (*api.Secret, bool, error) { 318 secret := store.GetSecret(readTx, secretID) 319 if secret == nil { 320 return nil, false, fmt.Errorf("secret not found") 321 } 322 if secret.Spec.Driver == nil { 323 return secret, false, nil 324 } 325 d, err := a.dp.NewSecretDriver(secret.Spec.Driver) 326 if err != nil { 327 return nil, false, err 328 } 329 value, doNotReuse, err := d.Get(&secret.Spec, task) 330 if err != nil { 331 return nil, false, err 332 } 333 if err := validation.ValidateSecretPayload(value); err != nil { 334 return nil, false, err 335 } 336 // Assign the secret 337 secret.Spec.Data = value 338 return secret, doNotReuse, nil 339 }