Sending GET requests from user input within Docker? - docker

I'm using Golang and trying to take user input from the CLI and send it as a GET request to localhost:8080 but I can't work out how to do it. I currently have the following and am using a Docker container:
func main() {
fmt.Println("Enter desired input: ")
// Get user input
var input string
fmt.Scanln(&input)
http.HandleFunc("/", doSomething)
log.Fatal(http.ListenAndServe(":8081", nil))
resp, err := http.Get("http://localhost:8080/?input=" + input)
// Print response out here
}
func doSomething(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("input")
// Send response
}
Docker file:
FROM golang:1.12.0-alpine3.9
RUN mkdir /app
ADD . /app
WORKDIR /app
RUN go build -o main .
CMD ["/app/main"]
I then start the Docker container:
docker run -it -p 8080:8081 go-app
I would like to enter in numbers in the CLI and then return a response from the HTTP server. How can I do this as the above solution doesn't work (since it doesn't make the GET request within the main() method).

Run the http server in a different goroutine
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
fmt.Println("Enter desired input: ")
// Get user input
var input string
fmt.Scanln(&input)
l, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
go func() {
http.HandleFunc("/", doSomething)
log.Fatal(http.Serve(l, nil))
}()
resp, err := http.Get("http://localhost:8080/?input=" + input)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Resp: %v\n", resp)
// Print response out here
}
func doSomething(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("input")
fmt.Println(query)
// Send response
}

Related

Can't find local Docker images created with Go SDK

I'm trying to create some docker images on macOS (with Colima backend) using Go Docker SDK with following code:
package main
import (
"context"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/archive"
)
const dockerfile = `
# syntax=docker/dockerfile:1
FROM golang:1.19-alpine
WORKDIR /app
ENV GO111MODULE=on
ENV CGO_ENABLED=0
COPY . ./
RUN go mod download
RUN apk add --no-cache git
RUN go install github.com/securego/gosec/v2/cmd/gosec#latest
RUN go install golang.org/x/vuln/cmd/govulncheck#latest
RUN go get github.com/stripe/safesql
CMD gosec ./... && govulncheck ./... && safesql ./...`
func main() {
ctx := context.Background()
pathFlag := flag.String("path", ".", "path to the directory containing the go files")
flag.Parse()
if pathFlag == nil {
log.Fatal("path flag is nil")
}
err := os.WriteFile(*pathFlag+"/Dockerfile", []byte(dockerfile), 0777)
if err != nil {
fmt.Printf("Unable to write file: %v", err)
}
cli, err := client.NewClientWithOpts()
if err != nil {
log.Fatal(err, " :unable to init client")
}
files, err := WalkMatch(*pathFlag, "*.go")
if err != nil {
log.Fatal(err, " :unable to walk files")
}
reader, err := archive.TarWithOptions(*pathFlag, &archive.TarOptions{
IncludeFiles: append(files, []string{"go.mod", "go.sum", "Dockerfile"}...),
})
if err != nil {
log.Fatal(err, " :unable to create tar")
}
imageBuildResponse, err := cli.ImageBuild(
ctx,
reader,
types.ImageBuildOptions{
Tags: []string{"newname"},
Context: reader,
Dockerfile: "Dockerfile",
})
if err != nil {
log.Fatal(err, " :unable to build docker image")
}
defer imageBuildResponse.Body.Close()
_, err = io.Copy(os.Stdout, imageBuildResponse.Body)
if err != nil {
log.Fatal(err, " :unable to read image build response")
}
}
func WalkMatch(root, pattern string) ([]string, error) {
var matches []string
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if matched, err := filepath.Match(pattern, filepath.Base(path)); err != nil {
return err
} else if matched {
r, err := filepath.Rel(root, path)
if err != nil {
return err
}
matches = append(matches, r)
}
return nil
})
if err != nil {
return nil, err
}
return matches, nil
}
The creation seems to be successful:
{"aux":{"ID":"sha256:568d5aed149aa73d38b644f49f68590cc06222467be1e1c3b902792da39f6e24"}}
{"stream":"Successfully built 568d5aed149a\n"}
{"stream":"Successfully tagged newname:latest\n"}
However, I can't find these images anywhere, docker run states there are no images with such tag, docker ls tells me the same. Do I miss something crucial? Are there any macOS specific things I should consider?
You are only building the images not running them so you should use the command
docker images
to get all the images present on your system.
docker ps
only gives back the images which are currently running

How to force docker file use envs which are passed by docker client in golang?

I'm using a docker client with golang; in the following script, I'm trying to pass an environmental variable when a container is going to start.
package main
import (
"context"
"fmt"
"os/user"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
)
func main() {
dockerClient, _ := client.NewClientWithOpts(client.FromEnv)
env := []string{}
for key, value := range map[string]string{"hi": "hoo"} {
env = append(env, fmt.Sprintf("%s=%s", key, value))
}
user, err := user.Current()
if err != nil {
fmt.Println(err)
}
config := container.Config{
User: fmt.Sprintf("%s:%s", user.Uid, user.Gid),
Image: "675d8442a90f",
Env: env,
}
response, err := dockerClient.ContainerCreate(context.Background(), &config, nil, nil, nil, "")
if err != nil {
fmt.Println(err)
}
if err = dockerClient.ContainerStart(context.Background(), response.ID, types.ContainerStartOptions{}); err != nil {
fmt.Println(err)
}
}
and my docker file is a simple one which I try to echo the hi env:
# Filename: Dockerfile
FROM ubuntu:latest
COPY . .
CMD ["echo", "$hi"]
When I built the image and passed the id to the script, it didn't echo the variable. Do you have any idea how I can use the environmental variables in the dockerfile, which are sent to the container by docker golang client?

Go gRPC on Docker on Cloud Run "transport: Error while dialing dial tcp : i/o timeout""

