Passing []net.Conn to io.MultiWriter - go

I have many of net.Conn and i want to implement multi writer. so i can send data to every available net.Conn.
My current approach is using io.MultiWriter.
func route(conn, dst []net.Conn) {
targets := io.MultiWriter(dst[0], dst[1])
_, err := io.Copy(targets, conn)
if err != nil {
log.Println(err)
}
}
but the problem is, i must specify every net.Conn index in io.MultiWriter and it will be a problem, because the slice size is dynamic.
when i try another approach by pass the []net.Conn to io.MultiWriter, like code below
func route(conn, dst []net.Conn) {
targets := io.MultiWriter(dst...)
_, err := io.Copy(targets, conn)
if err != nil {
log.Println(err)
}
}
there is an error "cannot use mirrors (variable of type []net.Conn) as []io.Writer value in argument to io.MultiWriter"
Is there proper way to handle this case? so i can pass the net.Conn slice to io.MultiWriter.
Thank you.

io.MultiWriter() has a param of type ...io.Writer, so you may only pass a slice of type []io.Writer.
So first create a slice of the proper type, copy the net.Conn values to it, then pass it like this:
ws := make([]io.Writer, len(dst))
for i, c := range dst {
ws[i] = c
}
targets := io.MultiWriter(ws...)

Related

Can't indexing an array without using another var

