pulsarctl

repository·master·Indexed 41 days ago

https://github.com/streamnative/pulsarctl

A CLI tool for managing Apache Pulsar clusters, providing a streamlined interface for interacting with resources such as topics, subscriptions, functions, and bookies. It supports multiple output formats (text, json, yaml) and provides shell autocompletion for Zsh, Bash, and Fish.

Tokens
7.7K
Snippets
30
Records
37
Agent score
49%

What's inside pulsarctl

  1. How pulsarctl plugins work

    master

    Plugins are implemented as external command-line executables. There is no special installation or pre-loading mechanism required.

    Key behaviors:

    • Discovery: pulsarctl finds plugins by looking for executables in your PATH that start with the prefix pulsarctl-.
    • Environment: Plugins inherit the environment from the pulsarctl binary.
    • Argument Passing: All arguments and flags passed to the pulsarctl <command> call are passed as-is to the plugin executable.
    • Command Mapping: The name of the executable determines the command path. For example, an executable named pulsarctl-foo implements the command pulsarctl foo.
  2. Write output with support for multiple formats

    master

    To emit output that respects the user's requested format (text, json, or yaml), a command should use the vc.OutputConfig.WriteOutput(...) method.

    Instead of manually formatting strings, commands should use the cmdutils.OutputContent implementation of the cmdutils.OutputNegotiable interface. This allows the command to provide a Go struct that the framework can automatically marshal into JSON or YAML, while also providing a custom text representation.

    There are two primary ways to provide the text representation:

    1. Using a format string: Use cmdutils.WithText(formatString).
    2. Using a function: Use cmdutils.WithTextFunc(func) to generate a more complex layout, such as a table.

    Note: For scripting purposes, always use -o json to ensure stable output, as the default text representation is subject to change.

    // Example pattern for writing output
    func myRunFunc(vc *cmdutils.VerbCmd) error {
        data := MyStruct{Field: "value"}
        
        // Use OutputContent to handle text, json, and yaml
        output := cmdutils.OutputContent(data). 
            WithText("Field: %s\n", data.Field)
    
        return vc.OutputConfig.WriteOutput(vc, output)
    }
  3. Project structure of pulsarctl

    master

    The pulsarctl project is organized into several key directories:

    • pkg/auth: Encryption and authentication related code.
    • pkg/cmdutils: A wrapper for the cobra library used for command handling.
    • pkg/ctl: The core package containing the implementation of all pulsarctl commands. Commands are organized into subdirectories corresponding to their resource type (e.g., cluster, topic, tenant).
    • pkg/pulsar: A public package that defines the interfaces and data structures used by the commands. It references auth but does not reference ctl or cmdutils to avoid circular dependencies.
    • test: Resources used for testing.
    • docs: Project documentation.
    • site: Website-related code for user documentation.
  4. Define Pulsar API interfaces in the pulsar package

    master

    To maintain a clean architecture, define the Pulsar API interfaces in the pkg/pulsar package. This allows the ctl package to call abstracted methods without being tightly coupled to the underlying implementation.

    For example, to implement topic operations:

    1. Define a Topics interface in pkg/pulsar.
    2. Implement the interface in a struct that handles the HTTP requests (using put, get, delete, or post).
    // In pkg/pulsar/admin.go
    type Client interface {
    	Topics() Topics
    }
    
    // In pkg/pulsar/topic.go
    type Topics interface {
    	Create(TopicName, int) error
    }
    
    func (t *topics) Create(topic TopicName, partitions int) error {
    	endpoint := t.client.endpoint(t.basePath, topic.GetRestPath(), "partitions")
    	if partitions == 0 {
    		endpoint = t.client.endpoint(t.basePath, topic.GetRestPath())
    	}
    	return t.client.put(endpoint, partitions, nil)
    }
  5. Manage multiple clusters using pulsarctl context

    master

    In multi-cluster environments, pulsarctl context allows you to cache connection information for multiple clusters and switch between them easily. This prevents the need to manually specify service URLs for every command.

    Core Workflow

    1. Define a context with its service URLs and authentication details.
    2. Switch to a specific context to make it the active cluster.
    3. Verify which context is currently active.

    This feature was introduced in v0.3.0.

  6. Understand the pulsarctl command syntax

    master

    The pulsarctl CLI follows a specific hierarchical structure for executing commands. The syntax is:

    pulsarctl [resource] [command] [name] [flags]
    • resource (Required): The type of Pulsar object you are interacting with (e.g., topics, tenants, namespaces).
    • command (Required): The action to perform (e.g., create, get, delete, update, list).
    • name (Required): The specific name of the resource. Note that name is case-sensitive.
    • flags (Optional): Configuration modifiers. Flags provided via the command line override default values and environment variables.
    pulsarctl topics list public/default
  7. Enable pulsarctl autocompletion for Fish

    master

    You can enable autocompletion for the Fish shell either for the current session only or permanently for all future sessions.

    # For the current session only
    pulsarctl completion fish | source
    
    # For all future sessions
    pulsarctl completion fish > ~/.config/fish/completions/pulsarctl.fish
  8. Configure JWT authentication for pulsarctl client

    master

    To authenticate using a JSON Web Token (JWT), use either the --token flag for a raw string or the --token-file flag to point to a file containing the token. You only need to specify one of these.

    # Using a raw token string
    pulsarctl --token "YOUR_JWT_TOKEN" topics list public/default
    
    # Using a token file
    pulsarctl --token-file file:///test/auth/tokens/token topics list public/default
  9. Implement a command with CreateTopicCmd and doCreateTopic

    master

    Each command file typically requires two functions:

    1. CreateTopicCmd(vc *cmdutils.VerbCmd): Configures the command metadata using the vc object. You must set:

      • Description: Use pulsar.LongDescription to set CommandUsedFor, CommandPermission, CommandExamples, and CommandOutput.
      • Arguments/Flags:
        • For positional arguments, use vc.SetRunFuncWithMultiNameArgs or vc.SetRunFuncWithNameArg.
        • For flags, use vc.FlagSetGroup.InFlagSet to define parameters (e.g., BoolVarP). Use cobra.MarkFlagRequired for mandatory flags.
        • For flag-based commands, use vc.SetRunFunc.
    2. doCreateTopic(vc *cmdutils.VerbCmd): Contains the execution logic.

      • Initialize a Pulsar client using cmdutils.NewPulsarClient() (V2) or cmdutils.NewPulsarClientWithApiVersion(version).
      • Call the appropriate interface method from the pulsar package.
      • Handle output using vc.Command.Printf for single lines, PrintJson for JSON, or tablewriter for tables.
    func CreateTopicCmd(vc *cmdutils.VerbCmd) {
    	var desc pulsar.LongDescription
    	desc.CommandUsedFor = "This command is used for creating topic."
    	desc.CommandPermission = "This command requires namespace admin permissions."
    
    	var examples []pulsar.Example
    	examples = append(examples, pulsar.Example{
    		Desc:    "Create a non-partitioned topic <topic-name>",
    		Command: "Pulsarctl topics create <topic-name> 0",
    	})
    	desc.CommandExamples = examples
    
    	vc.SetDescription(
    		"create",
    		"Create a topic with n partitions",
    		desc.ToString(),
    		desc.ExampleToString(),
    		"c")
    
    	vc.SetRunFuncWithMultiNameArgs(func() error {
    		return doCreateTopic(vc)
    	}, CheckTopicNameTwoArgs)
    }
    
    func doCreateTopic(vc *cmdutils.VerbCmd) error {
    	admin := cmdutils.NewPulsarClient()
    	err := admin.Topics().Create(topic, partitions)
    	if err == nil {
    		vc.Command.Printf("Create topic %s with %d partitions successfully\n", topic.String(), partitions)
    	}
    	return err
    }
  10. Use pulsarctl as a Go project dependency

    master

    You can include pulsarctl in your Go project using go get. Note that v0.y.z tags are not released, so you must specify a branch or a specific version tag.

    # Using master branch
    go get github.com/streamnative/pulsarctl@master
    
    # Or using v2.10.1.3 tag
    go get github.com/streamnative/pulsarctl@v2.10.1.3
    
    # Or using v2.9.3.3 tag
    go get github.com/streamnative/pulsarctl@v2.9.3.3
  11. Enable pulsarctl autocompletion for Bash

    master

    Enabling autocompletion for Bash requires bash-completion version 2 and Bash 4.1 or newer. This is particularly important on macOS, where the default Bash 3.2 is incompatible with the pulsarctl completion script.

    Prerequisites

    1. Ensure you are using Bash 4.1+.
    2. Install bash-completion@2 (e.g., via Homebrew).
    3. Configure your ~/.bashrc to include the bash-completion environment variables and source path.

    Setup Steps

    Generate the script using pulsarctl completion bash and either place it in /usr/local/etc/bash_completion.d/ or source it directly in your ~/.bashrc.

    # 1. Install bash-completion v2
    brew install bash-completion@2
    
    # 2. Add to ~/.bashrc
    export BASH_COMPLETION_COMPAT_DIR="/usr/local/etc/bash_completion.d"
    [[ -r "/usr/local/etc/profile.d/bash_completion.sh" ]] && . "/usr/local/etc/profile.d/bash_completion.sh"
    
    # 3. Enable pulsarctl completion
    pulsarctl completion bash >/usr/local/etc/bash_completion.d/pulsarctl.bash
    echo 'source /usr/local/etc/bash_completion.d/pulsarctl.bash' >> ~/.bashrc
    source ~/.bashrc