I have a gRPC server
type Server struct {
name string
host string
port string
dbUser string
dbPassword string
dbURL string
dbParameters string
}
func main() {
/*Start of config*/
server := Server{
"User service",
"",
os.Getenv("PORT"),
"",
"",
"",
"",
}
/*End of config*/
log.Printf("Starting: %s RPCServer\n", server.name)
lis, err := net.Listen("tcp", server.host+":"+server.port)
if err != nil {
log.Fatalf("Failed to liste: %v\n", err)
}
defer func() {
err = lis.Close()
if err != nil {
log.Fatalf("Failed to close listener: %v\n", err)
}
}()
gRPCServer := grpc.NewServer()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
reg := codecs.Register(bson.NewRegistryBuilder()).Build()
mongoURI := options.Client().ApplyURI(
"mongodb+srv://" + server.dbUser + ":" + server.dbPassword + "#" + server.dbURL + server.dbParameters,
)
mongoClient, err := mongo.NewClient(mongoURI, &options.ClientOptions{
Registry: reg,
})
if err != nil {
log.Fatalf("unable to create new mongo clinet: %s\n", err)
}
err = mongoClient.Connect(ctx)
if err != nil {
log.Fatalf("unable to connect to db: %s\n", err)
}
/*Start of registering service*/
authService, err := AuthService.NewAuthService()
if err != nil {
log.Fatalf("unable to create server: %s\n", err)
}
s := UserService.NewServer(authService, mongoClient)
UserService.RegisterUserServiceServer(gRPCServer, &s)
/*End of registering service*/
go func() {
if err := gRPCServer.Serve(lis); err != nil {
log.Printf("Failed to serve: %v\n", err)
}
}()
log.Printf("server successfully started on port: %s\n\n", server.port)
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
<-c
log.Printf("\nstopping server...\n")
gRPCServer.Stop()
err = lis.Close()
if err != nil {
log.Fatalf("failed to close listner: %s\n", err)
}
err = mongoClient.Disconnect(ctx)
if err != nil {
log.Fatalf("failed to disconnect from mongodb: %s\n", err)
}
log.Printf("successfully closed server\n")
}
And my Dockerfile is
FROM golang:alpine AS build-env
WORKDIR /app
ADD . /app
RUN cd /app && go build Main/server.go
FROM alpine
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
WORKDIR /app
COPY --from=build-env /app/server /app
COPY --from=build-env /app/Config/* /app
ENV GOOGLE_APPLICATION_CREDENTIALS=cred.json
EXPOSE 50051
CMD ["./server"]
I put up the docker image on Google Container Registry, and tried to use Cloud Run to run it using the following command
gcloud run deploy grpc-server-streaming\
--project=project-id\
--platform=managed\
--region=asia-south1\
--image=image-tag\
--allow-unauthenticated
After which I went into Cloud Run application and enabled HTTP/2 connections. But am still not able to connect to the service. I get the following error
2021/04/21 17:04:36 rpc error: code = Unavailable desc = upstream connect error or disconnect/reset before headers. reset reason: connection termination
I've been stuck at this for two days and am not sure what to do.
The same is happening for my ASPNet hosted gRPC service. It was working last Friday (2021-04-30), but on Monday (2021-05-03) I started getting this error message.
I have been searching for days, like you, for an answer. Thank-you to #menghanl for the Envoy pointer, I will open that can of worms.
#Chandraaditya Have a look at your Cloud Run Service's Logs to see if your service is actually being triggered.
I can see my service handling the request at the time I am getting the error. The gRPC service call appears to be 200 OK, but "Envoy" is not sending it back to the client.

kubectl log shows 'standard_init_linux.go:211: exec user process caused "no such file or directory"'

I am trying to create a containerSource for knative service. When I use docker run for the image it gives the output ("or the error from the code"). However when I apply the yaml file then kubectl log shows 'standard_init_linux.go:211: exec user process caused "no such file or directory"'. docker run shows that it is able to find the exec file. So I am not able to understand whats wrong. Someone please guide me through.
my yaml file:
apiVersion: sources.eventing.knative.dev/v1alpha1
kind: ContainerSource
metadata:
labels:
controller-tools.k8s.io: "1.0"
name: cloudevents-source
spec:
image: docker.io/username/pkt-event:latest
args:
- '--debug=true'
sink:
apiVersion: serving.knative.dev/v1alpha1
kind: Service
name: event-display
my go code for the dockerimage is:
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"time"
"github.com/satori/go.uuid"
"knative.dev/eventing-contrib/pkg/kncloudevents"
"encoding/json"
// "io/ioutil"
// "knative.dev/eventing-contrib/vendor/github.com/cloudevents/sdk-go/pkg/cloudevents"
"github.com/cloudevents/sdk-go/pkg/cloudevents"
"github.com/cloudevents/sdk-go/pkg/cloudevents/types"
"github.com/kelseyhightower/envconfig"
)
var (
eventSource string
eventType string
sink string
)
//var u, _ = uuid.NewV4()
var debug = flag.Bool("debug", false, "Enable debug mode (print more information)")
var source = flag.String("source", uuid.NewV4().String(), "Set custom Source for the driver")
func init() {
flag.StringVar(&eventSource, "eventSource", "", "the event-source (CloudEvents)")
flag.StringVar(&eventType, "eventType", "dev.knative.eventing.samples.pkt", "the event-type (CloudEvents)")
flag.StringVar(&sink, "sink", "", "the host url to send pkts to")
}
type envConfig struct {
// Sink URL where to send heartbeat cloudevents
Sink string `envconfig:"SINK"`
}
func main() {
flag.Parse()
var env envConfig
if err := envconfig.Process("", &env); err != nil {
log.Printf("[ERROR] Failed to process env var: %s", err)
os.Exit(1)
}
if env.Sink != "" {
sink = env.Sink
}
if eventSource == "" {
eventSource = fmt.Sprintf("https://knative.dev/eventing-contrib/cmd/heartbeats/#local/demo")
log.Printf("Source: %s", eventSource)
}
client, err := kncloudevents.NewDefaultClient(sink)
if err != nil {
log.Fatalf("failed to create client: %s", err.Error())
}
var period time.Duration
period = time.Duration(1) * time.Second
ticker := time.NewTicker(period)
for {
content := "Send data"
data, err := json.Marshal(content)
if err != nil {
fmt.Println(err)
}
event := cloudevents.Event{
Context: cloudevents.EventContextV02{
Type: "packet.invoke",
Source: *types.ParseURLRef(eventSource),
/*Extensions: map[string]interface{}{
"the": 42,
"heart": "yes",
"beats": true,
},*/
}.AsV02(),
Data: data,
}
if *debug{
log.Printf("Sending event %v", event)
} else {
if _, err := client.Send(context.TODO(), event); err != nil {
log.Printf("failed to send cloudevent: %s", err.Error())
}
}
<-ticker.C
}
}
And Dockerfile is:
FROM golang:1.12 as builder
RUN go version
WORKDIR ${GOPATH}/src/Event-driver
COPY ./ ./
RUN curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
##RUN dep ensure
RUN dep init
RUN dep ensure
RUN CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -v -o my-event my-event.go
RUN pwd && ls
FROM scratch
#FROM ubuntu:disco
COPY --from=builder /go/src/Event-driver/my-event /
ENTRYPOINT ["/my-event"]
That problem occurs because you're trying to run your binary from bash, but scratch has no bash.
I'm normally using alpina instead. To build for alpina you need the same environment variables, so probably you only need to change a second stage image.

