code: func RootRotationProgress(ctx context.Context, dclient client.APIClient, progressWriter io.WriteCloser) error {
  	defer progressWriter.Close()
  
  	progressOut := streamformatter.NewJSONProgressOutput(progressWriter, false)
  
  	sigint := make(chan os.Signal, 1)
  	signal.Notify(sigint, os.Interrupt)
  	defer signal.Stop(sigint)
  
  	// draw 2 progress bars, 1 for nodes with the correct cert, 1 for nodes with the correct trust root
  	progress.Update(progressOut, "desired root digest", "")
  	progress.Update(progressOut, certsRotatedStr, certsAction)
  	progress.Update(progressOut, rootsRotatedStr, rootsAction)
  
  	var done bool
  
  	for {
  		info, err := dclient.SwarmInspect(ctx)
  		if err != nil {
  			return err
  		}
  
  		if done {
  			return nil
  		}
  
  		nodes, err := dclient.NodeList(ctx, types.NodeListOptions{})
  		if err != nil {
  			return err
  		}
  
  		done = updateProgress(progressOut, info.ClusterInfo.TLSInfo, nodes, info.ClusterInfo.RootRotationInProgress)
  
  		select {
  		case <-time.After(200 * time.Millisecond):
  		case <-sigint:
  			if !done {
  				progress.Message(progressOut, "", "Operation continuing in background.")
  				progress.Message(progressOut, "", "Use `swarmctl cluster inspect default` to check progress.")
  			}
  			return nil
  		}
  	}
  }
docs: // RootRotationProgress outputs progress information for convergence of a root rotation.