mirror of https://github.com/docker/cli.git
121 lines
3.4 KiB
Go
121 lines
3.4 KiB
Go
package progress
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"os/signal"
|
|
"time"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/swarm"
|
|
"github.com/docker/docker/client"
|
|
"github.com/docker/docker/pkg/progress"
|
|
"github.com/docker/docker/pkg/streamformatter"
|
|
"github.com/opencontainers/go-digest"
|
|
)
|
|
|
|
const (
|
|
certsRotatedStr = " rotated TLS certificates"
|
|
rootsRotatedStr = " rotated CA certificates"
|
|
// rootsAction has a single space because rootsRotatedStr is one character shorter than certsRotatedStr.
|
|
// This makes sure the progress bar are aligned.
|
|
certsAction = ""
|
|
rootsAction = " "
|
|
)
|
|
|
|
// RootRotationProgress outputs progress information for convergence of a root rotation.
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateProgress(progressOut progress.Output, desiredTLSInfo swarm.TLSInfo, nodes []swarm.Node, rootRotationInProgress bool) bool {
|
|
// write the current desired root cert's digest, because the desired root certs might be too long
|
|
progressOut.WriteProgress(progress.Progress{
|
|
ID: "desired root digest",
|
|
Action: digest.FromBytes([]byte(desiredTLSInfo.TrustRoot)).String(),
|
|
})
|
|
|
|
// If we had reached a converged state, check if we are still converged.
|
|
var certsRight, trustRootsRight int64
|
|
for _, n := range nodes {
|
|
if bytes.Equal(n.Description.TLSInfo.CertIssuerPublicKey, desiredTLSInfo.CertIssuerPublicKey) &&
|
|
bytes.Equal(n.Description.TLSInfo.CertIssuerSubject, desiredTLSInfo.CertIssuerSubject) {
|
|
certsRight++
|
|
}
|
|
|
|
if n.Description.TLSInfo.TrustRoot == desiredTLSInfo.TrustRoot {
|
|
trustRootsRight++
|
|
}
|
|
}
|
|
|
|
total := int64(len(nodes))
|
|
progressOut.WriteProgress(progress.Progress{
|
|
ID: certsRotatedStr,
|
|
Action: certsAction,
|
|
Current: certsRight,
|
|
Total: total,
|
|
Units: "nodes",
|
|
})
|
|
|
|
rootsProgress := progress.Progress{
|
|
ID: rootsRotatedStr,
|
|
Action: rootsAction,
|
|
Current: trustRootsRight,
|
|
Total: total,
|
|
Units: "nodes",
|
|
}
|
|
|
|
if certsRight == total && !rootRotationInProgress {
|
|
progressOut.WriteProgress(rootsProgress)
|
|
return certsRight == total && trustRootsRight == total
|
|
}
|
|
|
|
// we still have certs that need renewing, so display that there are zero roots rotated yet
|
|
rootsProgress.Current = 0
|
|
progressOut.WriteProgress(rootsProgress)
|
|
return false
|
|
}
|