"golang.org/x/net/ipv4" working on Mac, but not on Linux

I'm using "golang.org/x/net/ipv4" in order to use its SetTTL function. Unfortunately it does not seem to work on Linux, only on Mac, even though the documentation indicates Linux supports all the functions.
Here's a minimal example of the problem, with a Dockerfile:
main.go:
package main
import (
"fmt"
"net"
"bufio"
xnet "golang.org/x/net/ipv4"
)
const Host = "google.com"
func main() {
var err error
conn, err := net.Dial("tcp4", Host + ":80")
if err != nil {
panic(err)
}
defer conn.Close()
xconn := xnet.NewConn(conn)
err = xconn.SetTTL(5)
if err != nil {
panic(err)
}
defer xconn.Close()
fmt.Fprint(conn, "GET / HTTP/1.1\r\nHOST: google.com\r\n\r\n")
firstLine, err := bufio.NewReader(xconn).ReadString('\n')
if err != nil {
panic(err)
}
fmt.Println(firstLine)
}
Dockerfile:
FROM golang:1.8.1-alpine
RUN apk --no-cache add git
RUN go get golang.org/x/net/ipv4
COPY . /go/src/me.com/me/xnetproblem
RUN go install me.com/me/xnetproblem
CMD ["/go/bin/xnetproblem"]
I run this command:
docker build -t xnet .
I get this output:
john xnetproblem > docker build -t xnet .
Sending build context to Docker daemon 90.62 kB
Step 1/6 : FROM golang:1.8.1-alpine
[snip]
Step 5/6 : RUN go install me.com/me/xnetproblem
---> Running in c3802fe61d63
# me.com/me/xnetproblem
src/me.com/me/xnetproblem/main.go:25: xconn.Close undefined (type *ipv4.Conn has no field or method Close)
src/me.com/me/xnetproblem/main.go:28: cannot use xconn (type *ipv4.Conn) as type io.Reader in argument to bufio.NewReader:
*ipv4.Conn does not implement io.Reader (missing Read method)
The command '/bin/sh -c go install me.com/me/xnetproblem' returned a non-zero code: 2
Using go install natively, instead of Docker, the program works on Mac but not on Linux.
Thanks to #JimB's comment I realized my Mac had an old version of the ipv4 package installed. After updating I was able to fix the code.
Here's a complete working version:
package main
import (
"fmt"
"net"
"bufio"
"golang.org/x/net/ipv4"
)
const Host = "google.com"
func main() {
var err error
conn, err := net.Dial("tcp4", Host + ":80")
if err != nil {
panic(err)
}
defer conn.Close()
if err = ipv4.NewConn(conn).SetTTL(5); err != nil {
panic(err)
}
fmt.Fprint(conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %v\r\n\r\n", Host))
firstLine, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
panic(err)
}
fmt.Println(firstLine)
}
Here is the output:
HTTP/1.1 301 Moved Permanently

Resources