func splitWhitespace(s string) []string {
	re := regexp.MustCompile(`\s+`)
	return strings.Split(re.ReplaceAllString(s, `\s`), `\s`)
}
Given a string with multiple consecutive spaces, splitWhitespace returns a
slice of strings which represent the given string split by \s characters with
all duplicates removed
package chef
import (
	"bufio"
	"bytes"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math/big"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)
type Chef struct {
	Host         string
	Url          string
	Port         string
	Version      string
	Key          *rsa.PrivateKey
	UserId       string
	SSLNoVerify  bool
	Organization string
}
func Connect(filename ...string) (*Chef, error) {
	knifeFiles := []string{}
	if len(filename) > 0 {
		for _, v := range filename {
			knifeFiles = append(knifeFiles, v)
		}
	}
	knifeFiles = append(knifeFiles, ".chef/knife.rb")
	homedir := os.Getenv("HOME")
	if homedir != "" {
		knifeFiles = append(knifeFiles, filepath.Join(homedir, ".chef/knife.rb"))
	}
	knifeFiles = append(knifeFiles, "/etc/chef/client.rb")
	knifeFiles = append(knifeFiles, "test/support/knife.rb")
	var knifeFile string
	for _, each := range knifeFiles {
		if _, err := os.Stat(each); err == nil {
			knifeFile = each
			break
		}
	}
	if knifeFile == "" {
		return nil, errors.New("Configuration file not found")
	}
	file, err := os.Open(knifeFile)
	defer file.Close()
	if err != nil {
		return nil, err
	}
	scanner := bufio.NewScanner(file)
	chef := new(Chef)
	for scanner.Scan() {
		split := splitWhitespace(scanner.Text())
		if len(split) == 2 {
			switch split[0] {
			case "node_name":
				chef.UserId = filterQuotes(split[1])
			case "client_key":
				key, err := keyFromFile(filterQuotes(split[1]))
				if err != nil {
					return nil, err
				}
				chef.Key = key
			case "chef_server_url":
				parsedUrl := filterQuotes(split[1])
				chef.Url = parsedUrl
				chefUrl, err := url.Parse(parsedUrl)
				if err != nil {
					return nil, err
				}
				hostPath := strings.Split(chefUrl.Path, "/")
				if len(hostPath) == 3 && hostPath[1] == "organizations" {
					chef.Organization = hostPath[2]
				}
				hostPort := strings.Split(chefUrl.Host, ":")
				if len(hostPort) == 2 {
					chef.Host = hostPort[0]
					chef.Port = hostPort[1]
				} else if len(hostPort) == 1 {
					chef.Host = hostPort[0]
					switch chefUrl.Scheme {
					case "http":
						chef.Port = "80"
					case "https":
						chef.Port = "443"
					default:
						return nil, errors.New("Invalid http scheme")
					}
				} else {
					return nil, errors.New("Invalid host format")
				}
			}
		}
	}
	if chef.Key == nil {
		return nil, errors.New("missing 'client_key' in knife.rb")
	}
	if chef.Version == "" {
		chef.Version = "11.6.0"
	}
	return chef, nil
}
func ConnectBuilder(host, port, version, userid, key string, organization string) (*Chef, error) {
	chef := new(Chef)
	chef.Host = host
	chef.Port = port
	chef.Version = version
	chef.UserId = userid
	chef.Organization = organization
	var url string
	switch chef.Port {
	case "443":
		url = fmt.Sprintf("https://%s", chef.Host)
	case "80":
		url = fmt.Sprintf("http://%s", chef.Host)
	default:
		url = fmt.Sprintf("%s:%s", chef.Host, chef.Port)
	}
	if chef.Organization != "" {
		url += "/organizations/" + chef.Organization
	}
	chef.Url = url
	var rsaKey *rsa.PrivateKey
	var err error
	if strings.Contains(key, "-----BEGIN RSA PRIVATE KEY-----") {
		rsaKey, err = keyFromString([]byte(key))
	} else {
		rsaKey, err = keyFromFile(key)
	}
	if err != nil {
		return nil, err
	}
	chef.Key = rsaKey
	if chef.Version == "" {
		chef.Version = "11.6.0"
	}
	return chef, nil
}
func filterQuotes(s string) string {
	re1 := regexp.MustCompile(`^(\'|\")`)
	re2 := regexp.MustCompile(`(\'|\")$`)
	return re2.ReplaceAllString(re1.ReplaceAllString(s, ``), ``)
}