func execute(fun func(ctx context.Context, f *franz.Franz) (string, error)) error {
	ctx, cancel := context.WithCancel(context.Background())
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-signals
		cancel()  
		<-signals
		os.Exit(1)  
	}()
	f, err := franz.New(getFranzConfig(), verbose)
	if err != nil {
		return err
	}
	defer f.Close()
	out, err := fun(ctx, f)
	if err != nil {
		return err
	}
	if out != "" {
		fmt.Println(out)
	}
	return nil
}
execute does several things:
1. creates new franz instance
2. executes the passed in function
3. on success, prints the return value to the console,
otherwise it just returns the error
package cmd
import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"github.com/open-ch/franz/pkg/franz"
	"github.com/open-ch/franz/pkg/list"
	"github.com/pkg/errors"
	"github.com/spf13/viper"
	"gopkg.in/yaml.v2"
)
func getFranzConfig() franz.Config {
	c := franz.Config{
		KafkaVersion:   viper.GetString("kafka_version"),
		Brokers:        viper.GetStringSlice("brokers"),
		SchemaRegistry: viper.GetString("registry"),
	}
	certFile := viper.GetString("tls.cert")
	keyFile := viper.GetString("tls.key")
	caFile := viper.GetString("tls.caCert")
	if certFile != "" || keyFile != "" || caFile != "" {
		c.TLSConfig = &franz.TLSConfig{
			CertFile: certFile,
			KeyFile:  keyFile,
			CaFile:   caFile,
		}
	}
	return c
}