20

I'm trying to make a simple package to send SSH commands to a server.

I have the following code:

type Connection *ssh.Client

func Connect(addr, user, password string) (conn Connection, err error) {
    sshConfig := &ssh.ClientConfig{
        User: user,
        Auth: []ssh.AuthMethod{
            ssh.Password(password),
        },
        HostKeyCallback: ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }),
    }

    conn, err = ssh.Dial("tcp", addr, sshConfig)
    return
}

func (conn Connection) SendCommand() ([]byte, error) {
    session, err := (*ssh.Client)(conn).NewSession()
    // ...
}

My problem is on the two lines func (conn Connection) SendCommand() ([]byte, error) and session, err := (*ssh.Client)(conn).NewSession().

I can't figure out how to use the methods available for *ssh.Client from my overlaying Connection type.

I understand that I need to do some conversion, and using ssh.Client(*conn).NewSession() would work, but it copies the values of the *ssh.Client which doesn't seem to be the right method.

What should do to access the methods available for a *ssh.Client when working with my custom type Connection *ssh.Client type?

0

3 Answers 3

47

You can't declare a new type with a pointer TypeSpec. Also declaring a new type is used specifically to remove the entire method set, so you won't have any of the original methods from the *ssh.Client.

What you want is to use composition by embedding the *ssh.Client in your own struct type:

type Connection struct {
    *ssh.Client
}

func Connect(addr, user, password string) (*Connection, error) {
    sshConfig := &ssh.ClientConfig{
        User: user,
        Auth: []ssh.AuthMethod{
            ssh.Password(password),
        },
        HostKeyCallback: ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }),
    }

    conn, err = ssh.Dial("tcp", addr, sshConfig)
    if err != nil {
        return nil, err
    }

    return &Connection{conn}, nil
}

func (conn *Connection) SendCommand() ([]byte, error) {
    session, err := conn.NewSession()
    // ...
}
Sign up to request clarification or add additional context in comments.

1 Comment

Works perfectly. return Connection{conn}, nil should be changed to return &Connection{conn}, nil
5

This is the best I can come up with:

type Connection ssh.Client

func (conn *Connection) SendCommand() ([]byte, error) {
    (*ssh.Client)(conn).NewSession()

Note that I've changed the type to not be a pointer type (but then I've made a pointer receiver for SendCommand). I'm not sure there's any way to create a function with a pointer type as a receiver.

5 Comments

This works and it's the method I am using while I was waiting for an answer, but my linter is giving me a message saying call of Connection copies lock value ssh.Client
What line is it complaining about?
play.golang.org/p/dY3xcXpHif Lines 23 and 28 Or in your example, the SendCommand() line
But that code differs in important ways (e.g. doesn't take a pointer to SendCommand).
This solution didn't work for me because the struct that I'm trying to gain access to has public functions attached to it (declared in the package where the struct is declared) - but when I try to access them from within my new function, using the above approach, the public functions aren't found.
1

Another option is to use type aliasing to achieve the desired behavior. I was trying to do something "clever" for readability:

type foo struct {
    i int
}

type foo_ptr = *foo

type foo_ptr_slice = []foo_ptr
type foo_ptr_map = map[string]foo_ptr
type foo_ptr_slice_map = map[string]foo_ptr_slice

func (r foo_ptr) dump() {
    fmt.Printf("%d\n", r.i)
}

func main() {
    // need a map of slice of pointers
    var m foo_ptr_map

    m = make(foo_ptr_map, 0)

    m["test"] = &foo{i: 1}

    var m2 foo_ptr_slice_map

    m2 = make(foo_ptr_slice_map, 0)
    m2["test"] = make(foo_ptr_slice, 0, 10)

    m2["test"] = append(m2["test"], &foo{i: 2})

    fmt.Printf("%d\n", m["test"].i)
    fmt.Printf("%d\n", m2["test"][0].i)
    m["test"].dump()
}

I acknowledge that type aliasing is used for large-scale refactoring but this seems like a very good use for readability sake.

1 Comment

This only works for local types.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.