I think is something that I miss theoretically from the passing by reference topic but I can't find a way to read the ID without using the support networkInterfaceReference
package main
import (
"context"
"fmt"
"github.com/Azure/azure-sdk-for-go/profiles/preview/resources/mgmt/resources"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-03-01/compute"
"github.com/Azure/azure-sdk-for-go/services/subscription/mgmt/2020-09-01/subscription"
"github.com/Azure/go-autorest/autorest/azure/auth"
"github.com/ktr0731/go-fuzzyfinder"
)
var selectedSub subscription.Model
var selectedRG resources.Group
var selectedVM compute.VirtualMachine
func main() {
selectedSub = GetSubscription()
selectedRG = GetResourceGroup()
selectedVM = GetVM()
fmt.Printf("Sub: %s\nRG: %s\nVM: %s\n", *selectedSub.DisplayName, *selectedRG.Name, *selectedVM.Name)
// THIS WORK
networkInterfaceReference := *selectedVM.NetworkProfile.NetworkInterfaces
fmt.Printf("%s", *networkInterfaceReference[0].ID)
// THIS DOESN'T WORK
fmt.Printf("%s", *selectedVM.NetworkProfile.NetworkInterfaces[0].ID)
}
...
...
...
func GetVM() compute.VirtualMachine {
vmClient := compute.NewVirtualMachinesClient(*selectedSub.SubscriptionID)
authorizer, err := auth.NewAuthorizerFromCLI()
if err == nil {
vmClient.Authorizer = authorizer
}
vmList, err := vmClient.List(context.TODO(), *selectedRG.Name)
if err != nil {
panic(err)
}
idx, err := fuzzyfinder.Find(vmList.Values(), func(i int) string {
return *vmList.Values()[i].Name
})
if err != nil {
panic(err)
}
return vmList.Values()[idx]
}
Hovering to the error showed the following error message:
field NetworkProfile *[]compute.NetworkProfile
(compute.VirtualMachineProperties).NetworkProfile on pkg.go.dev
NetworkProfile - Specifies the network interfaces of the virtual machine.
invalid operation: cannot index selectedVM.NetworkProfile.NetworkInterfaces (variable of type *[]compute.NetworkInterfaceReference)compiler (NonIndexableOperand)
If you want the 2nd way to work:
// WORKS
networkInterfaceReference := *selectedVM.NetworkProfile.NetworkInterfaces
fmt.Printf("%s", *networkInterfaceReference[0].ID)
// THIS DOESN'T WORK
fmt.Printf("%s", *selectedVM.NetworkProfile.NetworkInterfaces[0].ID)
studying the compilation error you are getting (P.S. please don't post screen-shots of code/errors) - the error is failing because you are trying to index a pointer which is not allowed in Go. You can only index maps, arrays or slices.
The fix is simple, since you do two (2) pointer dereferences in the working version, you need to do two (2) same in the single expression - but also you need to ensure the lexical binding order so the indexing is done after the pointer dereference:
fmt.Printf("%s", *(*selectedVM.NetworkProfile.NetworkInterfaces)[0].ID)
Finally, there is no pass-by-reference in Go. Everything is by value. If you want to change a value, pass a pointer to it - but that pointer is a still just a value that is copied.

Extract Prometheus Metrics in Go

I'm new in Golang, what I am trying to do is to query Prometheus and save the query result in an object (such as a map) that has all timestamps and their values of the metric.
I started from this example code with only a few changes (https://github.com/prometheus/client_golang/blob/master/api/prometheus/v1/example_test.go)
func getFromPromRange(start time.Time, end time.Time, metric string) model.Value {
client, err := api.NewClient(api.Config{
Address: "http://localhost:9090",
})
if err != nil {
fmt.Printf("Error creating client: %v\n", err)
os.Exit(1)
}
v1api := v1.NewAPI(client)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
r := v1.Range{
Start: start,
End: end,
Step: time.Second,
}
result, warnings, err := v1api.QueryRange(ctx, metric, r)
if err != nil {
fmt.Printf("Error querying Prometheus: %v\n", err)
os.Exit(1)
}
if len(warnings) > 0 {
fmt.Printf("Warnings: %v\n", warnings)
}
fmt.Printf("Result:\n%v\n", result)
return result
}
The result that is printed is for example:
"TEST{instance="localhost:4321", job="realtime"} =>\n21 #[1597758502.337]\n22 #[1597758503.337]...
These are actually the correct values and timestamps that are on Prometheus. How can I insert these timestamps and values into a map object (or another type of object that I can then use in code)?
The result coming from QueryRange has the type model.Matrix.
This will then contain a pointer of type *SampleStream. As your example then contains only one SampleStream, we can access the first one directly.
The SampleStream then has a Metric and Values of type []SamplePair. What you are aiming for is the slice of sample pairs. Over this we then can iterate and build for instance a map.
mapData := make(map[model.Time]model.SampleValue)
for _, val := range result.(model.Matrix)[0].Values {
mapData[val.Timestamp] = val.Value
}
fmt.Println(mapData)
You have to know the type of result you're getting returned. For example, model.Value can be of type Scalar, Vector, Matrix or String. Each of these types have their own way of getting the data and timestamps. For example, a Vector has an array of Sample types which contain the data you're looking for. The godocs and the github repo for the prom/go client have really great documentation if you want to dive deeper.
maybe you can find your answer in this issue
https://github.com/prometheus/client_golang/issues/194
switch {
case val.Type() == model.ValScalar:
scalarVal := val.(*model.Scalar)
// handle scalar stuff
case val.Type() == model.ValVector:
vectorVal := val.(model.Vector)
for _, elem := range vectorVal {
// do something with each element in the vector
// etc

Passing []*io.PipeWriter to io.MultiWriter

I have a bunch of *io.PipeWriter created and would like to make a multiwriter based on all those pipewriters in a function. So I call a function something like
func copyToWriters(reader *bufio.Reader, errs chan error, writers []*io.PipeWriter) {
for _, writer := range writers {
defer writer.Close()
}
mw := io.MultiWriter(writers)
_, err := io.Copy(mw, reader)
if err != nil {
errs <- err
}
}
I call the method with arguments copyToWriters(reader, errs, []*io.PipeWriter{w1, w2})
But it says
cannot use writers (type []*io.PipeWriter) as type []io.Writer in argument to io.MultiWriter. But
if I change io.MultiWriter(writers) to io.MultiWriter(writers[0],writers[1]) it works. How can I make the existing function work by not having to pass writers separately.
Unfortunately, Golang's type system does not allow casting []*io.PipeWriter to []io.Writer even when *io.PipeWriter implements io.Writer since it requires O(n) operation (reference).
The best you can do is create another []io.Writer and copy the pipe writers into it
ws := make([]io.Writer, len(writers))
for i, w := range writers {
ws[i] = w
}
mw := io.MultiWriter(ws...)
And reason why you nead ..., read the document

Write a slice of any type to a file in Go

For logging purposes I want to be able to quickly write a slice of any type, whether it be ints, strings, or custom structs, to a file in Go. For instance, in C#, I can do the following in 1 line:
File.WriteAllLines(filePath, myCustomTypeList.Select(x => x.ToString());
How would I go about doing this in Go? The structs implement the Stringer interface.
Edit: I in particular would like the output to be printed to a file and one line per item in the slice
Use the fmt package format values as strings and print to a file:
func printLines(filePath string, values []interface{}) error {
f, err := os.Create(filePath)
if err != nil {
return err
}
defer f.Close()
for _, value := range values {
fmt.Fprintln(f, value) // print values to f, one per line
}
return nil
}
fmt.Fprintln will call Stringer() on your struct type. It will also print int values and string values.
playground example
Use the reflect package to write any slice type:
func printLines(filePath string, values interface{}) error {
f, err := os.Create(filePath)
if err != nil {
return err
}
defer f.Close()
rv := reflect.ValueOf(values)
if rv.Kind() != reflect.Slice {
return errors.New("Not a slice")
}
for i := 0; i < rv.Len(); i++ {
fmt.Fprintln(f, rv.Index(i).Interface())
}
return nil
}
If you have variable values of type myCustomList, then you can call it like this: err := printLines(filePath, values)
playground example

How to pass by reference a slice of maps with value type interface in Golang

I want to pass []map[string]interface{} by reference to a function. Here is my attempt.
package main
import "fmt"
func main() {
b := map[string]int{"foo": 1, "bar": 2}
a := [...]map[string]int{b}
fmt.Println(a)
editit(a)
fmt.Println(a)
}
func editit(a interface{}) {
fmt.Println(a)
b := map[string]int{"foo": 3, "bar": 4}
a = [...]map[string]int{b}
fmt.Println(a)
}
https://play.golang.org/p/9Bt15mvud1
Here is another attempt and what I want to do finally. This does not compile.
func (self BucketStats) GetSamples() {
buckets := []make(map[string]interface{})
self.GetAuthRequest(self.url, &buckets)
//ProcessBuckets()
}
func (self BucketStats) GetAuthRequest(rurl string, **data interface{}) (err error) {
client := &http.Client{}
req, err := http.NewRequest("GET", rurl, nil)
req.SetBasicAuth(self.un, self.pw)
resp, err := client.Do(req)
if err != nil {
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}
// it's all for this!!!
err = json.Unmarshal(body, data)
return
}
Several things wrong here.
First, [...]map[string]int{b} is not in fact a slice, but a fixed-length array. The [...] syntax means "make an array, and set the length at compile time based on what's being put into it". The slice syntax is simply []map[string]int{b}. As a result, your call to editit(a) is in fact passing a copy of the array, not a reference (slices are innately references, arrays are not). When a is reassigned in editit(), you're reassigning the copy, not the original, so nothing changes.
Second, it's almost never useful to use pointers to interfaces. In fact, the Go runtime was changed a few versions back to not automatically dereference pointers to interfaces (like it does for pointers to almost everything else) to discourage this habit. Interfaces are innately references already, so there's little reason to make a pointer to one.
Third, you don't actually need to pass a reference to an interface here. You're trying to unmarshal into the fundamental data structure contained within that interface. You don't actually care about the interface itself. GetAuthRequest(rurl string, data interface{}) works just fine here.
func (self BucketStats) GetSamples() {
var buckets []map[string]interface{}
self.GetAuthRequest(self.url, &buckets)
//ProcessBuckets()
}
func (self BucketStats) GetAuthRequest(rurl string, data interface{}) (err error) {
client := &http.Client{}
req, err := http.NewRequest("GET", rurl, nil)
req.SetBasicAuth(self.un, self.pw)
resp, err := client.Do(req)
if err != nil {
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}
// it's all for this!!!
err = json.Unmarshal(body, data)
return
}
Let me walk you through what exactly takes place, in order:
buckets := var buckets []map[string]interface{}
We don't need a make here, because json.Unmarshal() will fill it for us.
self.GetAuthRequest(self.url, &buckets)
This passes a reference into an interface field. Within GetAuthRequest, data is an interface with underlying type *[]map[string]interface{} and an underlying value equal to the address of the original buckets variable in GetSamples().
err = json.Unmarshal(body, data)
This passes the interface data, by value, to the interface argument to json.Unmarshal(). Inside json.Unmarshal(), it has new interface v with underlying type *[]map[string]interface{} and an underlying value equal to the address of the original buckets variable in GetSamples(). This interface is a different variable, with a different address in memory, from the interface that held that same data in GetAuthRequest, but the data was copied over, and the data contains a reference to your slice, so you're still good.
json.Unmarshal() will, by reflection, fill the slice pointed to by the interface you handed it with the data in your request. It has a reference to the slice header buckets that you handed it, even though it passed through two interfaces to get there, so any changes it make will affect the original buckets variable.
When you get all the way back up to ProcessBuckets(), the buckets variable will contain all of your data.
As an ancillary suggestion, don't use named returns if your function is more than a few lines long. It's better to explicitly return your variables. This is particularly important because of variable shadowing. For example, in your GetAuthRequest() function, it will never return a non-nil error. This is because you're declaring an error variable err in the function signature, but you're immediately shadowing it with a local variable err using the short declaration in req, err := http.NewRequest("GET", rurl, nil). For the remainder of the function, err now refers to this new error variable rather than the one defined as the return variable. As a result, when you return, the original err variable in the return is always nil. A much better style would be:
func (self BucketStats) GetAuthRequest(rurl string, **data interface{}) error {
client := &http.Client{}
req, err := http.NewRequest("GET", rurl, nil)
req.SetBasicAuth(self.un, self.pw)
resp, err := client.Do(req)
if err != nil {
return err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
// it's all for this!!!
return json.Unmarshal(body, data)
}
The function is passing a **interface{} to Unmarshal. To pass the the *[]map[string]interface{} through to Unmarshal, change the function signature to:
func (self BucketStats) GetAuthRequest(rurl string, data interface{}) (err error) {

Resources