Skip to content

Commit

Permalink
Merge pull request #74 from Azure-Samples/npatilsen/goSample
Browse files Browse the repository at this point in the history
getting_started in Golang
  • Loading branch information
patilsnr authored Nov 14, 2023
2 parents 629dcea + 23925d2 commit 3b2c699
Show file tree
Hide file tree
Showing 15 changed files with 338 additions and 12 deletions.
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"vscode": {
"extensions": [
"ms-dotnettools.csharp",
"ms-vscode.cpptools-extension-pack"
"ms-vscode.cpptools-extension-pack",
"golang.go"
]
}
}
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
*.pem
*.crt
*.env
*.exe
*.exe~
*.dll
*.so
*.dylib
*.out
launchSettings.json

# User-specific files
Expand Down
14 changes: 10 additions & 4 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "go_getting_started",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/scenarios/getting_started/go/getting_started.go",
"args": ["${workspaceFolder}/scenarios/getting_started/.env"],
"cwd" : "${workspaceFolder}/scenarios/getting_started/"
},
{
"name": "getting_started/dotnet",
"type": "coreclr",
Expand All @@ -11,13 +20,10 @@
"cwd": "${workspaceFolder}/scenarios/getting_started",
"console": "integratedTerminal",
"stopAtEntry": false,
"env": {
"HostName" : "localhost"
},
"envFile": "${workspaceFolder}/scenarios/getting_started/.env",
"presentation": {
"group": "dotnet"
}

},
{
"name": "telemetry_producer/dotnet",
Expand Down
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ languages:
- csharp
- c
- python
- go
name: "MQTT Application Samples"
description: "Guidance to build Pub/Sub applications targeting MQTT Brokers."
products:
Expand All @@ -14,7 +15,12 @@ products:

| [Setup](./Setup.md) | [Getting Started](./scenarios/getting_started/) | [Telemetry](./scenarios/telemetry/) | [Command](./scenarios/command/) |

These samples provide guidance to build Pub/Sub applications targeting MQTT Brokers in different programming languages. The samples are provided in different programming languages: C#, Python and C.
These samples provide guidance to build Pub/Sub applications targeting MQTT Brokers in different programming languages. The samples are provided in different programming languages:

- C#
- C
- Go
- Python

The instructions are provided for the following MQTT Brokers:
- **Azure Event Grid Namespaces**
Expand Down Expand Up @@ -43,6 +49,7 @@ Each language requires developer tools, such as compilers and SDKs to build and

- [dotnet](./mqttclients/dotnet/README.md)
- [C](./mqttclients/c/README.md)
- [Go](./mqttclients/go/README.md)
- Python (TBD)

# Scenarios
Expand All @@ -58,12 +65,12 @@ Each scenario requires the following configurations:

Follow the instructions in the [Prerequisites](#magic_wand-prerequisites) to configure these scenarios.

| Scenario | Description | dotnet | C | python |
| -------- | ------------|--------|---|------- |
| [Getting Started](./scenarios/getting_started/) | This quick start scenario simulates basic MQTT tasks.| ✓| ✓| ✓|
| [Telemetry](./scenarios/telemetry/) | This scenario simulates multiple clients (the producers) sending data to a different set of topics to be consumed by a single application (the consumer). | ✓| ✓| ✓|
| [Command](./scenarios/command/) | This scenario simulates the request-response messaging pattern using MQTT v5. | ✓| ✓ | soon|
| [Alert](./scenarios/alert/) | This scenario simulates a fan-out use case where multiple clients receive a singlemessage from the same topic. | ✓| soon| soon|
| Scenario | Description | dotnet | C | python | go |
| -------- | ------------|--------|---|------- | -- |
| [Getting Started](./scenarios/getting_started/) | This quick start scenario simulates basic MQTT tasks.| ✓| ✓| ✓| ✓ |
| [Telemetry](./scenarios/telemetry/) | This scenario simulates multiple clients (the producers) sending data to a different set of topics to be consumed by a single application (the consumer). | ✓| ✓| ✓| soon |
| [Command](./scenarios/command/) | This scenario simulates the request-response messaging pattern using MQTT v5. | ✓| ✓ | soon | soon |
| [Alert](./scenarios/alert/) | This scenario simulates a fan-out use case where multiple clients receive a singlemessage from the same topic. | ✓| soon| soon| soon |

>note: soon: in progress and will be added soon
Expand Down
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eclipse/paho.golang v0.12.0 h1:EXQFJbJklDnUqW6lyAknMWRhM2NgpHxwrrL8riUmp3Q=
github.com/eclipse/paho.golang v0.12.0/go.mod h1:TSDCUivu9JnoR9Hl+H7sQMcHkejWH2/xKK1NJGtLbIE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
6 changes: 6 additions & 0 deletions go.work
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
go 1.21.3

use (
./mqttclients/go
./scenarios/getting_started/go
)
3 changes: 3 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
135 changes: 135 additions & 0 deletions mqttclients/go/ConnectionSettings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package ConnectionSettings

import (
"crypto/tls"
"crypto/x509"
"fmt"
"log"
"os"
"strconv"

"github.com/joho/godotenv"
)

type MqttConnectionSettings struct {
Hostname string
TcpPort int
UseTls bool
CleanSession bool
CaFile string
CertFile string
KeyFile string
KeyFilePassword string
KeepAlive uint16
ClientId string
Username string
Password string
}

var mqttSettingNames = [12]string{
"MQTT_HOST_NAME",
"MQTT_TCP_PORT",
"MQTT_USE_TLS",
"MQTT_CLEAN_SESSION",
"MQTT_KEEP_ALIVE_IN_SECONDS",
"MQTT_CLIENT_ID",
"MQTT_USERNAME",
"MQTT_PASSWORD",
"MQTT_CA_FILE",
"MQTT_CERT_FILE",
"MQTT_KEY_FILE",
"MQTT_KEY_FILE_PASSWORD",
}

var defaults = map[string]string{
"MQTT_TCP_PORT": "8883",
"MQTT_USE_TLS": "true",
"MQTT_CLEAN_SESSION": "true",
"MQTT_KEEP_ALIVE_IN_SECONDS": "30",
}

func parseIntValue(value string) int {
parsed, err := strconv.Atoi(value)
if err != nil {
panic(err)
}
return parsed
}

func parseBoolValue(value string) bool {
parsed, err := strconv.ParseBool(value)
if err != nil {
panic(err)
}
return parsed
}

func GetTlsConnection(cs MqttConnectionSettings) *tls.Conn {

cfg := &tls.Config{}

if cs.CertFile != "" && cs.KeyFile != "" {
if cs.KeyFilePassword != "" {
log.Fatal("Password protected key files are not supported at this time.")
}

cert, err := tls.LoadX509KeyPair(cs.CertFile, cs.KeyFile)
if err != nil {
log.Fatal(err)
}

cfg.Certificates = []tls.Certificate{cert}
}

if cs.CaFile != "" {
ca, err := os.ReadFile(cs.CaFile)
if err != nil {
panic(err)
}

caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(ca)
cfg.RootCAs = caCertPool
}

conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", cs.Hostname, cs.TcpPort), cfg)
if err != nil {
panic(err)
}

return conn
}

func LoadConnectionSettings(path string) MqttConnectionSettings {
godotenv.Load(path)
cs := MqttConnectionSettings{}
envVars := make(map[string]string)

// Check to see which env vars are set
for i := 0; i < len(mqttSettingNames); i++ {
name := mqttSettingNames[i]
value := os.Getenv(name)
// If var is not set, check if it has a default value
if value == "" && defaults[name] != "" {
value = defaults[name]
}

envVars[name] = value
}

// Based on which vars are set, construct MqttConnectionSettings
cs.Hostname = envVars["MQTT_HOST_NAME"]
cs.TcpPort = parseIntValue(envVars["MQTT_TCP_PORT"])
cs.UseTls = parseBoolValue(envVars["MQTT_USE_TLS"])
cs.CleanSession = parseBoolValue(envVars["MQTT_CLEAN_SESSION"])
cs.KeepAlive = uint16(parseIntValue(envVars["MQTT_KEEP_ALIVE_IN_SECONDS"]))
cs.ClientId = envVars["MQTT_CLIENT_ID"]
cs.Username = envVars["MQTT_USERNAME"]
cs.Password = envVars["MQTT_PASSWORD"]
cs.CaFile = envVars["MQTT_CA_FILE"]
cs.CertFile = envVars["MQTT_CERT_FILE"]
cs.KeyFile = envVars["MQTT_KEY_FILE"]
cs.KeyFilePassword = envVars["MQTT_KEY_FILE_PASSWORD"]

return cs
}
12 changes: 12 additions & 0 deletions mqttclients/go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Using Go Samples

## Loading Environment Variables
Samples import the code from `ConnectionSettings.go`, which includes the struct `MqttConnectionSettings` and the function `LoadConnectionSettings(path string)` that takes a parameter of type string that specifies the location of a `.env` file. Running samples requires passing this path as a command line argument:

```bash
go run ./program.go <path-to-env-file>
```

## Relevant Libraries
- MQTT V5 [Paho Client for Go](https://github.com/eclipse/paho.golang)
- [Godotenv](https://github.com/joho/godotenv) to load environment variables
5 changes: 5 additions & 0 deletions mqttclients/go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module mqttapplicationsamples/ConnectionSettings

go 1.21.3

require github.com/joho/godotenv v1.5.1
2 changes: 2 additions & 0 deletions mqttclients/go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
16 changes: 16 additions & 0 deletions scenarios/getting_started/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,20 @@ Run the sample using settings from an envfile. This assumes the env file is plac
```bash
# from folder scenarios/getting_started
python python/getting_started.py --env-file <path to .env file>
```

### Go

To build the Go sample run:

```bash
# from folder scenarios/getting_started
$GOROOT/bin/go build -C go/ -o bin/
```

Run the sample using settings from an envfile

```bash
# from folder scenarios/getting_started
go/bin/getting_started .env
```
Loading

0 comments on commit 3b2c699

Please sign in to comment.