code: func (r *rootRotationReconciler) finishRootRotation(tx store.Tx, expectedRootCA *api.RootCA) error {
  	cluster := store.GetCluster(tx, r.clusterID)
  	if cluster == nil {
  		return fmt.Errorf("unable to get cluster %s", r.clusterID)
  	}
  
  	// If the RootCA object has changed (because another root rotation was started or because some other node
  	// had finished the root rotation), we cannot finish the root rotation that we were working on.
  	if !equality.RootCAEqualStable(expectedRootCA, &cluster.RootCA) {
  		return errRootRotationChanged
  	}
  
  	var signerCert []byte
  	if len(cluster.RootCA.RootRotation.CAKey) > 0 {
  		signerCert = cluster.RootCA.RootRotation.CACert
  	}
  	// we don't actually have to parse out the default node expiration from the cluster - we are just using
  	// the ca.RootCA object to generate new tokens and the digest
  	updatedRootCA, err := NewRootCA(cluster.RootCA.RootRotation.CACert, signerCert, cluster.RootCA.RootRotation.CAKey,
  		DefaultNodeCertExpiration, nil)
  	if err != nil {
  		return errors.Wrap(err, "invalid cluster root rotation object")
  	}
  	cluster.RootCA = api.RootCA{
  		CACert:     cluster.RootCA.RootRotation.CACert,
  		CAKey:      cluster.RootCA.RootRotation.CAKey,
  		CACertHash: updatedRootCA.Digest.String(),
  		JoinTokens: api.JoinTokens{
  			Worker:  GenerateJoinToken(&updatedRootCA, cluster.FIPS),
  			Manager: GenerateJoinToken(&updatedRootCA, cluster.FIPS),
  		},
  		LastForcedRotation: cluster.RootCA.LastForcedRotation,
  	}
  	return store.UpdateCluster(tx, cluster)
  }
docs: // This function assumes that the expected root CA has root rotation.  This is intended to be used by
  // `reconcileNodeRootsAndCerts`, which uses the root CA from the `lastSeenClusterRootCA`, and checks
  // that it has a root rotation before calling this function.