Skip to content

Instantly share code, notes, and snippets.

@bart0sh
Last active May 8, 2019 21:10
Show Gist options
  • Save bart0sh/9d16471ac1e20f7c81a996bb68afbcb5 to your computer and use it in GitHub Desktop.
Save bart0sh/9d16471ac1e20f7c81a996bb68afbcb5 to your computer and use it in GitHub Desktop.
type OutputType string
const (
Text OutputType = "text"
YAML = "yaml"
JSON = "json"
)
type Output interface {
Out(name string, data interface{})
Flush() error
}
// Marshal returns data representation in the specified output format
func Marshal(data interface{}, outputFormat OutputType) (string, error) {
if outputFormat == JSON || outputFormat == YAML {
bytes, err := json.MarshalIndent(data, "", " ")
if err != nil {
return "", err
}
if outputFormat == YAML {
bytes, err = yaml.JSONToYAML(bytes)
if err != nil {
return "", errors.Wrap(err, "failed to convert JSON output to YAML")
}
}
return string(bytes), nil
}
return "", errors.Errorf("invalid output format: %s. It should be either YAML or JSON.", outputFormat)
}
// BootstrapTokenList represents information for JSON and YAML output produced by 'kubeadm token list'
type BootstrapTokenList struct {
PublicKeyPins []string `json:"publicKeyPins"`
ControlPlaneHostPort string `json:"controlPlaneHostPort"`
Tokens []*kubeadmapiv1beta1.BootstrapToken `json:"tokens"`
}
type OutputItem struct {
name string
data interface{}
}
type tokenOutput struct {
outWriter io.Writer
errWriter io.Writer
tabWriter io.Writer
outputFormat OutputType
outputItems []*OutputItem
}
func (to *tokenOutput) Out(name string, data interface{}) {
if to.outputFormat == Text {
if to.tabWriter == nil {
var out bytes.Buffer
to.tabWriter = tabwriter.NewWriter(to.outWriter, 10, 4, 3, ' ', 0)
fmt.Fprintln(to.tabWriter, "TOKEN\tDESCRIPTION\tEXPIRES\tTTL")
}
if name == "token" {
token := *kubeadmapiv1beta1.BootstrapToken(data)
// Get the human-friendly string representation for the token
humanFriendlyTokenOutput := humanReadableBootstrapToken(token)
fmt.Fprintln(to.tabWriter, humanFriendlyTokenOutput)
}
} else if to.outputFormat == YAML || to.outputFormat == JSON {
to.outputItems = append(to.outputItems, &OutputItem{name, data})
}
}
func (to *tokenOutput) Flush() error {
if to.outputFormat == YAML || to.outputFormat == JSON {
tList := BootstrapTokenList{}
for _, item := range to.outputItems {
switch item.name {
case "token":
tList.Tokens = append(tList.Tokens, item.data)
case "publicKeyPins":
tList.PublicKeyPins = item.data
case "controlPlaneHostPort":
tList.ControlPlaneHostPort = item.data
}
}
formatted, err := utils.Marshal(tList, to.outputFormat)
if err != nil {
return err
}
fmt.Printf(to.outWriter, formatted)
}
return nil
}
// RunListTokens lists details on all existing bootstrap tokens on the server.
func RunListTokens(out io.Writer, errW io.Writer, client clientset.Interface, kubeConfigFile, outputFormat OutputType) error {
// First, build our selector for bootstrap tokens only
klog.V(1).Infoln("[token] preparing selector for bootstrap token")
tokenSelector := fields.SelectorFromSet(
map[string]string{
// TODO: We hard-code "type" here until `field_constants.go` that is
// currently in `pkg/apis/core/` exists in the external API, i.e.
// k8s.io/api/v1. Should be v1.SecretTypeField
"type": string(bootstrapapi.SecretTypeBootstrapToken),
},
)
listOptions := metav1.ListOptions{
FieldSelector: tokenSelector.String(),
}
klog.V(1).Infoln("[token] retrieving list of bootstrap tokens")
secrets, err := client.CoreV1().Secrets(metav1.NamespaceSystem).List(listOptions)
if err != nil {
return errors.Wrap(err, "failed to list bootstrap tokens")
}
output := tokenOutput{outWriter: out, errWriter: errW, outputFormat: outputFormat}
for _, secret := range secrets.Items {
// Get the BootstrapToken struct representation from the Secret object
token, err := kubeadmapi.BootstrapTokenFromSecret(&secret)
if err != nil {
fmt.Fprintf(errW, "%v", err)
continue
}
output.Out("token", token)
}
clusterConfig, err := cmdutil.GetClusterConfig(kubeConfigFile)
if err != nil {
return errors.Wrapf(err, "failed to load cluster config from %s", kubeConfigFile)
}
publicKeyPins, err := cmdutil.GetCAPubKeyPins(clusterConfig)
if err != nil {
return errors.Wrapf(err, "failed to get CA certs hashes from cluster config")
}
output.Out("publicKeyPins", publicKeyPins)
output.Out("controlPlaneHostPort", strings.Replace(clusterConfig.Server, "https://", "", -1))
return output.Flush()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment