Related
I'm totally confused figuring out how I can mock a function, without using any additional packages like golang/mock. I'm just trying to learn how to do so but can't find many decent online resources.
Essentially, I followed this excellent article that explains how to use an interface to mock things.
As so, I've re-written the function I wanted to test. The function just inserts some data into datastore. My tests for that are ok - I can mock the function directly.
The issue I'm having is mocking it 'within' an http route I'm trying to test. Am using the Gin framework.
My router (simplified) looks like this:
func SetupRouter() *gin.Engine {
r := gin.Default()
r.Use(gin.Logger())
r.Use(gin.Recovery())
v1 := r.Group("v1")
v1.PATCH("operations/:id", controllers.UpdateOperation)
}
Which calls the UpdateOperation function:
func UpdateOperation(c *gin.Context) {
id := c.Param("id")
r := m.Response{}
str := m.OperationInfoer{}
err := m.FindAndCompleteOperation(str, id, r.Report)
if err == nil {
c.JSON(200, gin.H{
"message": "Operation completed",
})
}
}
So, I need to mock the FindAndCompleteOperation() function.
The main (simplified) functions looks like this:
func (oi OperationInfoer) FindAndCompleteOp(id string, report Report) error {
ctx := context.Background()
q := datastore.NewQuery("Operation").
Filter("Unique_Id =", id).
Limit(1)
var ops []Operation
if ts, err := db.Datastore.GetAll(ctx, q, &ops); err == nil {
{
if len(ops) > 0 {
ops[0].Id = ts[0].ID()
ops[0].Complete = true
// Do stuff
_, err := db.Datastore.Put(ctx, key, &o)
if err == nil {
log.Print("OPERATION COMPLETED")
}
}
}
}
err := errors.New("Not found")
return err
}
func FindAndCompleteOperation(ri OperationInfoer, id string, report Report) error {
return ri.FindAndCompleteOp(id, report)
}
type OperationInfoer struct{}
To test the route that updates the operation, I have something like so:
FIt("Return 200, updates operation", func() {
testRouter := SetupRouter()
param := make(url.Values)
param["access_token"] = []string{public_token}
report := m.Report{}
report.Success = true
report.Output = "my output"
jsonStr, _ := json.Marshal(report)
req, _ := http.NewRequest("PATCH", "/v1/operations/123?"+param.Encode(), bytes.NewBuffer(jsonStr))
resp := httptest.NewRecorder()
testRouter.ServeHTTP(resp, req)
Expect(resp.Code).To(Equal(200))
o := FakeResponse{}
json.NewDecoder(resp.Body).Decode(&o)
Expect(o.Message).To(Equal("Operation completed"))
})
Originally, I tried to cheat a bit and just tried something like this:
m.FindAndCompleteOperation = func(string, m.Report) error {
return nil
}
But that affects all the other tests etc.
I'm hoping someone can explain simply what the best way to mock the FindAndCompleteOperation function so I can test the routes, without relying on datastore etc.
I have another relevant, more informative answer to a similar question here, but here's an answer for your specific scenario:
Update your SetupRouter() function to take a function that can either be the real FindAndCompleteOperation function or a stub function:
Playground
package main
import "github.com/gin-gonic/gin"
// m.Response.Report
type Report struct {
// ...
}
// m.OperationInfoer
type OperationInfoer struct {
// ...
}
type findAndComplete func(s OperationInfoer, id string, report Report) error
func FindAndCompleteOperation(OperationInfoer, string, Report) error {
// ...
return nil
}
func SetupRouter(f findAndComplete) *gin.Engine {
r := gin.Default()
r.Group("v1").PATCH("/:id", func(c *gin.Context) {
if f(OperationInfoer{}, c.Param("id"), Report{}) == nil {
c.JSON(200, gin.H{"message": "Operation completed"})
}
})
return r
}
func main() {
r := SetupRouter(FindAndCompleteOperation)
if err := r.Run(":8080"); err != nil {
panic(err)
}
}
Test/mocking example
package main
import (
"encoding/json"
"net/http/httptest"
"strings"
"testing"
)
func TestUpdateRoute(t *testing.T) {
// build findAndComplete stub
var callCount int
var lastInfoer OperationInfoer
var lastID string
var lastReport Report
stub := func(s OperationInfoer, id string, report Report) error {
callCount++
lastInfoer = s
lastID = id
lastReport = report
return nil // or `fmt.Errorf("Err msg")` if you want to test fault path
}
// invoke endpoint
w := httptest.NewRecorder()
r := httptest.NewRequest(
"PATCH",
"/v1/id_value",
strings.NewReader(""),
)
SetupRouter(stub).ServeHTTP(w, r)
// check that the stub was invoked correctly
if callCount != 1 {
t.Fatal("Wanted 1 call; got", callCount)
}
if lastInfoer != (OperationInfoer{}) {
t.Fatalf("Wanted %v; got %v", OperationInfoer{}, lastInfoer)
}
if lastID != "id_value" {
t.Fatalf("Wanted 'id_value'; got '%s'", lastID)
}
if lastReport != (Report{}) {
t.Fatalf("Wanted %v; got %v", Report{}, lastReport)
}
// check that the correct response was returned
if w.Code != 200 {
t.Fatal("Wanted HTTP 200; got HTTP", w.Code)
}
var body map[string]string
if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil {
t.Fatal("Unexpected error:", err)
}
if body["message"] != "Operation completed" {
t.Fatal("Wanted 'Operation completed'; got %s", body["message"])
}
}
You can't mock if you use globals that can't be mocked in an handler. Either your globals are mockable (i.e. declared as variables of interface type) or you need to use dependency injection.
func (oi OperationInfoer) FindAndCompleteOp(id string, report Report) error {...}
looks like a method of a struct, so you should be able to inject that struct into an handler, at the very least.
type OperationInfoer interface {
FindAndCompleteOp(id string, report Report) error
}
type ConcreteOperationInfoer struct { /* actual implementation */ }
func UpdateOperation(oi OperationInfoer) func(c *gin.Context) {
return func (c *gin.Context){
// the code
}
}
then mocking becomes a breeze in your tests :
UpdateOperation(mockOperationInfoer)(ginContext)
You can use a struct instead of closures
type UpdateOperationHandler struct {
Oi OperationInfoer
}
func (h UpdateOperationHandler ) UpdateOperation (c *gin.Context) {
h.Oi.FindAndCompleteOp(/* code */ )
}
I have the following snippet which recovers from index out of range panics
Playground, also pasted below
The error is nil when called from main but not nil in an equivalent test case. What's the difference ?
type Foo struct {
Is []int
}
func main() {
fp := &Foo{}
if err := fp.Panic(); err != nil {
fmt.Errorf("Error: %v", err)
}
fmt.Println("ok")
}
func (fp *Foo) Panic() (err error) {
defer PanicRecovery(&err)
fp.Is[0] = 5
return nil
}
func PanicRecovery(err *error) {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
//fmt.Println("Panicing")
*err = r.(error) //panic(r)
} else {
*err = r.(error)
}
}
}
Test case:
func TestPanic(t *testing.T) {
fp := &Foo{}
if err := fp.Panic(); err != nil {
t.Errorf("Panic: %v", err)
}
}
Change the nested line of your main function from:
fmt.Errorf("Error: %v", err)
To:
fmt.Printf("Error: %v", err)
Notice that the "Errorf" function doesn't print anything to stdout. It creates an error by formatting the text and arguments you provide and simply returns that error. What you really want is "fmt.Printf".
In a golang program I'm reading the Os.Stdin input from a bufio.Reader.
After enter is pressed, the program reads the input and it is then printed onto the console. Is it possible to not print the input onto the console? After reading it, I process the input and reprint it (and no longer need the original input).
I read the data like this:
inputReader := bufio.NewReader(os.Stdin)
for {
outgoing, _ := inputReader.ReadString('\n')
outs <- outgoing
}
I cannot think to other methods than to use ANSI escape codes to clear the terminal and move the cursor to a specific location (in your case to column 1:row 1).
var screen *bytes.Buffer = new(bytes.Buffer)
var output *bufio.Writer = bufio.NewWriter(os.Stdout)
And here are some basic helper methods to ease your job working with terminal.
// Move cursor to given position
func moveCursor(x int, y int) {
fmt.Fprintf(screen, "\033[%d;%dH", x, y)
}
// Clear the terminal
func clearTerminal() {
output.WriteString("\033[2J")
}
Then inside your function you need to clear the terminal and move the cursor to the first column and first row of the terminal window. At the end you have to output the computed result.
for {
outgoing, err := input.ReadString('\n')
if err != nil {
break
}
if _, err := fmt.Sscanf(outgoing, "%f", input); err != nil {
fmt.Println("Input error!")
continue
}
// Clear console
clearTerminal()
moveCursor(1,1)
fmt.Println(outs) // prints the computed result
}
It seems you are looking for a terminal-specific function to disable echo. This is usually used when writing passwords on the terminal (you can type but you don't see the characters).
I suggest you give a try to terminal.ReadPassword it should work nicely and probably in the most cross-platform compatible way.
prompt := ""
t := terminal.NewTerminal(os.Stdin, prompt)
for {
outgoing, err := t.ReadPassword(prompt)
if err != nil {
log.Fatalln(err)
}
outs <- outgoing
}
other than crypto/ssh/terminal;
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
)
func raw(start bool) error {
r := "raw"
if !start {
r = "-raw"
}
rawMode := exec.Command("stty", r)
rawMode.Stdin = os.Stdin
err := rawMode.Run()
if err != nil {
return err
}
return rawMode.Wait()
}
// http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html
func main() {
var rs []rune
raw(true)
for {
inp := bufio.NewReader(os.Stdin)
r, _, err := inp.ReadRune()
if err != nil {
raw(false)
panic(err)
}
if r == '\x03' { // ctrl+c
break
} else if r == '\r' { // enter
fmt.Print(string(rs), "\n\r")
rs = []rune{}
continue
} else if r == '\u007f' { // backspace
fmt.Printf("\033[1D\033[K")
continue
}
rs = append(rs, r)
}
raw(false)
}
I would like to run WMI queries from Go. There are ways to call DLL functions from Go. My understanding is that there must be some DLL somewhere which, with the correct call, will return some data I can parse and use. I'd prefer to avoid calling into C or C++, especially since I would guess those are wrappers over the Windows API itself.
I've examined the output of dumpbin.exe /exports c:\windows\system32\wmi.dll, and the following entry looks promising:
WmiQueryAllDataA (forwarded to wmiclnt.WmiQueryAllDataA)
However I'm not sure what to do from here. What arguments does this function take? What does it return? Searching for WmiQueryAllDataA is not helpful. And that name only appears in a comment of c:\program files (x86)\windows kits\8.1\include\shared\wmistr.h, but with no function signature.
Are there better methods? Is there another DLL? Am I missing something? Should I just use a C wrapper?
Running a WMI query in Linqpad with .NET Reflector shows the use of WmiNetUtilsHelper:ExecQueryWmi (and a _f version), but neither have a viewable implementation.
Update: use the github.com/StackExchange/wmi package which uses the solution in the accepted answer.
Welcome to the wonderful world of COM, Object Oriented Programming in C from when C++ was "a young upstart".
On github mattn has thrown together a little wrapper in Go, which I used to throw together a quick example program. "This repository was created for experimentation and should be considered unstable." instills all sorts of confidence.
I'm leaving out a lot of error checking. Trust me when I say, you'll want to add it back.
package main
import (
"github.com/mattn/go-ole"
"github.com/mattn/go-ole/oleutil"
)
func main() {
// init COM, oh yeah
ole.CoInitialize(0)
defer ole.CoUninitialize()
unknown, _ := oleutil.CreateObject("WbemScripting.SWbemLocator")
defer unknown.Release()
wmi, _ := unknown.QueryInterface(ole.IID_IDispatch)
defer wmi.Release()
// service is a SWbemServices
serviceRaw, _ := oleutil.CallMethod(wmi, "ConnectServer")
service := serviceRaw.ToIDispatch()
defer service.Release()
// result is a SWBemObjectSet
resultRaw, _ := oleutil.CallMethod(service, "ExecQuery", "SELECT * FROM Win32_Process")
result := resultRaw.ToIDispatch()
defer result.Release()
countVar, _ := oleutil.GetProperty(result, "Count")
count := int(countVar.Val)
for i :=0; i < count; i++ {
// item is a SWbemObject, but really a Win32_Process
itemRaw, _ := oleutil.CallMethod(result, "ItemIndex", i)
item := itemRaw.ToIDispatch()
defer item.Release()
asString, _ := oleutil.GetProperty(item, "Name")
println(asString.ToString())
}
}
The real meat is the call to ExecQuery, I happen to grab Win32_Process from the available classes because it's easy to understand and print.
On my machine, this prints:
System Idle Process
System
smss.exe
csrss.exe
wininit.exe
services.exe
lsass.exe
svchost.exe
svchost.exe
atiesrxx.exe
svchost.exe
svchost.exe
svchost.exe
svchost.exe
svchost.exe
spoolsv.exe
svchost.exe
AppleOSSMgr.exe
AppleTimeSrv.exe
... and so on
go.exe
main.exe
I'm not running it elevated or with UAC disabled, but some WMI providers are gonna require a privileged user.
I'm also not 100% that this won't leak a little, you'll want to dig into that. COM objects are reference counted, so defer should be a pretty good fit there (provided the method isn't crazy long running) but go-ole may have some magic inside I didn't notice.
I'm commenting over a year later, but there is a solution here on github (and posted below for posterity).
// +build windows
/*
Package wmi provides a WQL interface for WMI on Windows.
Example code to print names of running processes:
type Win32_Process struct {
Name string
}
func main() {
var dst []Win32_Process
q := wmi.CreateQuery(&dst, "")
err := wmi.Query(q, &dst)
if err != nil {
log.Fatal(err)
}
for i, v := range dst {
println(i, v.Name)
}
}
*/
package wmi
import (
"bytes"
"errors"
"fmt"
"log"
"os"
"reflect"
"runtime"
"strconv"
"strings"
"sync"
"time"
"github.com/mattn/go-ole"
"github.com/mattn/go-ole/oleutil"
)
var l = log.New(os.Stdout, "", log.LstdFlags)
var (
ErrInvalidEntityType = errors.New("wmi: invalid entity type")
lock sync.Mutex
)
// QueryNamespace invokes Query with the given namespace on the local machine.
func QueryNamespace(query string, dst interface{}, namespace string) error {
return Query(query, dst, nil, namespace)
}
// Query runs the WQL query and appends the values to dst.
//
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
// the query must have the same name in dst. Supported types are all signed and
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
// Array types are not supported.
//
// By default, the local machine and default namespace are used. These can be
// changed using connectServerArgs. See
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
func Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
dv := reflect.ValueOf(dst)
if dv.Kind() != reflect.Ptr || dv.IsNil() {
return ErrInvalidEntityType
}
dv = dv.Elem()
mat, elemType := checkMultiArg(dv)
if mat == multiArgTypeInvalid {
return ErrInvalidEntityType
}
lock.Lock()
defer lock.Unlock()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
if err != nil {
oleerr := err.(*ole.OleError)
// S_FALSE = 0x00000001 // CoInitializeEx was already called on this thread
if oleerr.Code() != ole.S_OK && oleerr.Code() != 0x00000001 {
return err
}
} else {
// Only invoke CoUninitialize if the thread was not initizlied before.
// This will allow other go packages based on go-ole play along
// with this library.
defer ole.CoUninitialize()
}
unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
if err != nil {
return err
}
defer unknown.Release()
wmi, err := unknown.QueryInterface(ole.IID_IDispatch)
if err != nil {
return err
}
defer wmi.Release()
// service is a SWbemServices
serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", connectServerArgs...)
if err != nil {
return err
}
service := serviceRaw.ToIDispatch()
defer serviceRaw.Clear()
// result is a SWBemObjectSet
resultRaw, err := oleutil.CallMethod(service, "ExecQuery", query)
if err != nil {
return err
}
result := resultRaw.ToIDispatch()
defer resultRaw.Clear()
count, err := oleInt64(result, "Count")
if err != nil {
return err
}
// Initialize a slice with Count capacity
dv.Set(reflect.MakeSlice(dv.Type(), 0, int(count)))
var errFieldMismatch error
for i := int64(0); i < count; i++ {
err := func() error {
// item is a SWbemObject, but really a Win32_Process
itemRaw, err := oleutil.CallMethod(result, "ItemIndex", i)
if err != nil {
return err
}
item := itemRaw.ToIDispatch()
defer itemRaw.Clear()
ev := reflect.New(elemType)
if err = loadEntity(ev.Interface(), item); err != nil {
if _, ok := err.(*ErrFieldMismatch); ok {
// We continue loading entities even in the face of field mismatch errors.
// If we encounter any other error, that other error is returned. Otherwise,
// an ErrFieldMismatch is returned.
errFieldMismatch = err
} else {
return err
}
}
if mat != multiArgTypeStructPtr {
ev = ev.Elem()
}
dv.Set(reflect.Append(dv, ev))
return nil
}()
if err != nil {
return err
}
}
return errFieldMismatch
}
// ErrFieldMismatch is returned when a field is to be loaded into a different
// type than the one it was stored from, or when a field is missing or
// unexported in the destination struct.
// StructType is the type of the struct pointed to by the destination argument.
type ErrFieldMismatch struct {
StructType reflect.Type
FieldName string
Reason string
}
func (e *ErrFieldMismatch) Error() string {
return fmt.Sprintf("wmi: cannot load field %q into a %q: %s",
e.FieldName, e.StructType, e.Reason)
}
var timeType = reflect.TypeOf(time.Time{})
// loadEntity loads a SWbemObject into a struct pointer.
func loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismatch error) {
v := reflect.ValueOf(dst).Elem()
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
isPtr := f.Kind() == reflect.Ptr
if isPtr {
ptr := reflect.New(f.Type().Elem())
f.Set(ptr)
f = f.Elem()
}
n := v.Type().Field(i).Name
if !f.CanSet() {
return &ErrFieldMismatch{
StructType: f.Type(),
FieldName: n,
Reason: "CanSet() is false",
}
}
prop, err := oleutil.GetProperty(src, n)
if err != nil {
errFieldMismatch = &ErrFieldMismatch{
StructType: f.Type(),
FieldName: n,
Reason: "no such struct field",
}
continue
}
defer prop.Clear()
switch val := prop.Value().(type) {
case int, int64:
var v int64
switch val := val.(type) {
case int:
v = int64(val)
case int64:
v = val
default:
panic("unexpected type")
}
switch f.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
f.SetInt(v)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
f.SetUint(uint64(v))
default:
return &ErrFieldMismatch{
StructType: f.Type(),
FieldName: n,
Reason: "not an integer class",
}
}
case string:
iv, err := strconv.ParseInt(val, 10, 64)
switch f.Kind() {
case reflect.String:
f.SetString(val)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if err != nil {
return err
}
f.SetInt(iv)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if err != nil {
return err
}
f.SetUint(uint64(iv))
case reflect.Struct:
switch f.Type() {
case timeType:
if len(val) == 25 {
mins, err := strconv.Atoi(val[22:])
if err != nil {
return err
}
val = val[:22] + fmt.Sprintf("%02d%02d", mins/60, mins%60)
}
t, err := time.Parse("20060102150405.000000-0700", val)
if err != nil {
return err
}
f.Set(reflect.ValueOf(t))
}
}
case bool:
switch f.Kind() {
case reflect.Bool:
f.SetBool(val)
default:
return &ErrFieldMismatch{
StructType: f.Type(),
FieldName: n,
Reason: "not a bool",
}
}
default:
typeof := reflect.TypeOf(val)
if isPtr && typeof == nil {
break
}
return &ErrFieldMismatch{
StructType: f.Type(),
FieldName: n,
Reason: fmt.Sprintf("unsupported type (%T)", val),
}
}
}
return errFieldMismatch
}
type multiArgType int
const (
multiArgTypeInvalid multiArgType = iota
multiArgTypeStruct
multiArgTypeStructPtr
)
// checkMultiArg checks that v has type []S, []*S for some struct type S.
//
// It returns what category the slice's elements are, and the reflect.Type
// that represents S.
func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) {
if v.Kind() != reflect.Slice {
return multiArgTypeInvalid, nil
}
elemType = v.Type().Elem()
switch elemType.Kind() {
case reflect.Struct:
return multiArgTypeStruct, elemType
case reflect.Ptr:
elemType = elemType.Elem()
if elemType.Kind() == reflect.Struct {
return multiArgTypeStructPtr, elemType
}
}
return multiArgTypeInvalid, nil
}
func oleInt64(item *ole.IDispatch, prop string) (int64, error) {
v, err := oleutil.GetProperty(item, prop)
if err != nil {
return 0, err
}
defer v.Clear()
i := int64(v.Val)
return i, nil
}
// CreateQuery returns a WQL query string that queries all columns of src. where
// is an optional string that is appended to the query, to be used with WHERE
// clauses. In such a case, the "WHERE" string should appear at the beginning.
func CreateQuery(src interface{}, where string) string {
var b bytes.Buffer
b.WriteString("SELECT ")
s := reflect.Indirect(reflect.ValueOf(src))
t := s.Type()
if s.Kind() == reflect.Slice {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return ""
}
var fields []string
for i := 0; i < t.NumField(); i++ {
fields = append(fields, t.Field(i).Name)
}
b.WriteString(strings.Join(fields, ", "))
b.WriteString(" FROM ")
b.WriteString(t.Name())
b.WriteString(" " + where)
return b.String()
}
To access the winmgmts object or a namespace (which is the same), you can use the code below. Basically, you need to specify the namespace as parameter, which is not documented properly in go-ole.
In the code below, you can also see how to access a class within this namespace and execute a method.
package main
import (
"log"
"github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
)
func main() {
ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
defer ole.CoUninitialize()
unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
if err != nil {
log.Panic(err)
}
defer unknown.Release()
wmi, err := unknown.QueryInterface(ole.IID_IDispatch)
if err != nil {
log.Panic(err)
}
defer wmi.Release()
// Connect to namespace
// root/PanasonicPC = winmgmts:\\.\root\PanasonicPC
serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", nil, "root/PanasonicPC")
if err != nil {
log.Panic(err)
}
service := serviceRaw.ToIDispatch()
defer serviceRaw.Clear()
// Get class
setBiosRaw, err := oleutil.CallMethod(service, "Get", "SetBIOS4Conf")
if err != nil {
log.Panic(err)
}
setBios := setBiosRaw.ToIDispatch()
defer setBiosRaw.Clear()
// Run method
resultRaw, err := oleutil.CallMethod(setBios, "AccessAuthorization", "letmein")
resultVal := resultRaw.Value().(int32)
log.Println("Return Code:", resultVal)
}
import(
"os/exec"
)
func (lcu *LCU) GrabToken() {
cmd := exec.Command("powershell", "$cmdline = Get-WmiObject -Class Win32_Process")
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(err)
}
outstr := string(out)
}
Situation:
I want to get a password entry from the stdin console - without echoing what the user types. Is there something comparable to getpasswd functionality in Go?
What I tried:
I tried using syscall.Read, but it echoes what is typed.
The following is one of best ways to get it done.
First get term package by go get golang.org/x/term
package main
import (
"bufio"
"fmt"
"os"
"strings"
"syscall"
"golang.org/x/term"
)
func main() {
username, password, _ := credentials()
fmt.Printf("Username: %s, Password: %s\n", username, password)
}
func credentials() (string, string, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter Username: ")
username, err := reader.ReadString('\n')
if err != nil {
return "", "", err
}
fmt.Print("Enter Password: ")
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return "", "", err
}
password := string(bytePassword)
return strings.TrimSpace(username), strings.TrimSpace(password), nil
}
http://play.golang.org/p/l-9IP1mrhA
Just saw a mail in #go-nuts maillist. There is someone who wrote quite a simple go package to be used. You can find it here: https://github.com/howeyc/gopass
It something like that:
package main
import "fmt"
import "github.com/howeyc/gopass"
func main() {
fmt.Printf("Password: ")
pass := gopass.GetPasswd()
// Do something with pass
}
Since Go ~v1.11 there is an official package golang.org/x/term which replaces the deprecated crypto/ssh/terminal. It has, among other things, the function term.ReadPassword.
Example usage:
package main
import (
"fmt"
"os"
"syscall"
"golang.org/x/term"
)
func main() {
fmt.Print("Password: ")
bytepw, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
os.Exit(1)
}
pass := string(bytepw)
fmt.Printf("\nYou've entered: %q\n", pass)
}
I had a similar usecase and the following code snippet works well for me. Feel free to try this if you are still stuck here.
import (
"fmt"
"golang.org/x/crypto/ssh/terminal"
)
func main() {
fmt.Printf("Now, please type in the password (mandatory): ")
password, _ := terminal.ReadPassword(0)
fmt.Printf("Password is : %s", password)
}
Of course, you need to install terminal package using go get beforehand.
you can do this by execing stty -echo to turn off echo and then stty echo after reading in the password to turn it back on
Here is a solution that I developed using Go1.6.2 that you might find useful.
It only uses the following standard packages: bufio, fmt, os, strings and syscall. More specifically, it uses syscall.ForkExec() and syscall.Wait4() to invoke stty to disable/enable terminal echo.
I have tested it on Linux and BSD (Mac). It will not work on windows.
// getPassword - Prompt for password. Use stty to disable echoing.
import ( "bufio"; "fmt"; "os"; "strings"; "syscall" )
func getPassword(prompt string) string {
fmt.Print(prompt)
// Common settings and variables for both stty calls.
attrs := syscall.ProcAttr{
Dir: "",
Env: []string{},
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
Sys: nil}
var ws syscall.WaitStatus
// Disable echoing.
pid, err := syscall.ForkExec(
"/bin/stty",
[]string{"stty", "-echo"},
&attrs)
if err != nil {
panic(err)
}
// Wait for the stty process to complete.
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
// Echo is disabled, now grab the data.
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
// Re-enable echo.
pid, err = syscall.ForkExec(
"/bin/stty",
[]string{"stty", "echo"},
&attrs)
if err != nil {
panic(err)
}
// Wait for the stty process to complete.
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
return strings.TrimSpace(text)
}
Required launching stty via Go ForkExec() function:
package main
import (
os "os"
bufio "bufio"
fmt "fmt"
str "strings"
)
func main() {
fmt.Println();
if passwd, err := Getpasswd("Enter password: "); err == nil {
fmt.Printf("\n\nPassword: '%s'\n",passwd)
}
}
func Getpasswd(prompt string) (passwd string, err os.Error) {
fmt.Print(prompt);
const stty_arg0 = "/bin/stty";
stty_argv_e_off := []string{"stty","-echo"};
stty_argv_e_on := []string{"stty","echo"};
const exec_cwdir = "";
fd := []*os.File{os.Stdin,os.Stdout,os.Stderr};
pid, err := os.ForkExec(stty_arg0,stty_argv_e_off,nil,exec_cwdir,fd);
if err != nil {
return passwd, os.NewError(fmt.Sprintf("Failed turning off console echo for password entry:\n\t%s",err))
}
rd := bufio.NewReader(os.Stdin);
os.Wait(pid,0);
line, err := rd.ReadString('\n');
if err == nil {
passwd = str.TrimSpace(line)
} else {
err = os.NewError(fmt.Sprintf("Failed during password entry: %s",err))
}
pid, e := os.ForkExec(stty_arg0,stty_argv_e_on,nil,exec_cwdir,fd);
if e == nil {
os.Wait(pid,0)
} else if err == nil {
err = os.NewError(fmt.Sprintf("Failed turning on console echo post password entry:\n\t%s",e))
}
return passwd, err
}
Here is a version specific to Linux:
func terminalEcho(show bool) {
// Enable or disable echoing terminal input. This is useful specifically for
// when users enter passwords.
// calling terminalEcho(true) turns on echoing (normal mode)
// calling terminalEcho(false) hides terminal input.
var termios = &syscall.Termios{}
var fd = os.Stdout.Fd()
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
syscall.TCGETS, uintptr(unsafe.Pointer(termios))); err != 0 {
return
}
if show {
termios.Lflag |= syscall.ECHO
} else {
termios.Lflag &^= syscall.ECHO
}
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
uintptr(syscall.TCSETS),
uintptr(unsafe.Pointer(termios))); err != 0 {
return
}
}
So to use it:
fmt.Print("password: ")
terminalEcho(false)
var pw string
fmt.Scanln(&pw)
terminalEcho(true)
fmt.Println("")
It's the TCGETS syscall that is linux specific. There are different syscall values for OSX and Windows.
You could also use PasswordPrompt function of https://github.com/peterh/liner package.
Turning off echo before typing and turning on to turn it back on after typing.
Without third library, you can find ways to do with it on unix shown above. But it's difficult on Windows.
You can achieve it by method SetConsoleMode with windows kernel32.dll referring to the accepted answer from C: How to disable echo in windows console?
func GetPassword(prompt string) (err error, text string) {
var modeOn, modeOff uint32
stdin := syscall.Handle(os.Stdin.Fd())
err = syscall.GetConsoleMode(stdin, &modeOn)
if err != nil {
return
}
modeOff = modeOn &^ 0x0004
proc := syscall.MustLoadDLL("kernel32").MustFindProc("SetConsoleMode")
fmt.Print(prompt)
_, _, _ = proc.Call(uintptr(stdin), uintptr(modeOff))
_, err = fmt.Scanln(&text)
if err != nil {
return
}
_, _, _ = proc.Call(uintptr(stdin), uintptr(modeOn))
fmt.Println()
return nil, strings.TrimSpace(text)
}
You can get the behavior you want with the Read method from the os.File object (or the os.Stdin variable). The following sample program will read a line of text (terminated with by pressing the return key) but won't echo it until the fmt.Printf call.
package main
import "fmt"
import "os"
func main() {
var input []byte = make( []byte, 100 );
os.Stdin.Read( input );
fmt.Printf( "%s", input );
}
If you want more advanced behavior, you're probably going to have to use the Go C-wrapper utilities and create some wrappers for low-level api calls.