Repository: hashicorp/nomad
File: nomad/encrypter.go (Go)

Code before:
		}

		wrapper, err := e.newKMSWrapper(provider, wrappedKeys.KeyID, wrappedKey.KeyEncryptionKey)
		if err != nil {
			// the errors that bubble up from this library can be a bit opaque, so
			// make sure we wrap them with as much context as possible
			err := fmt.Errorf("unable to create KMS wrapper for provider %q: %w", providerID, err)
			mErr = multierror.Append(mErr, err)
			continue
		}

		// fan-out decryption tasks for HA in Nomad Enterprise. we can use the
		// key whenever any one provider returns a successful decryption.
		go e.decryptWrappedKeyTask(completeCtx, wrapper, wrappedKeys.Meta(), wrappedKey, cipherSetCh)
		decryptTasks++
	}

	if err := mErr.ErrorOrNil(); err != nil {

		// If we have no tasks running, we can log an error for the operator,
		// remove any tracking and exit.
		if decryptTasks == 0 {

			e.decryptTasksLock.Lock()
			delete(e.decryptTasks, wrappedKeys.KeyID)
			e.decryptTasksLock.Unlock()

			logger.Error("root key cannot be decrypted", "error", err)
			return err
		}

		// If we have at least one task running, we can log a warning for the
		// operator but continue to wait for the other tasks to complete.
		logger.Warn("root key cannot be decrypted by some KMS providers",
			"error", err)
	}

	// The routine will now wait until the server tells us to stop or until we
	// successfully decrypt the key. The context's cancellation function has a
	// deferred call above, so we don't need to worry about it here unless we
	// want to preemptively cancel in-flight tasks.
	select {
	case <-completeCtx.Done():

		// In this event, the server is shutting down and the agent process will
		// exit. This means the decrypter state will be lost, so there is no
		// need to tidy the decryption task state.
		return completeCtx.Err()

	case generatedCipher := <-cipherSetCh:


Review comment (suggestion):
Something important to note here is that this will now unblock `IsReady` even though we haven't decrypted the root key. However, it looks like the only way to reach this is if the server is misconfigured -- it's missing a KMS provider, the KMS provider is misconfigured, or the KEK buffer isn't a valid key. 

So I think that's actually fine but we should probably note it in the upgrade guide. The user will just get a bunch of errors if this happens.

Code after:

	for _, wrappedKey := range wrappedKeys.WrappedKeys {
		providerID := wrappedKey.ProviderID
		if providerID == "" {
			providerID = string(structs.KEKProviderAEAD)
		}

		provider, ok := e.providerConfigs[providerID]
		if !ok {
			err := fmt.Errorf("no such KMS provider %q configured", providerID)
			mErr = multierror.Append(mErr, err)
			continue
		}

		wrapper, err := e.newKMSWrapper(provider, wrappedKeys.KeyID, wrappedKey.KeyEncryptionKey)
		if err != nil {
			// the errors that bubble up from this library can be a bit opaque, so
			// make sure we wrap them with as much context as possible
			err := fmt.Errorf("unable to create KMS wrapper for provider %q: %w", providerID, err)
			mErr = multierror.Append(mErr, err)
			continue
		}

		// fan-out decryption tasks for HA in Nomad Enterprise. we can use the
		// key whenever any one provider returns a successful decryption.
		go e.decryptWrappedKeyTask(completeCtx, wrapper, wrappedKeys.Meta(), wrappedKey, cipherSetCh)
		decryptTasks++
	}

	if err := mErr.ErrorOrNil(); err != nil {

		// If we have no tasks running, we can log an error for the operator and
		// exit.
		//
		// It is likely any decryption configuration for the key is incorrect
		// and follow-up attempts from other Raft/FMS calls for this key will
		// also fail. We should not, however, continue with the server startup
		// without this key, and therefore we do not delete any added tracking.
		if decryptTasks == 0 {
			logger.Error("root key cannot be decrypted", "error", err)
			return err
		}

		// If we have at least one task running, we can log a warning for the
		// operator but continue to wait for the other tasks to complete.
		logger.Warn("root key cannot be decrypted by some KMS providers",
			"error", err)
	}

	// The routine will now wait until the server tells us to stop or until we
	// successfully decrypt the key. The context's cancellation function has a