github.com/AliyunContainerService/cli@v0.0.0-20181009023821-814ced4b30d0/cli/command/swarm/progress/root_rotation.go (about)

     1  package progress
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io"
     7  	"os"
     8  	"os/signal"
     9  	"time"
    10  
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/api/types/swarm"
    13  	"github.com/docker/docker/client"
    14  	"github.com/docker/docker/pkg/progress"
    15  	"github.com/docker/docker/pkg/streamformatter"
    16  	"github.com/opencontainers/go-digest"
    17  )
    18  
    19  const (
    20  	certsRotatedStr = "  rotated TLS certificates"
    21  	rootsRotatedStr = "  rotated CA certificates"
    22  	// rootsAction has a single space because rootsRotatedStr is one character shorter than certsRotatedStr.
    23  	// This makes sure the progress bar are aligned.
    24  	certsAction = ""
    25  	rootsAction = " "
    26  )
    27  
    28  // RootRotationProgress outputs progress information for convergence of a root rotation.
    29  func RootRotationProgress(ctx context.Context, dclient client.APIClient, progressWriter io.WriteCloser) error {
    30  	defer progressWriter.Close()
    31  
    32  	progressOut := streamformatter.NewJSONProgressOutput(progressWriter, false)
    33  
    34  	sigint := make(chan os.Signal, 1)
    35  	signal.Notify(sigint, os.Interrupt)
    36  	defer signal.Stop(sigint)
    37  
    38  	// draw 2 progress bars, 1 for nodes with the correct cert, 1 for nodes with the correct trust root
    39  	progress.Update(progressOut, "desired root digest", "")
    40  	progress.Update(progressOut, certsRotatedStr, certsAction)
    41  	progress.Update(progressOut, rootsRotatedStr, rootsAction)
    42  
    43  	var done bool
    44  
    45  	for {
    46  		info, err := dclient.SwarmInspect(ctx)
    47  		if err != nil {
    48  			return err
    49  		}
    50  
    51  		if done {
    52  			return nil
    53  		}
    54  
    55  		nodes, err := dclient.NodeList(ctx, types.NodeListOptions{})
    56  		if err != nil {
    57  			return err
    58  		}
    59  
    60  		done = updateProgress(progressOut, info.ClusterInfo.TLSInfo, nodes, info.ClusterInfo.RootRotationInProgress)
    61  
    62  		select {
    63  		case <-time.After(200 * time.Millisecond):
    64  		case <-sigint:
    65  			if !done {
    66  				progress.Message(progressOut, "", "Operation continuing in background.")
    67  				progress.Message(progressOut, "", "Use `swarmctl cluster inspect default` to check progress.")
    68  			}
    69  			return nil
    70  		}
    71  	}
    72  }
    73  
    74  func updateProgress(progressOut progress.Output, desiredTLSInfo swarm.TLSInfo, nodes []swarm.Node, rootRotationInProgress bool) bool {
    75  	// write the current desired root cert's digest, because the desired root certs might be too long
    76  	progressOut.WriteProgress(progress.Progress{
    77  		ID:     "desired root digest",
    78  		Action: digest.FromBytes([]byte(desiredTLSInfo.TrustRoot)).String(),
    79  	})
    80  
    81  	// If we had reached a converged state, check if we are still converged.
    82  	var certsRight, trustRootsRight int64
    83  	for _, n := range nodes {
    84  		if bytes.Equal(n.Description.TLSInfo.CertIssuerPublicKey, desiredTLSInfo.CertIssuerPublicKey) &&
    85  			bytes.Equal(n.Description.TLSInfo.CertIssuerSubject, desiredTLSInfo.CertIssuerSubject) {
    86  			certsRight++
    87  		}
    88  
    89  		if n.Description.TLSInfo.TrustRoot == desiredTLSInfo.TrustRoot {
    90  			trustRootsRight++
    91  		}
    92  	}
    93  
    94  	total := int64(len(nodes))
    95  	progressOut.WriteProgress(progress.Progress{
    96  		ID:      certsRotatedStr,
    97  		Action:  certsAction,
    98  		Current: certsRight,
    99  		Total:   total,
   100  		Units:   "nodes",
   101  	})
   102  
   103  	rootsProgress := progress.Progress{
   104  		ID:      rootsRotatedStr,
   105  		Action:  rootsAction,
   106  		Current: trustRootsRight,
   107  		Total:   total,
   108  		Units:   "nodes",
   109  	}
   110  
   111  	if certsRight == total && !rootRotationInProgress {
   112  		progressOut.WriteProgress(rootsProgress)
   113  		return certsRight == total && trustRootsRight == total
   114  	}
   115  
   116  	// we still have certs that need renewing, so display that there are zero roots rotated yet
   117  	rootsProgress.Current = 0
   118  	progressOut.WriteProgress(rootsProgress)
   119  	return false
   120  }