<commit_before>package winapi

import (
	"unsafe"
)

// https://msdn.microsoft.com/en-us/library/ms646289.aspx
func ActivateKeyboardLayout(hkl uintptr, Flags uintptr) (uintptr, error) {
	r1, _, err := procActivateKeyboardLayout.Call(hkl, Flags)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646290.aspx
func BlockInput(fBlockIt uintptr) (uintptr, error) {
	r1, _, err := procBlockInput.Call(fBlockIt)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646291.aspx
func EnableWindow(hWnd uintptr, bEnable uintptr) (uintptr, error) {
	r1, _, err := procEnableWindow.Call(hWnd, bEnable)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646292.aspx
func GetActiveWindow() (uintptr, error) {
	r1, _, err := procGetActiveWindow.Call()
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646293.aspx
func GetAsyncKeyState(vKey uintptr) (uintptr, error) {
	r1, _, err := procGetAsyncKeyState.Call(vKey)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646294.aspx
func GetFocus() (uintptr, error) {
	r1, _, err := procGetFocus.Call()
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646295.aspx
func GetKBCodePage() (uintptr, error) {
	r1, _, err := procGetKBCodePage.Call()
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646296.aspx
func GetKeyboardLayout(idThread uintptr) (uintptr, error) {
	r1, _, err := procGetKeyboardLayout.Call(idThread)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646297.aspx
func GetKeyboardLayoutList(nBuff uintptr, lpList []uintptr) (uintptr, error) {
	r1, _, err := procGetKeyboardLayoutList.Call(nBuff, uintptr(unsafe.Pointer(&lpList[0])))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646298.aspx
func GetKeyboardLayoutName(pwszKLID []uint16) (uintptr, error) {
	r1, _, err := procGetKeyboardLayoutName.Call(uintptr(unsafe.Pointer(&pwszKLID[0])))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646299.aspx
func GetKeyboardState(lpKeyState []uint8) (uintptr, error) {
	r1, _, err := procGetKeyboardState.Call(uintptr(unsafe.Pointer(&lpKeyState[0])))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms724336.aspx
func GetKeyboardType(nTypeFlag uintptr) (uintptr, error) {
	r1, _, err := procGetKeyboardType.Call(nTypeFlag)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646300.aspx
func GetKeyNameText(lParam int32, lpString []uint16, cchSize uintptr) (uintptr, error) {
	r1, _, err := procGetKeyNameText.Call(int32ToUintptr(lParam), uintptr(unsafe.Pointer(&lpString[0])), cchSize)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646301.aspx
func GetKeyState(nVirtKey uintptr) (uintptr, error) {
	r1, _, err := procGetKeyState.Call(nVirtKey)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646302.aspx
func GetLastInputInfo(plii *LASTINPUTINFO) (uintptr, error) {
	r1, _, err := procGetLastInputInfo.Call(uintptr(unsafe.Pointer(plii)))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646303.aspx
func IsWindowEnabled(hWnd uintptr) (uintptr, error) {
	r1, _, err := procIsWindowEnabled.Call(hWnd)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646304.aspx
func Keybd_event(bVk uintptr, bScan uintptr, dwFlags uintptr, dwExtraInfo uintptr) (uintptr, error) {
	r1, _, err := procKeybd_event.Call(bVk, bScan, dwFlags, dwExtraInfo)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646305.aspx
func LoadKeyboardLayout(pwszKLID string, Flags uintptr) (uintptr, error) {
	_pwszKLID, err := stringToUintptr(pwszKLID)
	if err != nil {
		return 0, err
	}
	r1, _, err := procLoadKeyboardLayout.Call(uintptr(unsafe.Pointer(_pwszKLID)), Flags)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646306.aspx
func MapVirtualKey(uCode uintptr, uMapType uintptr) (uintptr, error) {
	r1, _, err := procMapVirtualKey.Call(uCode, uMapType)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646307.aspx
func MapVirtualKeyEx(uCode uintptr, uMapType uintptr, dwhkl *uintptr) (uintptr, error) {
	r1, _, err := procMapVirtualKeyEx.Call(uCode, uMapType, uintptr(unsafe.Pointer(dwhkl)))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646308.aspx
func OemKeyScan(wOemChar uintptr) (uintptr, error) {
	r1, _, err := procOemKeyScan.Call(wOemChar)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646309.aspx
func RegisterHotKey(hWnd uintptr, id uintptr, fsModifiers uintptr, vk uintptr) (uintptr, error) {
	r1, _, err := procRegisterHotKey.Call(hWnd, id, fsModifiers, vk)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646310.aspx
func SendInputM(nInputs uintptr, pInputs []INPUT_M, cbSize uintptr) (uintptr, error) {
	r1, _, err := procSendInput.Call(nInputs, uintptr(unsafe.Pointer(&pInputs[0])), cbSize)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646310.aspx
func SendInputK(nInputs uintptr, pInputs []INPUT_K, cbSize uintptr) (uintptr, error) {
	r1, _, err := procSendInput.Call(nInputs, uintptr(unsafe.Pointer(&pInputs[0])), cbSize)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646310.aspx
func SendInputHW(nInputs uintptr, pInputs []INPUT_HW, cbSize uintptr) (uintptr, error) {
	r1, _, err := procSendInput.Call(nInputs, uintptr(unsafe.Pointer(&pInputs[0])), cbSize)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646311.aspx
func SetActiveWindow(hWnd uintptr) (uintptr, error) {
	r1, _, err := procSetActiveWindow.Call(hWnd)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646312.aspx
func SetFocus(hWnd uintptr) (uintptr, error) {
	r1, _, err := procSetFocus.Call(hWnd)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646314.aspx
func SetKeyboardState(lpKeyState []uint8) (uintptr, error) {
	r1, _, err := procSetKeyboardState.Call(uintptr(unsafe.Pointer(&lpKeyState[0])))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646316.aspx
func ToAscii(uVirtKey uintptr, uScanCode uintptr, lpKeyState []uint8, lpChar []uint16, uFlags uintptr) (uintptr, error) {
	var _lpKeyState uintptr
	if lpKeyState == nil {
		_lpKeyState = 0
	} else {
		_lpKeyState = uintptr(unsafe.Pointer(&lpKeyState[0]))
	}
	r1, _, err := procToAscii.Call(uVirtKey, uScanCode, _lpKeyState, uintptr(unsafe.Pointer(&lpChar[0])), uFlags)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646318.aspx
func ToAsciiEx(uVirtKey uintptr, uScanCode uintptr, lpKeyState []uint8, lpChar []uint16, uFlags uintptr, dwhkl uintptr) (uintptr, error) {
	var _lpKeyState uintptr
	if lpKeyState == nil {
		_lpKeyState = 0
	} else {
		_lpKeyState = uintptr(unsafe.Pointer(&lpKeyState[0]))
	}
	r1, _, err := procToAsciiEx.Call(uVirtKey, uScanCode, _lpKeyState, uintptr(unsafe.Pointer(&lpChar[0])), uFlags, dwhkl)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646320.aspx
func ToUnicode(wVirtKey uintptr, wScanCode uintptr, lpKeyState []uint8, pwszBuff []uint16, cchBuff uintptr, wFlags uintptr) (uintptr, error) {
	var _lpKeyState uintptr
	if lpKeyState == nil {
		_lpKeyState = 0
	} else {
		_lpKeyState = uintptr(unsafe.Pointer(&lpKeyState[0]))
	}
	r1, _, err := procToUnicode.Call(wVirtKey, wScanCode, _lpKeyState, uintptr(unsafe.Pointer(&pwszBuff[0])), cchBuff, wFlags)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646322.aspx
func ToUnicodeEx(wVirtKey uintptr, wScanCode uintptr, lpKeyState []uint8, pwszBuff []uint16, cchBuff uintptr, wFlags uintptr, dwhkl uintptr) (uintptr, error) {
	var _lpKeyState uintptr
	if lpKeyState == nil {
		_lpKeyState = 0
	} else {
		_lpKeyState = uintptr(unsafe.Pointer(&lpKeyState[0]))
	}
	r1, _, err := procToUnicodeEx.Call(wVirtKey, wScanCode, _lpKeyState, uintptr(unsafe.Pointer(&pwszBuff[0])), cchBuff, wFlags, dwhkl)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646324.aspx
func UnloadKeyboardLayout(hkl uintptr) (uintptr, error) {
	r1, _, err := procUnloadKeyboardLayout.Call(hkl)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646327.aspx
func UnregisterHotKey(hWnd uintptr, id uintptr) (uintptr, error) {
	r1, _, err := procUnregisterHotKey.Call(hWnd, id)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646329.aspx
func VkKeyScan(ch uintptr) (uintptr, error) {
	r1, _, err := procVkKeyScan.Call(ch)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646332.aspx
func VkKeyScanEx(ch uintptr, dwhkl uintptr) (uintptr, error) {
	r1, _, err := procVkKeyScanEx.Call(ch, dwhkl)
	return r1, err
}
<commit_msg>removed unnecessary unsafe.Pointer from keyboard_input.go<commit_after>package winapi

import (
	"unsafe"
)

// https://msdn.microsoft.com/en-us/library/ms646289.aspx
func ActivateKeyboardLayout(hkl uintptr, Flags uintptr) (uintptr, error) {
	r1, _, err := procActivateKeyboardLayout.Call(hkl, Flags)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646290.aspx
func BlockInput(fBlockIt uintptr) (uintptr, error) {
	r1, _, err := procBlockInput.Call(fBlockIt)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646291.aspx
func EnableWindow(hWnd uintptr, bEnable uintptr) (uintptr, error) {
	r1, _, err := procEnableWindow.Call(hWnd, bEnable)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646292.aspx
func GetActiveWindow() (uintptr, error) {
	r1, _, err := procGetActiveWindow.Call()
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646293.aspx
func GetAsyncKeyState(vKey uintptr) (uintptr, error) {
	r1, _, err := procGetAsyncKeyState.Call(vKey)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646294.aspx
func GetFocus() (uintptr, error) {
	r1, _, err := procGetFocus.Call()
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646295.aspx
func GetKBCodePage() (uintptr, error) {
	r1, _, err := procGetKBCodePage.Call()
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646296.aspx
func GetKeyboardLayout(idThread uintptr) (uintptr, error) {
	r1, _, err := procGetKeyboardLayout.Call(idThread)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646297.aspx
func GetKeyboardLayoutList(nBuff uintptr, lpList []uintptr) (uintptr, error) {
	r1, _, err := procGetKeyboardLayoutList.Call(nBuff, uintptr(unsafe.Pointer(&lpList[0])))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646298.aspx
func GetKeyboardLayoutName(pwszKLID []uint16) (uintptr, error) {
	r1, _, err := procGetKeyboardLayoutName.Call(uintptr(unsafe.Pointer(&pwszKLID[0])))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646299.aspx
func GetKeyboardState(lpKeyState []uint8) (uintptr, error) {
	r1, _, err := procGetKeyboardState.Call(uintptr(unsafe.Pointer(&lpKeyState[0])))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms724336.aspx
func GetKeyboardType(nTypeFlag uintptr) (uintptr, error) {
	r1, _, err := procGetKeyboardType.Call(nTypeFlag)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646300.aspx
func GetKeyNameText(lParam int32, lpString []uint16, cchSize uintptr) (uintptr, error) {
	r1, _, err := procGetKeyNameText.Call(int32ToUintptr(lParam), uintptr(unsafe.Pointer(&lpString[0])), cchSize)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646301.aspx
func GetKeyState(nVirtKey uintptr) (uintptr, error) {
	r1, _, err := procGetKeyState.Call(nVirtKey)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646302.aspx
func GetLastInputInfo(plii *LASTINPUTINFO) (uintptr, error) {
	r1, _, err := procGetLastInputInfo.Call(uintptr(unsafe.Pointer(plii)))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646303.aspx
func IsWindowEnabled(hWnd uintptr) (uintptr, error) {
	r1, _, err := procIsWindowEnabled.Call(hWnd)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646304.aspx
func Keybd_event(bVk uintptr, bScan uintptr, dwFlags uintptr, dwExtraInfo uintptr) (uintptr, error) {
	r1, _, err := procKeybd_event.Call(bVk, bScan, dwFlags, dwExtraInfo)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646305.aspx
func LoadKeyboardLayout(pwszKLID string, Flags uintptr) (uintptr, error) {
	_pwszKLID, err := stringToUintptr(pwszKLID)
	if err != nil {
		return 0, err
	}
	r1, _, err := procLoadKeyboardLayout.Call(_pwszKLID, Flags)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646306.aspx
func MapVirtualKey(uCode uintptr, uMapType uintptr) (uintptr, error) {
	r1, _, err := procMapVirtualKey.Call(uCode, uMapType)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646307.aspx
func MapVirtualKeyEx(uCode uintptr, uMapType uintptr, dwhkl *uintptr) (uintptr, error) {
	r1, _, err := procMapVirtualKeyEx.Call(uCode, uMapType, uintptr(unsafe.Pointer(dwhkl)))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646308.aspx
func OemKeyScan(wOemChar uintptr) (uintptr, error) {
	r1, _, err := procOemKeyScan.Call(wOemChar)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646309.aspx
func RegisterHotKey(hWnd uintptr, id uintptr, fsModifiers uintptr, vk uintptr) (uintptr, error) {
	r1, _, err := procRegisterHotKey.Call(hWnd, id, fsModifiers, vk)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646310.aspx
func SendInputM(nInputs uintptr, pInputs []INPUT_M, cbSize uintptr) (uintptr, error) {
	r1, _, err := procSendInput.Call(nInputs, uintptr(unsafe.Pointer(&pInputs[0])), cbSize)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646310.aspx
func SendInputK(nInputs uintptr, pInputs []INPUT_K, cbSize uintptr) (uintptr, error) {
	r1, _, err := procSendInput.Call(nInputs, uintptr(unsafe.Pointer(&pInputs[0])), cbSize)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646310.aspx
func SendInputHW(nInputs uintptr, pInputs []INPUT_HW, cbSize uintptr) (uintptr, error) {
	r1, _, err := procSendInput.Call(nInputs, uintptr(unsafe.Pointer(&pInputs[0])), cbSize)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646311.aspx
func SetActiveWindow(hWnd uintptr) (uintptr, error) {
	r1, _, err := procSetActiveWindow.Call(hWnd)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646312.aspx
func SetFocus(hWnd uintptr) (uintptr, error) {
	r1, _, err := procSetFocus.Call(hWnd)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646314.aspx
func SetKeyboardState(lpKeyState []uint8) (uintptr, error) {
	r1, _, err := procSetKeyboardState.Call(uintptr(unsafe.Pointer(&lpKeyState[0])))
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646316.aspx
func ToAscii(uVirtKey uintptr, uScanCode uintptr, lpKeyState []uint8, lpChar []uint16, uFlags uintptr) (uintptr, error) {
	var _lpKeyState uintptr
	if lpKeyState == nil {
		_lpKeyState = 0
	} else {
		_lpKeyState = uintptr(unsafe.Pointer(&lpKeyState[0]))
	}
	r1, _, err := procToAscii.Call(uVirtKey, uScanCode, _lpKeyState, uintptr(unsafe.Pointer(&lpChar[0])), uFlags)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646318.aspx
func ToAsciiEx(uVirtKey uintptr, uScanCode uintptr, lpKeyState []uint8, lpChar []uint16, uFlags uintptr, dwhkl uintptr) (uintptr, error) {
	var _lpKeyState uintptr
	if lpKeyState == nil {
		_lpKeyState = 0
	} else {
		_lpKeyState = uintptr(unsafe.Pointer(&lpKeyState[0]))
	}
	r1, _, err := procToAsciiEx.Call(uVirtKey, uScanCode, _lpKeyState, uintptr(unsafe.Pointer(&lpChar[0])), uFlags, dwhkl)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646320.aspx
func ToUnicode(wVirtKey uintptr, wScanCode uintptr, lpKeyState []uint8, pwszBuff []uint16, cchBuff uintptr, wFlags uintptr) (uintptr, error) {
	var _lpKeyState uintptr
	if lpKeyState == nil {
		_lpKeyState = 0
	} else {
		_lpKeyState = uintptr(unsafe.Pointer(&lpKeyState[0]))
	}
	r1, _, err := procToUnicode.Call(wVirtKey, wScanCode, _lpKeyState, uintptr(unsafe.Pointer(&pwszBuff[0])), cchBuff, wFlags)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646322.aspx
func ToUnicodeEx(wVirtKey uintptr, wScanCode uintptr, lpKeyState []uint8, pwszBuff []uint16, cchBuff uintptr, wFlags uintptr, dwhkl uintptr) (uintptr, error) {
	var _lpKeyState uintptr
	if lpKeyState == nil {
		_lpKeyState = 0
	} else {
		_lpKeyState = uintptr(unsafe.Pointer(&lpKeyState[0]))
	}
	r1, _, err := procToUnicodeEx.Call(wVirtKey, wScanCode, _lpKeyState, uintptr(unsafe.Pointer(&pwszBuff[0])), cchBuff, wFlags, dwhkl)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646324.aspx
func UnloadKeyboardLayout(hkl uintptr) (uintptr, error) {
	r1, _, err := procUnloadKeyboardLayout.Call(hkl)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646327.aspx
func UnregisterHotKey(hWnd uintptr, id uintptr) (uintptr, error) {
	r1, _, err := procUnregisterHotKey.Call(hWnd, id)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646329.aspx
func VkKeyScan(ch uintptr) (uintptr, error) {
	r1, _, err := procVkKeyScan.Call(ch)
	return r1, err
}

// https://msdn.microsoft.com/en-us/library/ms646332.aspx
func VkKeyScanEx(ch uintptr, dwhkl uintptr) (uintptr, error) {
	r1, _, err := procVkKeyScanEx.Call(ch, dwhkl)
	return r1, err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/gookit/color"
	"github.com/pkg/errors"
)

// TODO: Each error listed here of the form *Error() should:
// - report to Metriton the error;
// - Have a reasonable user message;
// - Have a page in our documentation that explains the error and what can be done to resolve it.

// Useful strings
const seeDocsURL = "https://www.getambassador.io/docs/latest/tutorials/getting-started/"
const seeDocs = "See " + seeDocsURL
const tryAgain = "If this appears to be a transient failure, please try running the installer again. It is safe to run the installer repeatedly on a cluster."
const noTlsSuccess = "Congratulations! You've successfully installed the Ambassador Edge Stack in your Kubernetes cluster. However, we cannot connect to your cluster from the Internet, so we could not configure TLS automatically."

// User interrupted the email request.
func (i *Installer) EmailRequestError(err error) Result {
	fmt.Println()

	return Result{
		Err: err,
	}
}

// AESInstallMessage occurs here in the sequence.

// Unable to get a kubectl path.
func (i *Installer) NoKubectlError(err error) Result {
	return Result{
		Report: "fail_no_kubectl",
		Message: "The installer depends on the 'kubectl' executable. Make sure you have the latest release downloaded in your PATH, and that you have executable permissions.",
		URL: "https://kubernetes.io/docs/tasks/tools/install-kubectl/",
		Err: err,
	}
}

// Unable to get cluster information
func (i *Installer) NoClusterError(err error) Result {
	noCluster := `
Unable to communicate with the remote Kubernetes cluster using your kubectl context.

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'
or get started and run Kubernetes.`

	return Result{
		Report: "fail_no_cluster",
		URL: "https://kubernetes.io/docs/setup/",
		Message: noCluster,
		Err: err,
	}
}

// Unable to get client configuration or namespace
func (i *Installer) GetRestConfigError(err error) Result {
	return Result{
		Report: "fail_no_cluster",
		Err: err,
	}
}

// Unable to create a new CoreV1Client for the given configuration.
func (i *Installer) NewForConfigError(err error) Result {
	return Result{
		Report: "fail_no_cluster",
		Err: err,
	}
}

// Unable to get versions via kubectl
func (i *Installer) CaptureKubectlError(err error) Result {
	return Result{
		Report: "fail_no_cluster",
		Err: err,
	}
}

// Unable to fetch the AES CRD manifests (aes-crds.yaml)
func (i *Installer) AESCRDManifestsError(err error) Result {
	i.Report("fail_no_internet", ScoutMeta{"err", err.Error()})

	return Result{
		Message: "download AES CRD manifests",
		Err: errors.Wrap(err, "download AES CRD manifests"),
	}
}

// Unable to fetch the AES manifests (aes.yaml)
func (i *Installer) AESManifestsError(err error) Result {
	i.Report("fail_no_internet", ScoutMeta{"err", err.Error()})

	return Result{
		Message:  "download AES manifests",
		Err: errors.Wrap(err, "download AES manifests"),
	}
}

// Unable to parse the downloaded AES manifests
func (i *Installer) ManifestParsingError(err error, matches []string) Result {
	i.log.Printf("matches is %+v", matches)

	return Result{
		Report: "fail_bad_manifests",
		Message: "Failed to parse downloaded manifests. Is there a proxy server interfering with HTTP downloads?",
		Err: err,
	}
}

// Existing AES CRD's of incompatible version
func (i *Installer) IncompatibleCRDVersionsError(err error, installedVersion string) Result {
	abortExisting := `
This tool does not support upgrades/downgrades at this time.

The installer will now quit to avoid corrupting an existing installation of AES.
`
	i.ShowWrapped(abortExisting)
	i.show.Println()
	i.ShowWrapped(seeDocs)
	i.Report("fail_existing_aes", ScoutMeta{"installing", i.version}, ScoutMeta{"found", installedVersion})

	return Result{
		URL: seeDocsURL,
		Message: fmt.Sprintf("existing AES %s found when installing AES %s", installedVersion, i.version),
		Err: err,
	}
}

// Existing AES CRD's, unable to upgrade.
func (i *Installer) ExistingCRDsError(err error) Result {
	abortCRDs := `You can manually remove installed CRDs if you are confident they are not in use by any installation.
Removing the CRDs will cause your existing Ambassador Mappings and other resources to be deleted as well.

$ kubectl delete crd -l product=aes

The installer will now quit to avoid corrupting an existing (but undetected) installation.
`
	return Result{
		Report:  "fail_existing_crds",
		Message: abortCRDs,
		URL:     seeDocsURL,
		Err:     err,
	}
}

// Unable to kubectl apply the aes-crd.yaml manifests
func (i *Installer) InstallCRDsError(err error) Result {
	return Result{
		Report: "fail_install_crds",
		Err: err,
	}
}

// 90-second timeout on waiting for aes-crd.yaml manifests to be established
func (i *Installer) WaitCRDsError(err error) Result {
	return Result{
		Report: "fail_wait_crds",
		Err: err,
	}
}

// Unable to kubectl apply the aes.yaml manifests
func (i *Installer) InstallAESError(err error) Result {
	return Result{
		Report: "fail_install_aes",
		Err: err,
	}
}

//90-second timeout on waiting for aes.yaml manifests to be deployed and available
func (i *Installer) WaitForAESError(err error) Result {
	return Result{
		Report: "fail_wait_aes",
		Err: err,
	}
}

// Unable to get the AES Install ID via kubectl exec to ask for the pod ID
func (i *Installer) AESPodStartupError(err error) Result {
	return Result{
		Report: "fail_pod_timeout",
		Err: err,
	}
}

// docker-desktop, minikube, or kind: local cluster so no automatic TLS.
func (i *Installer) KnownLocalClusterResult() Result {
	i.ShowWrapped(color.Bold.Sprintf(noTlsSuccess))
	i.show.Println()
	loginMsg := "Determine the IP address and port number of your Ambassador service, e.g.\n"
	loginMsg += color.Bold.Sprintf("$ minikube service -n ambassador ambassador\n\n")
	loginMsg += fmt.Sprintf("The following command will open the Edge Policy Console once you accept a self-signed certificate in your browser.\n")
	loginMsg += color.Bold.Sprintf("$ edgectl login -n ambassador IP_ADDRESS:PORT")
	i.ShowWrapped(loginMsg)
	i.show.Println()
	i.ShowWrapped(seeDocs)

	return Result{
		Report: "cluster_not_accessible",
		URL: seeDocsURL,
		Err: nil,
	}
}

// Unable to provision a load balancer (failed to retrieve the IP address)
func (i *Installer) LoadBalancerError(err error) Result {
	i.show.Println()

	failLoadBalancer := `
Timed out waiting for the load balancer's IP address for the AES Service.
- If a load balancer IP address shows up, simply run the installer again.
- If your cluster doesn't support load balancers, you'll need to expose AES some other way.
`
	i.ShowWrapped(failLoadBalancer)
	i.show.Println()
	i.ShowWrapped(color.Bold.Sprintf(noTlsSuccess))
	i.ShowWrapped(seeDocs)

	return Result{
		Report: "fail_loadbalancer_timeout",
		URL: seeDocsURL,
		Err: err,
	}
}

// AES failed to respond to the ACME challenge.  This may be because AES did not start quickly enough or
// if the AES load balancer is not reachable.
func (i *Installer) AESACMEChallengeError(err error) Result {
	i.ShowWrapped("It seems AES did not start in the expected time, or the AES load balancer is not reachable from here.")
	i.ShowWrapped(tryAgain)
	i.ShowWrapped(color.Bold.Sprintf(noTlsSuccess))
	i.ShowWrapped(seeDocs)

	return Result{
		Report: "aes_listening_timeout",
		TryAgain: true,
		URL: seeDocsURL,
		Err: err,
	}
}

// Unable to make an HTTP Post to Metriton at https://metriton.datawire.io/register-domain
// and so cannot acquire a DNS name for the cluster's load balancer.
func (i *Installer) DNSNamePostError(err error) Result {
	i.Report("dns_name_failure", ScoutMeta{"err", err.Error()})

	return Result{
		Err: errors.Wrap(err, "acquire DNS name (post)"),
	}
}

// Unable to fetch the response from the HTTP Post to Metriton.
func (i *Installer) DNSNameBodyError(err error) Result {
	i.Report("dns_name_failure", ScoutMeta{"err", err.Error()})

	return Result{
		Message: "acquire DNS name (read body)",
		Err: errors.Wrap(err, "acquire DNS name (read body)"),
	}
}

// Successful installation but no DNS.
func (i *Installer) AESInstalledNoDNSResult(statusCode int, message string) Result {
	i.Report("dns_name_failure", ScoutMeta{"code", statusCode}, ScoutMeta{"err", message})

	userMessage := `
<bold>Congratulations! You've successfully installed the Ambassador Edge Stack in your Kubernetes cluster. However, we cannot connect to your cluster from the Internet, so we could not configure TLS automatically.</>

If this IP address is reachable from here, you can access your installation without a DNS name. The following command will open the Edge Policy Console once you accept a self-signed certificate in your browser.
<bold>$ edgectl login -n ambassador {{ .address }}</>

You can use port forwarding to access your Edge Stack installation and the Edge Policy Console.  You will need to accept a self-signed certificate in your browser.
<bold>$ kubectl -n ambassador port-forward deploy/ambassador 8443 &</>
<bold>$ edgectl login -n ambassador 127.0.0.1:8443</>
`
	return Result{
		Message: userMessage,
		URL:     seeDocsURL,
		Report:  "", // FIXME: reported above due to additional metadata required
	}
}

// The DNS name propagation timed out, so unable to resolve the name.
func (i *Installer) DNSPropagationError(err error) Result {
	i.Report("dns_name_propagation_timeout")
	i.ShowWrapped("We are unable to resolve your new DNS name on this machine.")
	i.ShowWrapped(seeDocs)
	i.ShowWrapped(tryAgain)

	return Result{
		TryAgain: true,
		URL: seeDocsURL,
		Err: err,
	}
}

// In attempting to kubectl apply the hostResource yaml, kubectl failed.
func (i *Installer) HostResourceCreationError(err error) Result {
	i.Report("fail_host_resource", ScoutMeta{"err", err.Error()})
	i.ShowWrapped("We failed to create a Host resource in your cluster. This is unexpected.")
	i.ShowWrapped(seeDocs)

	return Result{
		URL: seeDocsURL,
		Err: err,
	}
}

// Unable to acquire a TLS certificate from Let's Encrypt
func (i *Installer) CertificateProvisionError(err error) Result {
	i.Report("cert_provision_failed")
	// Some info is reported by the check function.
	i.ShowWrapped(seeDocs)
	i.ShowWrapped(tryAgain)

	return Result{
		TryAgain: true,
		URL: seeDocsURL,
		Err: err,
	}
}

// Unable to acquire a TLS certificate from Let's Encrypt
func (i *Installer) HostRetrievalError(err error) Result {
	i.ShowWrapped("We failed to retrieve the Host resource from your cluster that we just created. This is unexpected.")
	i.ShowWrapped(tryAgain)

	return Result{
		TryAgain: true,
		Err: err,
	}
}

// AESInstallCompleteMessage occurs here in the sequence.

// Attempted to log in to the cluster but failed.
func (i *Installer) AESLoginError(err error) Result {
	return Result{
		Err: err,
	}
}

// AES login successful!
func (i *Installer) AESLoginSuccessResult()  Result {
	return Result{
		Err: nil,
	}
}
<commit_msg>checkpoint<commit_after>package main

import (
	"fmt"
	"github.com/gookit/color"
	"github.com/pkg/errors"
)

// TODO: Each error listed here of the form *Error() should:
// - report to Metriton the error;
// - Have a reasonable user message;
// - Have a page in our documentation that explains the error and what can be done to resolve it.

// Useful strings
const seeDocsURL = "https://www.getambassador.io/docs/latest/tutorials/getting-started/"
const seeDocs = "See " + seeDocsURL
const tryAgain = "If this appears to be a transient failure, please try running the installer again. It is safe to run the installer repeatedly on a cluster."
const noTlsSuccess = "Congratulations! You've successfully installed the Ambassador Edge Stack in your Kubernetes cluster. However, we cannot connect to your cluster from the Internet, so we could not configure TLS automatically."

// User interrupted the email request.
func (i *Installer) EmailRequestError(err error) Result {
	fmt.Println()

	return Result{
		Err: err,
	}
}

// AESInstallMessage occurs here in the sequence.

// Unable to get a kubectl path.
func (i *Installer) NoKubectlError(err error) Result {
	return Result{
		Report: "fail_no_kubectl",
		Message: "The installer depends on the 'kubectl' executable. Make sure you have the latest release downloaded in your PATH, and that you have executable permissions.",
		URL: "https://kubernetes.io/docs/tasks/tools/install-kubectl/",
		Err: err,
	}
}

// Unable to get cluster information
func (i *Installer) NoClusterError(err error) Result {
	noCluster := `
Unable to communicate with the remote Kubernetes cluster using your kubectl context.

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'
or get started and run Kubernetes.`

	return Result{
		Report: "fail_no_cluster",
		URL: "https://kubernetes.io/docs/setup/",
		Message: noCluster,
		Err: err,
	}
}

// Unable to get client configuration or namespace
func (i *Installer) GetRestConfigError(err error) Result {
	return Result{
		Report: "fail_no_cluster",
		Err: err,
	}
}

// Unable to create a new CoreV1Client for the given configuration.
func (i *Installer) NewForConfigError(err error) Result {
	return Result{
		Report: "fail_no_cluster",
		Err: err,
	}
}

// Unable to get versions via kubectl
func (i *Installer) CaptureKubectlError(err error) Result {
	return Result{
		Report: "fail_no_cluster",
		Err: err,
	}
}

// Unable to fetch the AES CRD manifests (aes-crds.yaml)
func (i *Installer) AESCRDManifestsError(err error) Result {
	i.Report("fail_no_internet", ScoutMeta{"err", err.Error()})

	return Result{
		Message: "download AES CRD manifests",
		Err: errors.Wrap(err, "download AES CRD manifests"),
	}
}

// Unable to fetch the AES manifests (aes.yaml)
func (i *Installer) AESManifestsError(err error) Result {
	i.Report("fail_no_internet", ScoutMeta{"err", err.Error()})

	return Result{
		Message:  "download AES manifests",
		Err: errors.Wrap(err, "download AES manifests"),
	}
}

// Unable to parse the downloaded AES manifests
func (i *Installer) ManifestParsingError(err error, matches []string) Result {
	i.log.Printf("matches is %+v", matches)

	return Result{
		Report: "fail_bad_manifests",
		Message: "Failed to parse downloaded manifests. Is there a proxy server interfering with HTTP downloads?",
		Err: err,
	}
}

// Existing AES CRD's of incompatible version
func (i *Installer) IncompatibleCRDVersionsError(err error, installedVersion string) Result {
	abortExisting := `
This tool does not support upgrades/downgrades at this time.

The installer will now quit to avoid corrupting an existing installation of AES.
`
	i.ShowWrapped(abortExisting)
	i.show.Println()
	i.ShowWrapped(seeDocs)
	i.Report("fail_existing_aes", ScoutMeta{"installing", i.version}, ScoutMeta{"found", installedVersion})

	return Result{
		URL: seeDocsURL,
		Message: fmt.Sprintf("existing AES %s found when installing AES %s", installedVersion, i.version),
		Err: err,
	}
}

// Existing AES CRD's, unable to upgrade.
func (i *Installer) ExistingCRDsError(err error) Result {
	abortCRDs := `You can manually remove installed CRDs if you are confident they are not in use by any installation.
Removing the CRDs will cause your existing Ambassador Mappings and other resources to be deleted as well.

$ kubectl delete crd -l product=aes

The installer will now quit to avoid corrupting an existing (but undetected) installation.
`
	return Result{
		Report:  "fail_existing_crds",
		Message: abortCRDs,
		URL:     seeDocsURL,
		Err:     err,
	}
}

// Unable to kubectl apply the aes-crd.yaml manifests
func (i *Installer) InstallCRDsError(err error) Result {
	return Result{
		Report: "fail_install_crds",
		Err: err,
	}
}

// 90-second timeout on waiting for aes-crd.yaml manifests to be established
func (i *Installer) WaitCRDsError(err error) Result {
	return Result{
		Report: "fail_wait_crds",
		Err: err,
	}
}

// Unable to kubectl apply the aes.yaml manifests
func (i *Installer) InstallAESError(err error) Result {
	return Result{
		Report: "fail_install_aes",
		Err: err,
	}
}

//90-second timeout on waiting for aes.yaml manifests to be deployed and available
func (i *Installer) WaitForAESError(err error) Result {
	return Result{
		Report: "fail_wait_aes",
		Err: err,
	}
}

// Unable to get the AES Install ID via kubectl exec to ask for the pod ID
func (i *Installer) AESPodStartupError(err error) Result {
	return Result{
		Report: "fail_pod_timeout",
		Err: err,
	}
}

// docker-desktop, minikube, or kind: local cluster so no automatic TLS.
func (i *Installer) KnownLocalClusterResult() Result {
	i.ShowWrapped(color.Bold.Sprintf(noTlsSuccess))
	i.show.Println()
	loginMsg := "Determine the IP address and port number of your Ambassador service, e.g.\n"
	loginMsg += color.Bold.Sprintf("$ minikube service -n ambassador ambassador\n\n")
	loginMsg += fmt.Sprintf("The following command will open the Edge Policy Console once you accept a self-signed certificate in your browser.\n")
	loginMsg += color.Bold.Sprintf("$ edgectl login -n ambassador IP_ADDRESS:PORT")
	i.ShowWrapped(loginMsg)
	i.show.Println()
	i.ShowWrapped(seeDocs)

	return Result{
		Report: "cluster_not_accessible",
		URL: seeDocsURL,
		Err: nil,
	}
}

// Unable to provision a load balancer (failed to retrieve the IP address)
func (i *Installer) LoadBalancerError(err error) Result {
	i.show.Println()

	failLoadBalancer := `
Timed out waiting for the load balancer's IP address for the AES Service.
- If a load balancer IP address shows up, simply run the installer again.
- If your cluster doesn't support load balancers, you'll need to expose AES some other way.
`
	i.ShowWrapped(failLoadBalancer)
	i.show.Println()
	i.ShowWrapped(color.Bold.Sprintf(noTlsSuccess))
	i.ShowWrapped(seeDocs)

	return Result{
		Report: "fail_loadbalancer_timeout",
		URL: seeDocsURL,
		Err: err,
	}
}

// AES failed to respond to the ACME challenge.  This may be because AES did not start quickly enough or
// if the AES load balancer is not reachable.
func (i *Installer) AESACMEChallengeError(err error) Result {
	i.ShowWrapped("It seems AES did not start in the expected time, or the AES load balancer is not reachable from here.")
	i.ShowWrapped(tryAgain)
	i.ShowWrapped(color.Bold.Sprintf(noTlsSuccess))
	i.ShowWrapped(seeDocs)

	return Result{
		Report: "aes_listening_timeout",
		TryAgain: true,
		URL: seeDocsURL,
		Err: err,
	}
}

// Unable to make an HTTP Post to Metriton at https://metriton.datawire.io/register-domain
// and so cannot acquire a DNS name for the cluster's load balancer.
func (i *Installer) DNSNamePostError(err error) Result {
	i.Report("dns_name_failure", ScoutMeta{"err", err.Error()})

	return Result{
		Err: errors.Wrap(err, "acquire DNS name (post)"),
	}
}

// Unable to fetch the response from the HTTP Post to Metriton.
func (i *Installer) DNSNameBodyError(err error) Result {
	i.Report("dns_name_failure", ScoutMeta{"err", err.Error()})

	return Result{
		Message: "acquire DNS name (read body)",
		Err: errors.Wrap(err, "acquire DNS name (read body)"),
	}
}

// Successful installation but no DNS.
func (i *Installer) AESInstalledNoDNSResult(statusCode int, message string) Result {
	i.Report("dns_name_failure", ScoutMeta{"code", statusCode}, ScoutMeta{"err", message})

	userMessage := `
<bold>Congratulations! You've successfully installed the Ambassador Edge Stack in your Kubernetes cluster. However, we cannot connect to your cluster from the Internet, so we could not configure TLS automatically.</>

If this IP address is reachable from here, you can access your installation without a DNS name. The following command will open the Edge Policy Console once you accept a self-signed certificate in your browser.
<bold>$ edgectl login -n ambassador {{ .address }}</>

You can use port forwarding to access your Edge Stack installation and the Edge Policy Console.  You will need to accept a self-signed certificate in your browser.
<bold>$ kubectl -n ambassador port-forward deploy/ambassador 8443 &</>
<bold>$ edgectl login -n ambassador 127.0.0.1:8443</>
`
	return Result{
		Message: userMessage,
		URL:     seeDocsURL,
		Report:  "", // FIXME: reported above due to additional metadata required
	}
}

// The DNS name propagation timed out, so unable to resolve the name.
func (i *Installer) DNSPropagationError(err error) Result {
	i.ShowWrapped("We are unable to resolve your new DNS name on this machine.")
	i.ShowWrapped(seeDocs)
	i.ShowWrapped(tryAgain)

	return Result{
		Report: "dns_name_propagation_timeout",
		TryAgain: true,
		URL: seeDocsURL,
		Err: err,
	}
}

// In attempting to kubectl apply the hostResource yaml, kubectl failed.
func (i *Installer) HostResourceCreationError(err error) Result {
	i.Report("fail_host_resource", ScoutMeta{"err", err.Error()})
	i.ShowWrapped("We failed to create a Host resource in your cluster. This is unexpected.")
	i.ShowWrapped(seeDocs)

	return Result{
		URL: seeDocsURL,
		Err: err,
	}
}

// Unable to acquire a TLS certificate from Let's Encrypt
func (i *Installer) CertificateProvisionError(err error) Result {
	// Some info is reported by the check function.
	i.ShowWrapped(seeDocs)
	i.ShowWrapped(tryAgain)

	return Result{
		Report: "cert_provision_failed",
		TryAgain: true,
		URL: seeDocsURL,
		Err: err,
	}
}

// Unable to acquire a TLS certificate from Let's Encrypt
func (i *Installer) HostRetrievalError(err error) Result {
	i.ShowWrapped("We failed to retrieve the Host resource from your cluster that we just created. This is unexpected.")
	i.ShowWrapped(tryAgain)

	return Result{
		TryAgain: true,
		Err: err,
	}
}

// AESInstallCompleteMessage occurs here in the sequence.

// Attempted to log in to the cluster but failed.
func (i *Installer) AESLoginError(err error) Result {
	return Result{
		Err: err,
	}
}

// AES login successful!
func (i *Installer) AESLoginSuccessResult()  Result {
	return Result{
		Err: nil,
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bufio"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/golang/snappy"
	flags "github.com/jessevdk/go-flags"
	"github.com/prometheus/common/expfmt"
	"github.com/prometheus/common/model"
	"github.com/sirupsen/logrus"

	dto "github.com/prometheus/client_model/go"
	"github.com/prometheus/prometheus/prompb"
)

var opts struct {
	BindAddr      string        `long:"bind-addr" description:"address to listen on" default:":8083"`
	WritePath     string        `long:"write-path" description:"url path" default:"/receive"`
	WriteTextPath string        `long:"write-text-path" description:"url path" default:"/receive_text"`
	MetricsPath   string        `long:"metrics-path" description:"url path" default:"/metrics"`
	TTL           time.Duration `long:"metric-ttl" description:"how long until we TTL things out of the map" required:"true"`
}

func main() {
	parser := flags.NewParser(&opts, flags.Default)
	if _, err := parser.Parse(); err != nil {
		// If the error was from the parser, then we can simply return
		// as Parse() prints the error already
		if _, ok := err.(*flags.Error); ok {
			os.Exit(1)
		}
		logrus.Fatalf("Error parsing flags: %v", err)
	}

	l := sync.Mutex{}
	m := make(map[string]*prompb.Sample)

	// TODO: ttl things
	go func() {
		for {
			time.Sleep(opts.TTL)
			cutoff := int64(model.Now().Add(-opts.TTL))

			l.Lock()
			for k, v := range m {
				if v.Timestamp < cutoff {
					delete(m, k)
				}
			}
			l.Unlock()
		}
	}()

	http.HandleFunc(opts.WritePath, func(w http.ResponseWriter, r *http.Request) {
		compressed, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		reqBuf, err := snappy.Decode(nil, compressed)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		var req prompb.WriteRequest
		if err := proto.Unmarshal(reqBuf, &req); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		for _, ts := range req.Timeseries {
			metric := make(model.Metric, len(ts.Labels))
			for _, l := range ts.Labels {
				metric[model.LabelName(l.Name)] = model.LabelValue(l.Value)
			}

			// pick which point
			var sample *prompb.Sample
			for _, s := range ts.Samples {
				if sample == nil {
					sample = &s
					continue
				}
				if s.Timestamp > sample.Timestamp {
					sample = &s
				}
			}

			l.Lock()
			// If it doesn't exist, or the sample is newer
			if currentSample, ok := m[metric.String()]; !ok || sample.Timestamp > currentSample.Timestamp {
				m[metric.String()] = sample
			}
			l.Unlock()
		}
	})

	http.HandleFunc(opts.WriteTextPath, func(w http.ResponseWriter, r *http.Request) {
		decoder := expfmt.NewDecoder(r.Body, expfmt.FmtText) // TODO: get content-type header instead
		for {
			var mf dto.MetricFamily
			err := decoder.Decode(&mf)
			if err == io.EOF {
				break
			}
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			vec, err := expfmt.ExtractSamples(&expfmt.DecodeOptions{Timestamp: model.Now()}, &mf)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			for _, vecSample := range vec {
				l.Lock()
				// If it doesn't exist, or the sample is newer
				if currentSample, ok := m[vecSample.Metric.String()]; !ok || int64(vecSample.Timestamp) > currentSample.Timestamp {
					m[vecSample.Metric.String()] = &prompb.Sample{
						Value:     float64(vecSample.Value),
						Timestamp: int64(vecSample.Timestamp),
					}
				}
				l.Unlock()
			}

		}
	})

	http.HandleFunc(opts.MetricsPath, func(w http.ResponseWriter, r *http.Request) {
		writer := bufio.NewWriter(w)
		l.Lock()
		for k, v := range m {
			var sb strings.Builder

			sb.WriteString(k)
			sb.WriteRune(' ')

			sb.WriteString(strconv.FormatFloat(float64(v.Value), 'f', -1, 64))

			if v.Timestamp > 0 {
				sb.WriteRune(' ')
				sb.WriteString(strconv.FormatInt(v.Timestamp, 10))
			}
			sb.WriteByte('\n')

			writer.WriteString(sb.String())
		}
		l.Unlock()
		writer.Flush()
	})

	logrus.Fatal(http.ListenAndServe(opts.BindAddr, nil))
}
<commit_msg>Correct comment<commit_after>// Copyright 2016 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bufio"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/golang/snappy"
	flags "github.com/jessevdk/go-flags"
	"github.com/prometheus/common/expfmt"
	"github.com/prometheus/common/model"
	"github.com/sirupsen/logrus"

	dto "github.com/prometheus/client_model/go"
	"github.com/prometheus/prometheus/prompb"
)

var opts struct {
	BindAddr      string        `long:"bind-addr" description:"address to listen on" default:":8083"`
	WritePath     string        `long:"write-path" description:"url path" default:"/receive"`
	WriteTextPath string        `long:"write-text-path" description:"url path" default:"/receive_text"`
	MetricsPath   string        `long:"metrics-path" description:"url path" default:"/metrics"`
	TTL           time.Duration `long:"metric-ttl" description:"how long until we TTL things out of the map" required:"true"`
}

func main() {
	parser := flags.NewParser(&opts, flags.Default)
	if _, err := parser.Parse(); err != nil {
		// If the error was from the parser, then we can simply return
		// as Parse() prints the error already
		if _, ok := err.(*flags.Error); ok {
			os.Exit(1)
		}
		logrus.Fatalf("Error parsing flags: %v", err)
	}

	l := sync.Mutex{}
	m := make(map[string]*prompb.Sample)

	// ttl things
	go func() {
		for {
			time.Sleep(opts.TTL)
			cutoff := int64(model.Now().Add(-opts.TTL))

			l.Lock()
			for k, v := range m {
				if v.Timestamp < cutoff {
					delete(m, k)
				}
			}
			l.Unlock()
		}
	}()

	http.HandleFunc(opts.WritePath, func(w http.ResponseWriter, r *http.Request) {
		compressed, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		reqBuf, err := snappy.Decode(nil, compressed)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		var req prompb.WriteRequest
		if err := proto.Unmarshal(reqBuf, &req); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		for _, ts := range req.Timeseries {
			metric := make(model.Metric, len(ts.Labels))
			for _, l := range ts.Labels {
				metric[model.LabelName(l.Name)] = model.LabelValue(l.Value)
			}

			// pick which point
			var sample *prompb.Sample
			for _, s := range ts.Samples {
				if sample == nil {
					sample = &s
					continue
				}
				if s.Timestamp > sample.Timestamp {
					sample = &s
				}
			}

			l.Lock()
			// If it doesn't exist, or the sample is newer
			if currentSample, ok := m[metric.String()]; !ok || sample.Timestamp > currentSample.Timestamp {
				m[metric.String()] = sample
			}
			l.Unlock()
		}
	})

	http.HandleFunc(opts.WriteTextPath, func(w http.ResponseWriter, r *http.Request) {
		decoder := expfmt.NewDecoder(r.Body, expfmt.FmtText) // TODO: get content-type header instead
		for {
			var mf dto.MetricFamily
			err := decoder.Decode(&mf)
			if err == io.EOF {
				break
			}
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			vec, err := expfmt.ExtractSamples(&expfmt.DecodeOptions{Timestamp: model.Now()}, &mf)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			for _, vecSample := range vec {
				l.Lock()
				// If it doesn't exist, or the sample is newer
				if currentSample, ok := m[vecSample.Metric.String()]; !ok || int64(vecSample.Timestamp) > currentSample.Timestamp {
					m[vecSample.Metric.String()] = &prompb.Sample{
						Value:     float64(vecSample.Value),
						Timestamp: int64(vecSample.Timestamp),
					}
				}
				l.Unlock()
			}

		}
	})

	http.HandleFunc(opts.MetricsPath, func(w http.ResponseWriter, r *http.Request) {
		writer := bufio.NewWriter(w)
		l.Lock()
		for k, v := range m {
			var sb strings.Builder

			sb.WriteString(k)
			sb.WriteRune(' ')

			sb.WriteString(strconv.FormatFloat(float64(v.Value), 'f', -1, 64))

			if v.Timestamp > 0 {
				sb.WriteRune(' ')
				sb.WriteString(strconv.FormatInt(v.Timestamp, 10))
			}
			sb.WriteByte('\n')

			writer.WriteString(sb.String())
		}
		l.Unlock()
		writer.Flush()
	})

	logrus.Fatal(http.ListenAndServe(opts.BindAddr, nil))
}
<|endoftext|>


<commit_before>package schema<commit_msg>gofmt docs/schema/doc.go<commit_after>package schema
<|endoftext|>


<commit_before>// Copyright 2015 Robert S. Gerus. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package bot

import (
	"log"
	"strings"

	cfg "github.com/arachnist/gorepost/config"
	"github.com/arachnist/gorepost/irc"
)

var callbacks = make(map[string]map[string]func(chan irc.Message, irc.Message))

// addCallback registers callbacks that can be later dispatched by Dispatcher
func addCallback(command, name string, callback func(chan irc.Message, irc.Message)) {
	log.Println("adding callback", command, name)
	if _, ok := callbacks[command]; !ok {
		callbacks[command] = make(map[string]func(chan irc.Message, irc.Message))
	}
	callbacks[strings.ToUpper(command)][strings.ToUpper(name)] = callback
}

func elementInSlice(s []string, e string) bool {
	for _, se := range s {
		if se == e {
			return true
		}
	}

	return false
}

// Dispatcher takes irc messages and dispatches them to registered callbacks.
//
// It will take an input message, check (based on message context), if the
// message should be dispatched, and passes it to registered callback.
func Dispatcher(output chan irc.Message, input irc.Message) {
	if input.Context["Source"] != "" {
		if elementInSlice(cfg.LookupStringSlice(input.Context, "Ignore"), input.Context["Source"]) {
			log.Println("Context:", input.Context, "Ignoring", input.Context["Source"])
			return
		}
	}
	if callbacks[input.Command] != nil {
		for i, f := range callbacks[input.Command] {
			if elementInSlice(cfg.LookupStringSlice(input.Context, "DisabledPlugins"), i) {
				log.Println("Context:", input.Context, "Plugin disabled", i)
				return
			}
			go f(output, input)
		}
	}
}
<commit_msg>Use config.LookupStringMap in bot.<commit_after>// Copyright 2015 Robert S. Gerus. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package bot

import (
	"log"
	"strings"

	cfg "github.com/arachnist/gorepost/config"
	"github.com/arachnist/gorepost/irc"
)

var callbacks = make(map[string]map[string]func(chan irc.Message, irc.Message))

// addCallback registers callbacks that can be later dispatched by Dispatcher
func addCallback(command, name string, callback func(chan irc.Message, irc.Message)) {
	log.Println("adding callback", command, name)
	if _, ok := callbacks[command]; !ok {
		callbacks[command] = make(map[string]func(chan irc.Message, irc.Message))
	}
	callbacks[strings.ToUpper(command)][strings.ToUpper(name)] = callback
}

// Dispatcher takes irc messages and dispatches them to registered callbacks.
//
// It will take an input message, check (based on message context), if the
// message should be dispatched, and passes it to registered callback.
func Dispatcher(output chan irc.Message, input irc.Message) {
	if _, ok := cfg.LookupStringMap(input.Context, "Ignore")[input.Context["Source"]]; ok {
		log.Println("Context:", input.Context, "Ignoring", input.Context["Source"])
		return
	}

	if callbacks[input.Command] != nil {
		for i, f := range callbacks[input.Command] {
			if _, ok := cfg.LookupStringMap(input.Context, "DisabledPlugins")[i]; ok {
				log.Println("Context:", input.Context, "Plugin disabled", i)
				return
			}
			go f(output, input)
		}
	}
}
<|endoftext|>


<commit_before>package install

import (
	"context"
	"flag"
	"fmt"
	"strings"

	mf "github.com/jcrossley3/manifestival"
	servingv1alpha1 "github.com/openshift-knative/knative-serving-operator/pkg/apis/serving/v1alpha1"
	"github.com/openshift-knative/knative-serving-operator/version"
	configv1 "github.com/openshift/api/config/v1"

	"github.com/operator-framework/operator-sdk/pkg/predicate"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller"
	"sigs.k8s.io/controller-runtime/pkg/handler"
	"sigs.k8s.io/controller-runtime/pkg/manager"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
	"sigs.k8s.io/controller-runtime/pkg/source"
)

var (
	filename = flag.String("filename", "deploy/resources",
		"The filename containing the YAML resources to apply")
	recursive = flag.Bool("recursive", false,
		"If filename is a directory, process all manifests recursively")
	installNs = flag.String("install-ns", "",
		"The namespace in which to create an Install resource, if none exist")
	log = logf.Log.WithName("controller_install")
)

// Add creates a new Install Controller and adds it to the Manager. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
	manifest, err := mf.NewManifest(*filename, *recursive, mgr.GetClient())
	if err != nil {
		return err
	}
	return add(mgr, newReconciler(mgr, manifest))
}

// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager, man mf.Manifest) reconcile.Reconciler {
	return &ReconcileInstall{client: mgr.GetClient(), scheme: mgr.GetScheme(), config: man}
}

// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
	// Create a new controller
	c, err := controller.New("install-controller", mgr, controller.Options{Reconciler: r})
	if err != nil {
		return err
	}

	// Watch for changes to primary resource Install
	err = c.Watch(&source.Kind{Type: &servingv1alpha1.Install{}}, &handler.EnqueueRequestForObject{}, predicate.GenerationChangedPredicate{})
	if err != nil {
		return err
	}

	// Make an attempt to create an Install CR, if necessary
	if len(*installNs) > 0 {
		c, _ := client.New(mgr.GetConfig(), client.Options{})
		go autoInstall(c, *installNs)
	}
	return nil
}

var _ reconcile.Reconciler = &ReconcileInstall{}

// ReconcileInstall reconciles a Install object
type ReconcileInstall struct {
	// This client, initialized using mgr.Client() above, is a split client
	// that reads objects from the cache and writes to the apiserver
	client client.Client
	scheme *runtime.Scheme
	config mf.Manifest
}

// Reconcile reads that state of the cluster for a Install object and makes changes based on the state read
// and what is in the Install.Spec
// Note:
// The Controller will requeue the Request to be processed again if the returned error is non-nil or
// Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
func (r *ReconcileInstall) Reconcile(request reconcile.Request) (reconcile.Result, error) {
	reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
	reqLogger.Info("Reconciling Install")

	// Fetch the Install instance
	instance := &servingv1alpha1.Install{}
	err := r.client.Get(context.TODO(), request.NamespacedName, instance)
	if err != nil {
		if errors.IsNotFound(err) {
			r.config.DeleteAll()
			return reconcile.Result{}, nil
		}
		// Error reading the object - requeue the request.
		return reconcile.Result{}, err
	}

	stages := []func(*servingv1alpha1.Install) error{
		r.install,
		r.deleteObsoleteResources,
		r.checkForMinikube,
		r.updateServiceNetwork,
		r.updateDomain,
		r.configure,
	}

	for _, stage := range stages {
		if err := stage(instance); err != nil {
			return reconcile.Result{}, err
		}
	}

	return reconcile.Result{}, nil
}

// Apply the embedded resources
func (r *ReconcileInstall) install(instance *servingv1alpha1.Install) error {
	// Transform resources as appropriate
	fns := []mf.Transformer{mf.InjectOwner(instance)}
	if len(instance.Spec.Namespace) > 0 {
		fns = append(fns, mf.InjectNamespace(instance.Spec.Namespace))
	}
	r.config.Transform(fns...)

	if instance.Status.Version == version.Version {
		// we've already successfully applied our YAML
		return nil
	}
	// Apply the resources in the YAML file
	if err := r.config.ApplyAll(); err != nil {
		return err
	}

	// Update status
	instance.Status.Resources = r.config.Resources
	instance.Status.Version = version.Version
	if err := r.client.Status().Update(context.TODO(), instance); err != nil {
		return err
	}
	return nil
}

// Set ConfigMap values from Install spec
func (r *ReconcileInstall) configure(instance *servingv1alpha1.Install) error {
	for suffix, config := range instance.Spec.Config {
		name := "config-" + suffix
		cm, err := r.config.Get(r.config.Find("v1", "ConfigMap", name))
		if err != nil {
			return err
		}
		if cm == nil {
			log.Error(fmt.Errorf("ConfigMap '%s' not found", name), "Invalid Install spec")
			continue
		}
		if err := r.updateConfigMap(cm, config); err != nil {
			return err
		}
	}
	return nil
}

// Delete obsolete istio-system resources, if any
func (r *ReconcileInstall) deleteObsoleteResources(instance *servingv1alpha1.Install) error {
	resource := &unstructured.Unstructured{}
	resource.SetNamespace("istio-system")
	resource.SetName("knative-ingressgateway")
	resource.SetAPIVersion("v1")
	resource.SetKind("Service")
	if err := r.config.Delete(resource); err != nil {
		return err
	}
	resource.SetAPIVersion("apps/v1")
	resource.SetKind("Deployment")
	if err := r.config.Delete(resource); err != nil {
		return err
	}
	resource.SetAPIVersion("autoscaling/v1")
	resource.SetKind("HorizontalPodAutoscaler")
	return r.config.Delete(resource)
}

// Configure minikube if we're soaking in it
func (r *ReconcileInstall) checkForMinikube(instance *servingv1alpha1.Install) error {
	node := &v1.Node{}
	err := r.client.Get(context.TODO(), types.NamespacedName{Name: "minikube"}, node)
	if err != nil {
		if errors.IsNotFound(err) {
			return nil // not running on minikube!
		}
		return err
	}

	cm, err := r.config.Get(r.config.Find("v1", "ConfigMap", "config-network"))
	if err != nil {
		return err
	}
	if cm == nil {
		log.Error(err, "Missing ConfigMap", "name", "config-network")
		return nil // no sense in trying if the CM is gone
	}

	log.Info("Detected minikube; checking egress")
	data := map[string]string{"istio.sidecar.includeOutboundIPRanges": "10.0.0.1/24"}
	return r.updateConfigMap(cm, data)
}

// Set some data in a configmap, only overwriting common keys
func (r *ReconcileInstall) updateConfigMap(cm *unstructured.Unstructured, data map[string]string) error {
	for k, v := range data {
		values := []interface{}{"map", cm.GetName(), k, v}
		if x, found, _ := unstructured.NestedString(cm.Object, "data", k); found {
			if v == x {
				continue
			}
			values = append(values, "previous", x)
		}
		log.Info("Setting", values...)
		unstructured.SetNestedField(cm.Object, v, "data", k)
	}
	return r.config.Apply(cm)

}

// Get Service Network from cluster resource
func (r *ReconcileInstall) getServiceNetwork() string {
	networkConfig := &configv1.Network{}
	serviceNetwork := ""
	if err := r.client.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, networkConfig); err != nil {
		if meta.IsNoMatchError(err) {
			log.V(1).Info("OpenShift Network Config is not available.")
		} else {
			log.Error(err, "Unexpected error querying Network")
		}
	} else if len(networkConfig.Spec.ServiceNetwork) > 0 {
		serviceNetwork = strings.Join(networkConfig.Spec.ServiceNetwork, ",")
		log.Info("OpenShift Network Config is available", "Service Network", serviceNetwork)
	}
	return serviceNetwork
}

func (r *ReconcileInstall) getDomain() string {
	ingressConfig := &configv1.Ingress{}
	domain := ""
	if err := r.client.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, ingressConfig); err != nil {
		if meta.IsNoMatchError(err) {
			log.V(1).Info("OpenShift Ingress Config is not available.")
		} else {
			log.Error(err, "Unexpected error querying Ingress")
		}
	} else {
		domain = ingressConfig.Spec.Domain
		log.Info("OpenShift Ingress Config is available", "Domain", domain)
	}

	return domain
}

// Set domain in the Config Map
func (r *ReconcileInstall) updateDomain(instance *servingv1alpha1.Install) error {

	// retrieve domain for configuring for ingress traffic
	domain := r.getDomain()

	// If domain is available, update config-domain config map
	if len(domain) > 0 {

		cm := &v1.ConfigMap{}
		u := r.config.Find("v1", "ConfigMap", "config-domain")
		key := types.NamespacedName{Namespace: u.GetNamespace(), Name: u.GetName()}
		if err := r.client.Get(context.TODO(), key, cm); err != nil {
			return err
		}
		cm.Data[domain] = ""
		return r.client.Update(context.TODO(), cm)
	}

	return nil
}

// Set istio.sidecar.includeOutboundIPRanges property with service network
func (r *ReconcileInstall) updateServiceNetwork(instance *servingv1alpha1.Install) error {

	// retrieve service networks for configuring egress traffic
	serviceNetwork := r.getServiceNetwork()

	// If service network is available, update config-network config map
	if len(serviceNetwork) > 0 {

		cm := &v1.ConfigMap{}
		u := r.config.Find("v1", "ConfigMap", "config-network")
		key := types.NamespacedName{Namespace: u.GetNamespace(), Name: u.GetName()}
		if err := r.client.Get(context.TODO(), key, cm); err != nil {
			return err
		}
		cm.Data["istio.sidecar.includeOutboundIPRanges"] = serviceNetwork
		return r.client.Update(context.TODO(), cm)

	}

	return nil
}

func autoInstall(c client.Client, ns string) (err error) {
	const path = "deploy/crds/serving_v1alpha1_install_cr.yaml"
	log.Info("Automatic Install requested", "namespace", ns)
	installList := &servingv1alpha1.InstallList{}
	err = c.List(context.TODO(), &client.ListOptions{Namespace: ns}, installList)
	if err != nil {
		log.Error(err, "Unable to list Installs")
		return err
	}
	if len(installList.Items) == 0 {
		if manifest, err := mf.NewManifest(path, false, c); err == nil {
			if err = manifest.Transform(mf.InjectNamespace(ns)).ApplyAll(); err != nil {
				log.Error(err, "Unable to create Install")
			}
		} else {
			log.Error(err, "Unable to create Install manifest")
		}
	} else {
		log.Info("Install found", "name", installList.Items[0].Name)
	}
	return err
}
<commit_msg>Sneak in upstream fixes required for 0.5.2 on OpenShift<commit_after>package install

import (
	"context"
	"flag"
	"fmt"
	"strings"

	mf "github.com/jcrossley3/manifestival"
	servingv1alpha1 "github.com/openshift-knative/knative-serving-operator/pkg/apis/serving/v1alpha1"
	"github.com/openshift-knative/knative-serving-operator/version"
	configv1 "github.com/openshift/api/config/v1"

	"github.com/operator-framework/operator-sdk/pkg/predicate"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller"
	"sigs.k8s.io/controller-runtime/pkg/handler"
	"sigs.k8s.io/controller-runtime/pkg/manager"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
	"sigs.k8s.io/controller-runtime/pkg/source"
)

var (
	filename = flag.String("filename", "deploy/resources",
		"The filename containing the YAML resources to apply")
	recursive = flag.Bool("recursive", false,
		"If filename is a directory, process all manifests recursively")
	installNs = flag.String("install-ns", "",
		"The namespace in which to create an Install resource, if none exist")
	log = logf.Log.WithName("controller_install")
)

// Add creates a new Install Controller and adds it to the Manager. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
	manifest, err := mf.NewManifest(*filename, *recursive, mgr.GetClient())
	if err != nil {
		return err
	}
	return add(mgr, newReconciler(mgr, manifest))
}

// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager, man mf.Manifest) reconcile.Reconciler {
	return &ReconcileInstall{client: mgr.GetClient(), scheme: mgr.GetScheme(), config: man}
}

// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
	// Create a new controller
	c, err := controller.New("install-controller", mgr, controller.Options{Reconciler: r})
	if err != nil {
		return err
	}

	// Watch for changes to primary resource Install
	err = c.Watch(&source.Kind{Type: &servingv1alpha1.Install{}}, &handler.EnqueueRequestForObject{}, predicate.GenerationChangedPredicate{})
	if err != nil {
		return err
	}

	// Make an attempt to create an Install CR, if necessary
	if len(*installNs) > 0 {
		c, _ := client.New(mgr.GetConfig(), client.Options{})
		go autoInstall(c, *installNs)
	}
	return nil
}

var _ reconcile.Reconciler = &ReconcileInstall{}

// ReconcileInstall reconciles a Install object
type ReconcileInstall struct {
	// This client, initialized using mgr.Client() above, is a split client
	// that reads objects from the cache and writes to the apiserver
	client client.Client
	scheme *runtime.Scheme
	config mf.Manifest
}

// Reconcile reads that state of the cluster for a Install object and makes changes based on the state read
// and what is in the Install.Spec
// Note:
// The Controller will requeue the Request to be processed again if the returned error is non-nil or
// Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
func (r *ReconcileInstall) Reconcile(request reconcile.Request) (reconcile.Result, error) {
	reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
	reqLogger.Info("Reconciling Install")

	// Fetch the Install instance
	instance := &servingv1alpha1.Install{}
	err := r.client.Get(context.TODO(), request.NamespacedName, instance)
	if err != nil {
		if errors.IsNotFound(err) {
			r.config.DeleteAll()
			return reconcile.Result{}, nil
		}
		// Error reading the object - requeue the request.
		return reconcile.Result{}, err
	}

	stages := []func(*servingv1alpha1.Install) error{
		r.install,
		r.deleteObsoleteResources,
		r.checkForMinikube,
		r.updateServiceNetwork,
		r.updateDomain,
		r.configure,
	}

	for _, stage := range stages {
		if err := stage(instance); err != nil {
			return reconcile.Result{}, err
		}
	}

	return reconcile.Result{}, nil
}

// Apply the embedded resources
func (r *ReconcileInstall) install(instance *servingv1alpha1.Install) error {
	// Transform resources as appropriate
	fns := []mf.Transformer{mf.InjectOwner(instance), addPolicyRules}
	if len(instance.Spec.Namespace) > 0 {
		fns = append(fns, mf.InjectNamespace(instance.Spec.Namespace))
	}
	r.config.Transform(fns...)

	if instance.Status.Version == version.Version {
		// we've already successfully applied our YAML
		return nil
	}
	// Apply the resources in the YAML file
	if err := r.config.ApplyAll(); err != nil {
		return err
	}

	// Update status
	instance.Status.Resources = r.config.Resources
	instance.Status.Version = version.Version
	if err := r.client.Status().Update(context.TODO(), instance); err != nil {
		return err
	}
	return nil
}

// Set ConfigMap values from Install spec
func (r *ReconcileInstall) configure(instance *servingv1alpha1.Install) error {
	for suffix, config := range instance.Spec.Config {
		name := "config-" + suffix
		cm, err := r.config.Get(r.config.Find("v1", "ConfigMap", name))
		if err != nil {
			return err
		}
		if cm == nil {
			log.Error(fmt.Errorf("ConfigMap '%s' not found", name), "Invalid Install spec")
			continue
		}
		if err := r.updateConfigMap(cm, config); err != nil {
			return err
		}
	}
	return nil
}

// Delete obsolete istio-system resources, if any
func (r *ReconcileInstall) deleteObsoleteResources(instance *servingv1alpha1.Install) error {
	resource := &unstructured.Unstructured{}
	resource.SetNamespace("istio-system")
	resource.SetName("knative-ingressgateway")
	resource.SetAPIVersion("v1")
	resource.SetKind("Service")
	if err := r.config.Delete(resource); err != nil {
		return err
	}
	resource.SetAPIVersion("apps/v1")
	resource.SetKind("Deployment")
	if err := r.config.Delete(resource); err != nil {
		return err
	}
	resource.SetAPIVersion("autoscaling/v1")
	resource.SetKind("HorizontalPodAutoscaler")
	return r.config.Delete(resource)
}

// Configure minikube if we're soaking in it
func (r *ReconcileInstall) checkForMinikube(instance *servingv1alpha1.Install) error {
	node := &v1.Node{}
	err := r.client.Get(context.TODO(), types.NamespacedName{Name: "minikube"}, node)
	if err != nil {
		if errors.IsNotFound(err) {
			return nil // not running on minikube!
		}
		return err
	}

	cm, err := r.config.Get(r.config.Find("v1", "ConfigMap", "config-network"))
	if err != nil {
		return err
	}
	if cm == nil {
		log.Error(err, "Missing ConfigMap", "name", "config-network")
		return nil // no sense in trying if the CM is gone
	}

	log.Info("Detected minikube; checking egress")
	data := map[string]string{"istio.sidecar.includeOutboundIPRanges": "10.0.0.1/24"}
	return r.updateConfigMap(cm, data)
}

// Set some data in a configmap, only overwriting common keys
func (r *ReconcileInstall) updateConfigMap(cm *unstructured.Unstructured, data map[string]string) error {
	for k, v := range data {
		values := []interface{}{"map", cm.GetName(), k, v}
		if x, found, _ := unstructured.NestedString(cm.Object, "data", k); found {
			if v == x {
				continue
			}
			values = append(values, "previous", x)
		}
		log.Info("Setting", values...)
		unstructured.SetNestedField(cm.Object, v, "data", k)
	}
	return r.config.Apply(cm)

}

// Get Service Network from cluster resource
func (r *ReconcileInstall) getServiceNetwork() string {
	networkConfig := &configv1.Network{}
	serviceNetwork := ""
	if err := r.client.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, networkConfig); err != nil {
		if meta.IsNoMatchError(err) {
			log.V(1).Info("OpenShift Network Config is not available.")
		} else {
			log.Error(err, "Unexpected error querying Network")
		}
	} else if len(networkConfig.Spec.ServiceNetwork) > 0 {
		serviceNetwork = strings.Join(networkConfig.Spec.ServiceNetwork, ",")
		log.Info("OpenShift Network Config is available", "Service Network", serviceNetwork)
	}
	return serviceNetwork
}

func (r *ReconcileInstall) getDomain() string {
	ingressConfig := &configv1.Ingress{}
	domain := ""
	if err := r.client.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, ingressConfig); err != nil {
		if meta.IsNoMatchError(err) {
			log.V(1).Info("OpenShift Ingress Config is not available.")
		} else {
			log.Error(err, "Unexpected error querying Ingress")
		}
	} else {
		domain = ingressConfig.Spec.Domain
		log.Info("OpenShift Ingress Config is available", "Domain", domain)
	}

	return domain
}

// Set domain in the Config Map
func (r *ReconcileInstall) updateDomain(instance *servingv1alpha1.Install) error {

	// retrieve domain for configuring for ingress traffic
	domain := r.getDomain()

	// If domain is available, update config-domain config map
	if len(domain) > 0 {

		cm := &v1.ConfigMap{}
		u := r.config.Find("v1", "ConfigMap", "config-domain")
		key := types.NamespacedName{Namespace: u.GetNamespace(), Name: u.GetName()}
		if err := r.client.Get(context.TODO(), key, cm); err != nil {
			return err
		}
		cm.Data[domain] = ""
		return r.client.Update(context.TODO(), cm)
	}

	return nil
}

// Set istio.sidecar.includeOutboundIPRanges property with service network
func (r *ReconcileInstall) updateServiceNetwork(instance *servingv1alpha1.Install) error {

	// retrieve service networks for configuring egress traffic
	serviceNetwork := r.getServiceNetwork()

	// If service network is available, update config-network config map
	if len(serviceNetwork) > 0 {

		cm := &v1.ConfigMap{}
		u := r.config.Find("v1", "ConfigMap", "config-network")
		key := types.NamespacedName{Namespace: u.GetNamespace(), Name: u.GetName()}
		if err := r.client.Get(context.TODO(), key, cm); err != nil {
			return err
		}
		cm.Data["istio.sidecar.includeOutboundIPRanges"] = serviceNetwork
		return r.client.Update(context.TODO(), cm)

	}

	return nil
}

func autoInstall(c client.Client, ns string) (err error) {
	const path = "deploy/crds/serving_v1alpha1_install_cr.yaml"
	log.Info("Automatic Install requested", "namespace", ns)
	installList := &servingv1alpha1.InstallList{}
	err = c.List(context.TODO(), &client.ListOptions{Namespace: ns}, installList)
	if err != nil {
		log.Error(err, "Unable to list Installs")
		return err
	}
	if len(installList.Items) == 0 {
		if manifest, err := mf.NewManifest(path, false, c); err == nil {
			if err = manifest.Transform(mf.InjectNamespace(ns)).ApplyAll(); err != nil {
				log.Error(err, "Unable to create Install")
			}
		} else {
			log.Error(err, "Unable to create Install manifest")
		}
	} else {
		log.Info("Install found", "name", installList.Items[0].Name)
	}
	return err
}

// TODO: These are addressed in master and shouldn't be required for 0.6.0
func addPolicyRules(u *unstructured.Unstructured) *unstructured.Unstructured {
	if u.GetKind() == "ClusterRole" && u.GetName() == "knative-serving-core" {
		field, _, _ := unstructured.NestedFieldNoCopy(u.Object, "rules")
		var rules []interface{} = field.([]interface{})
		for _, rule := range rules {
			m := rule.(map[string]interface{})
			for _, group := range m["apiGroups"].([]interface{}) {
				if group == "apps" {
					m["resources"] = append(m["resources"].([]interface{}), "deployments/finalizers")
				}
			}
		}
		// Required to open privileged ports in OpenShift
		unstructured.SetNestedField(u.Object, append(rules, map[string]interface{}{
			"apiGroups":     []interface{}{"security.openshift.io"},
			"verbs":         []interface{}{"use"},
			"resources":     []interface{}{"securitycontextconstraints"},
			"resourceNames": []interface{}{"privileged", "anyuid"},
		}), "rules")
	}
	return u
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package remotecommand

import (
	"fmt"
	"io"
	"net/http"
	"net/url"

	"github.com/golang/glog"

	"k8s.io/apimachinery/pkg/util/httpstream"
	"k8s.io/apimachinery/pkg/util/remotecommand"
	restclient "k8s.io/client-go/rest"
	spdy "k8s.io/client-go/transport/spdy"
)

// StreamOptions holds information pertaining to the current streaming session: supported stream
// protocols, input/output streams, if the client is requesting a TTY, and a terminal size queue to
// support terminal resizing.
type StreamOptions struct {
	Stdin             io.Reader
	Stdout            io.Writer
	Stderr            io.Writer
	Tty               bool
	TerminalSizeQueue TerminalSizeQueue
}

// Executor is an interface for transporting shell-style streams.
type Executor interface {
	// Stream initiates the transport of the standard shell streams. It will transport any
	// non-nil stream to a remote system, and return an error if a problem occurs. If tty
	// is set, the stderr stream is not used (raw TTY manages stdout and stderr over the
	// stdout stream).
	Stream(options StreamOptions) error
}

type streamCreator interface {
	CreateStream(headers http.Header) (httpstream.Stream, error)
}

type streamProtocolHandler interface {
	stream(conn streamCreator) error
}

// streamExecutor handles transporting standard shell streams over an httpstream connection.
type streamExecutor struct {
	upgrader  spdy.Upgrader
	transport http.RoundTripper

	method    string
	url       *url.URL
	protocols []string
}

// NewSPDYExecutor connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams.
func NewSPDYExecutor(config *restclient.Config, method string, url *url.URL) (Executor, error) {
	wrapper, upgradeRoundTripper, err := spdy.RoundTripperFor(config)
	if err != nil {
		return nil, err
	}
	return NewSPDYExecutorForTransports(wrapper, upgradeRoundTripper, method, url)
}

// NewSPDYExecutorForTransports connects to the provided server using the given transport,
// upgrades the response using the given upgrader to multiplexed bidirectional streams.
func NewSPDYExecutorForTransports(transport http.RoundTripper, upgrader spdy.Upgrader, method string, url *url.URL) (Executor, error) {
	return NewSPDYExecutorForProtocols(
		transport, upgrader, method, url,
		remotecommand.StreamProtocolV4Name,
		remotecommand.StreamProtocolV3Name,
		remotecommand.StreamProtocolV2Name,
		remotecommand.StreamProtocolV1Name,
	)
}

// NewSPDYExecutorForProtocols connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams using only the provided protocols. Exposed for testing, most
// callers should use NewSPDYExecutor or NewSPDYExecutorForTransports.
func NewSPDYExecutorForProtocols(transport http.RoundTripper, upgrader spdy.Upgrader, method string, url *url.URL, protocols ...string) (Executor, error) {
	return &streamExecutor{
		upgrader:  upgrader,
		transport: transport,
		method:    method,
		url:       url,
		protocols: protocols,
	}, nil
}

// Stream opens a protocol streamer to the server and streams until a client closes
// the connection or the server disconnects.
func (e *streamExecutor) Stream(options StreamOptions) error {
	req, err := http.NewRequest(e.method, e.url.String(), nil)
	if err != nil {
		return fmt.Errorf("error creating request: %v", err)
	}

	conn, protocol, err := spdy.Negotiate(
		e.upgrader,
		&http.Client{Transport: e.transport},
		req,
		e.protocols...,
	)
	if err != nil {
		return err
	}
	defer conn.Close()

	var streamer streamProtocolHandler

	switch protocol {
	case remotecommand.StreamProtocolV4Name:
		streamer = newStreamProtocolV4(options)
	case remotecommand.StreamProtocolV3Name:
		streamer = newStreamProtocolV3(options)
	case remotecommand.StreamProtocolV2Name:
		streamer = newStreamProtocolV2(options)
	case "":
		glog.V(4).Infof("The server did not negotiate a streaming protocol version. Falling back to %s", remotecommand.StreamProtocolV1Name)
		fallthrough
	case remotecommand.StreamProtocolV1Name:
		streamer = newStreamProtocolV1(options)
	}

	return streamer.stream(conn)
}
<commit_msg>client-go: update documentation for remotecommand.StreamOptions<commit_after>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package remotecommand

import (
	"fmt"
	"io"
	"net/http"
	"net/url"

	"github.com/golang/glog"

	"k8s.io/apimachinery/pkg/util/httpstream"
	"k8s.io/apimachinery/pkg/util/remotecommand"
	restclient "k8s.io/client-go/rest"
	spdy "k8s.io/client-go/transport/spdy"
)

// StreamOptions holds information pertaining to the current streaming session:
// input/output streams, if the client is requesting a TTY, and a terminal size queue to
// support terminal resizing.
type StreamOptions struct {
	Stdin             io.Reader
	Stdout            io.Writer
	Stderr            io.Writer
	Tty               bool
	TerminalSizeQueue TerminalSizeQueue
}

// Executor is an interface for transporting shell-style streams.
type Executor interface {
	// Stream initiates the transport of the standard shell streams. It will transport any
	// non-nil stream to a remote system, and return an error if a problem occurs. If tty
	// is set, the stderr stream is not used (raw TTY manages stdout and stderr over the
	// stdout stream).
	Stream(options StreamOptions) error
}

type streamCreator interface {
	CreateStream(headers http.Header) (httpstream.Stream, error)
}

type streamProtocolHandler interface {
	stream(conn streamCreator) error
}

// streamExecutor handles transporting standard shell streams over an httpstream connection.
type streamExecutor struct {
	upgrader  spdy.Upgrader
	transport http.RoundTripper

	method    string
	url       *url.URL
	protocols []string
}

// NewSPDYExecutor connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams.
func NewSPDYExecutor(config *restclient.Config, method string, url *url.URL) (Executor, error) {
	wrapper, upgradeRoundTripper, err := spdy.RoundTripperFor(config)
	if err != nil {
		return nil, err
	}
	return NewSPDYExecutorForTransports(wrapper, upgradeRoundTripper, method, url)
}

// NewSPDYExecutorForTransports connects to the provided server using the given transport,
// upgrades the response using the given upgrader to multiplexed bidirectional streams.
func NewSPDYExecutorForTransports(transport http.RoundTripper, upgrader spdy.Upgrader, method string, url *url.URL) (Executor, error) {
	return NewSPDYExecutorForProtocols(
		transport, upgrader, method, url,
		remotecommand.StreamProtocolV4Name,
		remotecommand.StreamProtocolV3Name,
		remotecommand.StreamProtocolV2Name,
		remotecommand.StreamProtocolV1Name,
	)
}

// NewSPDYExecutorForProtocols connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams using only the provided protocols. Exposed for testing, most
// callers should use NewSPDYExecutor or NewSPDYExecutorForTransports.
func NewSPDYExecutorForProtocols(transport http.RoundTripper, upgrader spdy.Upgrader, method string, url *url.URL, protocols ...string) (Executor, error) {
	return &streamExecutor{
		upgrader:  upgrader,
		transport: transport,
		method:    method,
		url:       url,
		protocols: protocols,
	}, nil
}

// Stream opens a protocol streamer to the server and streams until a client closes
// the connection or the server disconnects.
func (e *streamExecutor) Stream(options StreamOptions) error {
	req, err := http.NewRequest(e.method, e.url.String(), nil)
	if err != nil {
		return fmt.Errorf("error creating request: %v", err)
	}

	conn, protocol, err := spdy.Negotiate(
		e.upgrader,
		&http.Client{Transport: e.transport},
		req,
		e.protocols...,
	)
	if err != nil {
		return err
	}
	defer conn.Close()

	var streamer streamProtocolHandler

	switch protocol {
	case remotecommand.StreamProtocolV4Name:
		streamer = newStreamProtocolV4(options)
	case remotecommand.StreamProtocolV3Name:
		streamer = newStreamProtocolV3(options)
	case remotecommand.StreamProtocolV2Name:
		streamer = newStreamProtocolV2(options)
	case "":
		glog.V(4).Infof("The server did not negotiate a streaming protocol version. Falling back to %s", remotecommand.StreamProtocolV1Name)
		fallthrough
	case remotecommand.StreamProtocolV1Name:
		streamer = newStreamProtocolV1(options)
	}

	return streamer.stream(conn)
}
<|endoftext|>


<commit_before>package appendstorage

import (
	"bytes"
	"encoding/gob"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strconv"

	"github.com/minio-io/minio/pkgs/checksum/crc32c"
	"github.com/minio-io/minio/pkgs/storage"
)

type appendStorage struct {
	RootDir     string
	file        *os.File
	objects     map[string]Header
	objectsFile string
}

type Header struct {
	Path   string
	Offset int64
	Length int
	Crc    uint32
}

func NewStorage(rootDir string, slice int) (storage.ObjectStorage, error) {
	rootPath := path.Join(rootDir, strconv.Itoa(slice))
	// TODO verify and fix partial writes
	file, err := os.OpenFile(rootPath, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0600)
	if err != nil {
		return &appendStorage{}, err
	}
	objectsFile := path.Join(rootDir, strconv.Itoa(slice)+".map")
	objects := make(map[string]Header)
	if _, err := os.Stat(objectsFile); err == nil {
		mapFile, err := os.Open(objectsFile)
		defer mapFile.Close()
		if err != nil {
			return &appendStorage{}, nil
		}
		dec := gob.NewDecoder(mapFile)
		err = dec.Decode(&objects)
		if err != nil && err != io.EOF {
			return &appendStorage{}, nil
		}
	}
	if err != nil {
		return &appendStorage{}, err
	}
	return &appendStorage{
		RootDir:     rootDir,
		file:        file,
		objects:     objects,
		objectsFile: objectsFile,
	}, nil
}

func (storage *appendStorage) Get(objectPath string) (io.Reader, error) {
	header, ok := storage.objects[objectPath]
	if ok == false {
		return nil, nil
	}

	offset := header.Offset
	length := header.Length

	object := make([]byte, length)
	_, err := storage.file.ReadAt(object, offset)
	if err != nil {
		return nil, err
	}
	return bytes.NewBuffer(object), nil
}

func (aStorage *appendStorage) Put(objectPath string, object io.Reader) error {
	header := Header{
		Path:   objectPath,
		Offset: 0,
		Length: 0,
		Crc:    0,
	}
	offset, err := aStorage.file.Seek(0, os.SEEK_END)
	if err != nil {
		return err
	}
	objectBytes, err := ioutil.ReadAll(object)
	if err != nil {
		return err
	}
	if _, err := aStorage.file.Write(objectBytes); err != nil {
		return err
	}
	header.Offset = offset
	header.Length = len(objectBytes)
	header.Crc, err = crc32c.Crc32c(objectBytes)
	if err != nil {
		return err
	}
	aStorage.objects[objectPath] = header
	var mapBuffer bytes.Buffer
	encoder := gob.NewEncoder(&mapBuffer)
	encoder.Encode(aStorage.objects)
	ioutil.WriteFile(aStorage.objectsFile, mapBuffer.Bytes(), 0600)
	return nil
}

func (aStorage *appendStorage) List(listPath string) ([]storage.ObjectDescription, error) {
	return nil, errors.New("Not Implemented")
}
<commit_msg>Add crc checks for Object Get()<commit_after>package appendstorage

import (
	"bytes"
	"encoding/gob"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strconv"

	"github.com/minio-io/minio/pkgs/checksum/crc32c"
	"github.com/minio-io/minio/pkgs/storage"
)

type appendStorage struct {
	RootDir     string
	file        *os.File
	objects     map[string]Header
	objectsFile string
}

type Header struct {
	Path   string
	Offset int64
	Length int
	Crc    uint32
}

func NewStorage(rootDir string, slice int) (storage.ObjectStorage, error) {
	rootPath := path.Join(rootDir, strconv.Itoa(slice))
	// TODO verify and fix partial writes
	file, err := os.OpenFile(rootPath, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0600)
	if err != nil {
		return &appendStorage{}, err
	}
	objectsFile := path.Join(rootDir, strconv.Itoa(slice)+".map")
	objects := make(map[string]Header)
	if _, err := os.Stat(objectsFile); err == nil {
		mapFile, err := os.Open(objectsFile)
		defer mapFile.Close()
		if err != nil {
			return &appendStorage{}, nil
		}
		dec := gob.NewDecoder(mapFile)
		err = dec.Decode(&objects)
		if err != nil && err != io.EOF {
			return &appendStorage{}, nil
		}
	}
	if err != nil {
		return &appendStorage{}, err
	}
	return &appendStorage{
		RootDir:     rootDir,
		file:        file,
		objects:     objects,
		objectsFile: objectsFile,
	}, nil
}

func (storage *appendStorage) Get(objectPath string) (io.Reader, error) {
	header, ok := storage.objects[objectPath]
	if ok == false {
		return nil, nil
	}

	offset := header.Offset
	length := header.Length
	crc := header.Crc

	object := make([]byte, length)
	_, err := storage.file.ReadAt(object, offset)
	if err != nil {
		return nil, err
	}
	newcrc, err := crc32c.Crc32c(object)
	if err != nil {
		return nil, err
	}
	if newcrc != crc {
		return nil, err
	}
	return bytes.NewBuffer(object), nil
}

func (aStorage *appendStorage) Put(objectPath string, object io.Reader) error {
	header := Header{
		Path:   objectPath,
		Offset: 0,
		Length: 0,
		Crc:    0,
	}
	offset, err := aStorage.file.Seek(0, os.SEEK_END)
	if err != nil {
		return err
	}
	objectBytes, err := ioutil.ReadAll(object)
	if err != nil {
		return err
	}
	if _, err := aStorage.file.Write(objectBytes); err != nil {
		return err
	}
	header.Offset = offset
	header.Length = len(objectBytes)
	header.Crc, err = crc32c.Crc32c(objectBytes)
	if err != nil {
		return err
	}
	aStorage.objects[objectPath] = header
	var mapBuffer bytes.Buffer
	encoder := gob.NewEncoder(&mapBuffer)
	encoder.Encode(aStorage.objects)
	ioutil.WriteFile(aStorage.objectsFile, mapBuffer.Bytes(), 0600)
	return nil
}

func (aStorage *appendStorage) List(listPath string) ([]storage.ObjectDescription, error) {
	return nil, errors.New("Not Implemented")
}
<|endoftext|>