SlideShare a Scribd company logo
Go Project Layout
and Practice
Bo-Yi Wu
2019.08.29
ModernWeb
About me
• Software Engineer in Mediatek
• Member of Drone CI/CD Platform
• Member of Gitea Platform
• Member of Gin Golang Framework
• Teacher of Udemy Platform: Golang + Drone
https://blog.wu-boy.com
Agenda
• Go in Mediatek
• Go Project Layout
• Go Practices
• RESTful api and GraphQL
• Model testing (Postgres, SQLite, MySQL)
• Software Quality
• Data Metrics
• Go Testing
Tech Stack
• Initial Project using Go in 2018/01
• Golang
• Easy to Learn
• Performance
• Deployment
Repository folder
• api
• assets
• cmd
• configs
• docker
• pkg
├── api
├── assets
│   └── dist
├── cmd
│   └── ggz
├── configs
├── docker
│   ├── server
└── pkg
├── config
├── errors
├── fixtures
├── helper
├── middleware
│   ├── auth
│   └── header
├── model
├── module
│   ├── mailer
│   ├── metrics
│   └── storage
├── router
│   └── routes
├── schema
└── version
Root folder
• .drone.yml (deploy config)
• .revive.toml (golint config)
• docker-compose.yml (DB, Redis and UI)
• Makefile
• go module config (go.mod and go.sum)
• .env.example
Go Module
https://blog.golang.org/using-go-modules
Improve Deployment
Using Go Module Proxy
https://github.com/gomods/athens
save time
with proxy
97s -> 6s
Makefile
Build, Testing, Deploy
GOFMT ?= gofmt "-s"
GO ?= go
TARGETS ?= linux darwin windows
ARCHS ?= amd64 386
BUILD_DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
GOFILES := $(shell find . -name "*.go" -type f)
TAGS ?= sqlite sqlite_unlock_notify
ifneq ($(shell uname), Darwin)
  EXTLDFLAGS = -extldflags "-static" $(null)
else
  EXTLDFLAGS =
endif
ifneq ($(DRONE_TAG),)
  VERSION ?= $(subst v,,$(DRONE_TAG))
else
  VERSION ?= $(shell git describe --tags --always')
endif
.env
GGZ_DB_DRIVER=mysql
GGZ_DB_USERNAME=root
GGZ_DB_PASSWORD=123456
GGZ_DB_NAME=ggz
GGZ_DB_HOST=127.0.0.1:3307
GGZ_SERVER_ADDR=:8080
GGZ_DEBUG=true
GGZ_SERVER_HOST=http://localhost:8080
GGZ_STORAGE_DRIVER=disk
GGZ_MINIO_ACCESS_ID=xxxxxxxx
GGZ_MINIO_SECRET_KEY=xxxxxxxx
GGZ_MINIO_ENDPOINT=s3.example.com
GGZ_MINIO_BUCKET=example
GGZ_MINIO_SSL=true
GGZ_AUTH0_DEBUG=true
docker-compose.yml
db:
image: mysql
restart: always
volumes:
- mysql-data:/var/lib/mysql
environment:
MYSQL_USER: example
MYSQL_PASSWORD: example
MYSQL_DATABASE: example
MYSQL_ROOT_PASSWORD: example
minio:
image: minio/minio
restart: always
ports:
volumes:
- minio-data:/data
environment:
MINIO_ACCESS_KEY: minio123456
MINIO_SECRET_KEY: minio123456
command: server /data
Development
Productionapi:
image: foo/bar
restart: always
ports:
- 8080:8080
environment:
- GGZ_METRICS_TOKEN=test-prometheus-token
- GGZ_METRICS_ENABLED=true
labels:
- "traefik.enable=true"
- "traefik.basic.frontend.rule=Host:${WEB_HOST}"
- "traefik.basic.protocol=http"
VersionCompile version info into Go binary
Version
• -X github.com/go-ggz/ggz/pkg/
version.Version=$(VERSION)
• -X github.com/go-ggz/ggz/pkg/
version.BuildDate=$(BUILD_DATE)
go build -o bin/api -ldflags
var (
  // Version number for git tag.
  Version string
  // BuildDate is the ISO 8601 day drone was built.
  BuildDate string
)
// PrintCLIVersion print server info
func PrintCLIVersion() string {
  return fmt.Sprintf(
    "version %s, built on %s, %s",
    Version,
    BuildDate,
    runtime.Version(),
  )
}
BUILD_DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
ifneq ($(DRONE_TAG),)
  VERSION ?= $(subst v,,$(DRONE_TAG))
else
  VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed
's/^v//')
endif
AssetsEmbed files in Go
https://github.com/UnnoTed/fileb0x
func ReadSource(origPath string) (content []byte, err error) {
  content, err = ReadFile(origPath)
  if err != nil {
    log.Warn().Err(err).Msgf("Failed to read builtin %s file.", origPath)
  }
  if config.Server.Assets != "" && file.IsDir(config.Server.Assets) {
    origPath = path.Join(config.Server.Assets, origPath)
    if file.IsFile(origPath) {
      content, err = ioutil.ReadFile(origPath)
      if err != nil {
        log.Warn().Err(err).Msgf("Failed to read custom %s file", origPath)
      }
    }
  }
  return content, err
}
Debug Setting
// ViewHandler support dist handler from UI
func ViewHandler() gin.HandlerFunc {
  fileServer := http.FileServer(dist.HTTP)
  data := []byte(time.Now().String())
  etag := fmt.Sprintf("%x", md5.Sum(data))
  return func(c *gin.Context) {
    c.Header("Cache-Control", "public, max-age=31536000")
    c.Header("ETag", etag)
    if match := c.GetHeader("If-None-Match"); match != "" {
      if strings.Contains(match, etag) {
        c.Status(http.StatusNotModified)
        return
      }
    }
    fileServer.ServeHTTP(c.Writer, c.Request)
  }
}
File Server Handler
圖片來來源:https://developers.google.com/web/fundamentals/performance/optimizing-
content-efficiency/http-caching?hl=zh-tw
// Favicon represents the favicon.
func Favicon(c *gin.Context) {
  file, _ := dist.ReadFile("favicon.ico")
  etag := fmt.Sprintf("%x", md5.Sum(file))
  c.Header("ETag", etag)
  c.Header("Cache-Control", "max-age=0")
  if match := c.GetHeader("If-None-Match"); match != "" {
    if strings.Contains(match, etag) {
      c.Status(http.StatusNotModified)
      return
    }
  }
  c.Data(
    http.StatusOK,
    "image/x-icon",
    file,
  )
}
NO Cache
API
/healthz
• health check for load balancer
func Heartbeat(c *gin.Context) {
  c.AbortWithStatus(http.StatusOK)
  c.String(http.StatusOK, "ok")
}
CMDCommand line
Command line package
• Golang package: flag
• urfave/cli
• spf13/cobra
├── agent
│   ├── config
│   │   └── config.go
│   └── main.go
├── notify
│   └── main.go
└── tcp-server
├── config
│   └── config.go
└── main.go
Config
Management
github.com/spf13/viper
Config management
• Load config from File
• .json
• .ini
• Load config from Environment Variables
• .env
  var envfile string
flag.StringVar(&envfile, "env-file", ".env", "Read in a file of environment
variables")
  flag.Parse()
  godotenv.Load(envfile)
_ "github.com/joho/godotenv/autoload"
  Logging struct {
    Debug bool `envconfig:"GGZ_LOGS_DEBUG"`
    Level string `envconfig:"GGZ_LOGS_LEVEL" default:"info"`
    Color bool `envconfig:"GGZ_LOGS_COLOR"`
    Pretty bool `envconfig:"GGZ_LOGS_PRETTY"`
    Text bool `envconfig:"GGZ_LOGS_TEXT"`
  }
  // Server provides the server configuration.
  Server struct {
    Addr string `envconfig:"GGZ_SERVER_ADDR"`
    Port string `envconfig:"GGZ_SERVER_PORT" default:"12000"`
    Path string `envconfig:”GGZ_SERVER_PATH" default:"data"`
  }
github.com/kelseyhightower/envconfig
  config, err := config.Environ()
  if err != nil {
    log.Fatal().
      Err(err).
      Msg("invalid configuration")
  }
  initLogging(config)
  // check folder exist
  if !file.IsDir(config.Server.Path) {
    log.Fatal().
      Str("path", config.Server.Path).
      Msg("log folder not found")
  }
Load env from structure
/configs
Configuration file templates or default config
global:
scrape_interval: 5s
external_labels:
monitor: 'my-monitor'
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'ggz-server'
static_configs:
- targets: ['ggz-server:8080']
bearer_token: 'test-prometheus-token'
/docker
Docker file template
├── ggz-redirect
│ ├── Dockerfile.linux.amd64
│ ├── Dockerfile.linux.arm
│ ├── Dockerfile.linux.arm64
│ ├── Dockerfile.windows.amd64
│ └── manifest.tmpl
└── ggz-server
├── Dockerfile.linux.amd64
├── Dockerfile.linux.arm
├── Dockerfile.linux.arm64
├── Dockerfile.windows.amd64
└── manifest.tmpl
/integrations
  ctx := context.Background()
  req := testcontainers.ContainerRequest{
    Image: "goggz/ggz-server",
    ExposedPorts: []string{"8080/tcp"},
    WaitingFor: wait.ForLog("Starting shorten server on :8080")
  }
  ggzServer, err := testcontainers.GenericContainer(
    ctx,
    testcontainers.GenericContainerRequest{
      ContainerRequest: req,
      Started: true,
    })
  if err != nil {
    t.Fatal(err)
  }
github.com/testcontainers/testcontainers-go
/pkg
├── config
├── errors
├── fixtures
├── helper
├── middleware
│ ├── auth
│ └── header
├── model
├── module
│ ├── metrics
│ └── storage
│ ├── disk
│ └── minio
├── router
│ └── routes
├── schema
└── version
/pkg/errors
// Type defines the type of an error
type Type string
const (
  // Internal error
  Internal Type = "internal"
  // NotFound error means that a specific item does not exis
  NotFound Type = "not_found"
  // BadRequest error
  BadRequest Type = "bad_request"
  // Validation error
  Validation Type = "validation"
  // AlreadyExists error
  AlreadyExists Type = "already_exists"
  // Unauthorized error
  Unauthorized Type = "unauthorized"
)
// ENotExists creates an error of type NotExist
func ENotExists(msg string, err error, arg ...interface{}) error {
  return New(NotFound, fmt.Sprintf(msg, arg...), err)
}
// EBadRequest creates an error of type BadRequest
func EBadRequest(msg string, err error, arg ...interface{}) error {
  return New(BadRequest, fmt.Sprintf(msg, arg...), err)
}
// EAlreadyExists creates an error of type AlreadyExists
func EAlreadyExists(msg string, err error, arg ...interface{}) error {
  return New(AlreadyExists, fmt.Sprintf(msg, arg...), err)
}
/pkg/fixtures
Rails-like test fixtures
Write tests against a real database
github.com/go-testfixtures/testfixtures
fixtures/
posts.yml
comments.yml
tags.yml
posts_tags.yml
users.yml
-
id: 1
email: test@gmail.com
full_name: test
avatar: http://example.com
avatar_email: test@gmail.com
-
id: 2
email: test1234@gmail.com
full_name: test1234
avatar: http://example.com
avatar_email: test1234@gmail.com
Unit Testing with Database
func TestMain(m *testing.M) {
// test program to do extra
setup or teardown before or after
testing.
os.Exit(m.Run())
}
https://golang.org/pkg/testing/#hdr-Main
func MainTest(m *testing.M, pathToRoot string) {
  var err error
  fixturesDir := filepath.Join(pathToRoot, "pkg", "fixtures")
  if err = createTestEngine(fixturesDir); err != nil {
    fatalTestError("Error creating test engine: %vn", err)
  }
  os.Exit(m.Run())
}
func createTestEngine(fixturesDir string) error {
  var err error
  x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
  if err != nil {
    return err
  }
  x.ShowSQL(config.Server.Debug)
  return InitFixtures(&testfixtures.SQLite{}, fixturesDir)
}
Testing with SQLite
func TestIsUserExist(t *testing.T) {
  assert.NoError(t, PrepareTestDatabase())
  exists, err := IsUserExist(0, "test@gmail.com")
  assert.NoError(t, err)
  assert.True(t, exists)
  exists, err = IsUserExist(0, "test123456@gmail.com")
  assert.NoError(t, err)
  assert.False(t, exists)
  exists, err = IsUserExist(1, "test1234@gmail.com")
  assert.NoError(t, err)
  assert.True(t, exists)
  exists, err = IsUserExist(1, "test123456@gmail.com")
  assert.NoError(t, err)
  assert.False(t, exists)
} go test -v -run=TestIsUserExist ./pkg/models/
/pkg/helper
Helper func
• Encrypt and Decrypt
• Regexp func
• IsEmail, IsUsername
• Zipfile
/pkg/middleware
func Secure(c *gin.Context) {
  c.Header("Access-Control-Allow-Origin", "*")
  c.Header("X-Frame-Options", "DENY")
  c.Header("X-Content-Type-Options", "nosniff")
  c.Header("X-XSS-Protection", "1; mode=block")
  if c.Request.TLS != nil {
    c.Header("Strict-Transport-Security", "max-age=31536000")
  }
}
/pkg/model
Use gorm or xorm
Build in
SQLite3
// +build sqlite
package model
import (
  _ "github.com/mattn/go-sqlite3"
)
func init() {
  EnableSQLite3 = true
}
go build -v -tags 'sqlite sqlite_unlock_notify'
/pkg/module
├── cron
├── download
├── jwt
├── ldap
├── mailer
│ ├── ses
│ └── smtp
├── queue
├── metrics
├── redis
└── storage
├── disk
└── minio
Integration with
Prometheus + Grafana
func NewCollector() Collector {
  return Collector{
    Users: prometheus.NewDesc(
      namespace+"users",
      "Number of Users",
      nil, nil,
    ),
}
// Collect returns the metrics with values
func (c Collector) Collect(ch chan<- prometheus.Metric) {
  stats := model.GetStatistic()
  ch <- prometheus.MustNewConstMetric(
    c.Users,
    prometheus.GaugeValue,
    float64(stats.Counter.User),
  )
}
Prometheus Handler
func Metrics(token string) gin.HandlerFunc {
  h := promhttp.Handler()
  return func(c *gin.Context) {
    if token == "" {
      h.ServeHTTP(c.Writer, c.Request)
      return
    }
    header := c.Request.Header.Get("Authorization")
    if header == "" {
      c.String(http.StatusUnauthorized, errInvalidToken.Error())
      return
    }
    bearer := fmt.Sprintf("Bearer %s", token)
    if header != bearer {
      c.String(http.StatusUnauthorized, errInvalidToken.Error())
      return
    }
    h.ServeHTTP(c.Writer, c.Request)
  }
}
    c := metrics.NewCollector()
    prometheus.MustRegister(c)
    if config.Metrics.Enabled {
      root.GET("/metrics", router.Metrics(config.Metrics.Token))
    }
Your prometheus token
/pkg/schema
RESTful vs GraphQL
See the Slide: GraphQL in Go
var rootQuery = graphql.NewObject(
  graphql.ObjectConfig{
    Name: "RootQuery",
    Description: "Root Query",
    Fields: graphql.Fields{
      "queryShortenURL": &queryShortenURL,
      "queryMe": &queryMe,
    },
  })
var rootMutation = graphql.NewObject(
  graphql.ObjectConfig{
    Name: "RootMutation",
    Description: "Root Mutation",
    Fields: graphql.Fields{
      "createUser": &createUser,
    },
  })
// Schema is the GraphQL schema served by the server.
var Schema, _ = graphql.NewSchema(
  graphql.SchemaConfig{
    Query: rootQuery,
    Mutation: rootMutation,
  })
Write the GraphQL Testing
  assert.NoError(t, model.PrepareTestDatabase())
  t.Run("user not login", func(t *testing.T) {
    test := T{
      Query: `{
queryMe {
email
}
}`,
      Schema: Schema,
      Expected: &graphql.Result{
        Data: map[string]interface{}{
          "queryMe": nil,
        },
        Errors: []gqlerrors.FormattedError{
          {
            Message: errorYouAreNotLogin,
          },
        },
      },
    }
  })
}
Best Practice
Testing your Go code
Testable Code
• Code Quality
• Readability
• Maintainability
• Testability
#1. Testing in Go
func TestFooBar(t *testing.T) {}
func ExampleFooBar(t *testing.T) {}
func BenchmarkFooBar(b *testing.B) {}
go test package_name
#2. Benchmark Testing
Profiling: CPU, Memory, Goroutine Block
func BenchmarkPlaylyfeGraphQLMaster(b *testing.B) {
  for i := 0; i < b.N; i++ {
    context := map[string]interface{}{}
    variables := map[string]interface{}{}
    playlyfeExecutor.Execute(context, "{hello}", variables, "")
  }
}
func BenchmarkGophersGraphQLMaster(b *testing.B) {
  for i := 0; i < b.N; i++ {
    ctx := context.Background()
    variables := map[string]interface{}{}
    gopherSchema.Exec(ctx, "{hello}", "", variables)
  }
} http://bit.ly/2L0CG3Q
#3. Example Testing
Examples on how to use your code
func ExampleFooBar() {
  fmt.Println(strings.Compare("a", "b"))
  fmt.Println(strings.Compare("a", "a"))
  fmt.Println(strings.Compare("b", "a"))
  // Output:
  // -1
  // 0
  // 1
}
$ go test -v -tags=sqlite -run=ExampleFooBar ./pkg/model/...
=== RUN ExampleFooBar
--- PASS: ExampleFooBar (0.00s)
PASS
ok github.com/go-ggz/ggz/pkg/model 0.022s
#4. Subtests in Testing Package
func (t *T) Run(name string, f func(t *T)) bool {}
func (b *B) Run(name string, f func(b *B)) bool {}
  tests := []struct {
    name string
    fields fields
    args args
  }{}
  for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
      c := Collector{
        Shortens: tt.fields.Shortens,
        Users: tt.fields.Users,
      }
      c.Describe(tt.args.ch)
    })
  }
}
#5. Skipping Testing
t.Skip()
package metrics
import (
  "os"
  "testing"
)
func TestSkip(t *testing.T) {
  if os.Getenv("DEBUG_MODE") == "true" {
    t.Skipf("test skipped")
  }
}
#6. Running Tests in Parallel
Speedup your CI/CD Flow
t.Parallel()
func TestFooBar01(t *testing.T) {
  t.Parallel()
  time.Sleep(time.Second)
}
func TestFooBar02(t *testing.T) {
  t.Parallel()
  time.Sleep(time.Second * 2)
}
func TestFooBar03(t *testing.T) {
  t.Parallel()
  time.Sleep(time.Second * 3)
}
Just only use
one package
github.com/stretchr/testify
https://www.udemy.com/course/golang-fight/?couponCode=GOLANG2019
https://www.udemy.com/course/devops-oneday/?couponCode=DRONE2019
END

More Related Content

What's hot

eBPF Basics
eBPF BasicseBPF Basics
eBPF Basics
Michael Kehoe
 
Q4.11: Porting Android to new Platforms
Q4.11: Porting Android to new PlatformsQ4.11: Porting Android to new Platforms
Q4.11: Porting Android to new Platforms
Linaro
 
Anatomy of a Container: Namespaces, cgroups & Some Filesystem Magic - LinuxCon
Anatomy of a Container: Namespaces, cgroups & Some Filesystem Magic - LinuxConAnatomy of a Container: Namespaces, cgroups & Some Filesystem Magic - LinuxCon
Anatomy of a Container: Namespaces, cgroups & Some Filesystem Magic - LinuxCon
Jérôme Petazzoni
 
Android binder introduction
Android binder introductionAndroid binder introduction
Android binder introduction
Derek Fang
 
Learning AOSP - Android Linux Device Driver
Learning AOSP - Android Linux Device DriverLearning AOSP - Android Linux Device Driver
Learning AOSP - Android Linux Device Driver
Nanik Tolaram
 
Android Security Internals
Android Security InternalsAndroid Security Internals
Android Security Internals
Opersys inc.
 
Dpdk performance
Dpdk performanceDpdk performance
Dpdk performance
Stephen Hemminger
 
BPF Internals (eBPF)
BPF Internals (eBPF)BPF Internals (eBPF)
BPF Internals (eBPF)
Brendan Gregg
 
Using and Customizing the Android Framework / part 4 of Embedded Android Work...
Using and Customizing the Android Framework / part 4 of Embedded Android Work...Using and Customizing the Android Framework / part 4 of Embedded Android Work...
Using and Customizing the Android Framework / part 4 of Embedded Android Work...Opersys inc.
 
[DockerCon 2019] Hardening Docker daemon with Rootless mode
[DockerCon 2019] Hardening Docker daemon with Rootless mode[DockerCon 2019] Hardening Docker daemon with Rootless mode
[DockerCon 2019] Hardening Docker daemon with Rootless mode
Akihiro Suda
 
eBPF Perf Tools 2019
eBPF Perf Tools 2019eBPF Perf Tools 2019
eBPF Perf Tools 2019
Brendan Gregg
 
BPF - in-kernel virtual machine
BPF - in-kernel virtual machineBPF - in-kernel virtual machine
BPF - in-kernel virtual machine
Alexei Starovoitov
 
Performance Wins with eBPF: Getting Started (2021)
Performance Wins with eBPF: Getting Started (2021)Performance Wins with eBPF: Getting Started (2021)
Performance Wins with eBPF: Getting Started (2021)
Brendan Gregg
 
F9: A Secure and Efficient Microkernel Built for Deeply Embedded Systems
F9: A Secure and Efficient Microkernel Built for Deeply Embedded SystemsF9: A Secure and Efficient Microkernel Built for Deeply Embedded Systems
F9: A Secure and Efficient Microkernel Built for Deeply Embedded Systems
National Cheng Kung University
 
Meet cute-between-ebpf-and-tracing
Meet cute-between-ebpf-and-tracingMeet cute-between-ebpf-and-tracing
Meet cute-between-ebpf-and-tracing
Viller Hsiao
 
Hardware accelerated Virtualization in the ARM Cortex™ Processors
Hardware accelerated Virtualization in the ARM Cortex™ ProcessorsHardware accelerated Virtualization in the ARM Cortex™ Processors
Hardware accelerated Virtualization in the ARM Cortex™ Processors
The Linux Foundation
 
Anatomy of the loadable kernel module (lkm)
Anatomy of the loadable kernel module (lkm)Anatomy of the loadable kernel module (lkm)
Anatomy of the loadable kernel module (lkm)
Adrian Huang
 
Embedded Android : System Development - Part I
Embedded Android : System Development - Part IEmbedded Android : System Development - Part I
Embedded Android : System Development - Part I
Emertxe Information Technologies Pvt Ltd
 
Scheduling in Android
Scheduling in AndroidScheduling in Android
Scheduling in Android
Opersys inc.
 
ZgPHP 97 - Microservice architecture in Laravel
ZgPHP 97 - Microservice architecture in LaravelZgPHP 97 - Microservice architecture in Laravel
ZgPHP 97 - Microservice architecture in Laravel
Frano Šašvari
 

What's hot (20)

eBPF Basics
eBPF BasicseBPF Basics
eBPF Basics
 
Q4.11: Porting Android to new Platforms
Q4.11: Porting Android to new PlatformsQ4.11: Porting Android to new Platforms
Q4.11: Porting Android to new Platforms
 
Anatomy of a Container: Namespaces, cgroups & Some Filesystem Magic - LinuxCon
Anatomy of a Container: Namespaces, cgroups & Some Filesystem Magic - LinuxConAnatomy of a Container: Namespaces, cgroups & Some Filesystem Magic - LinuxCon
Anatomy of a Container: Namespaces, cgroups & Some Filesystem Magic - LinuxCon
 
Android binder introduction
Android binder introductionAndroid binder introduction
Android binder introduction
 
Learning AOSP - Android Linux Device Driver
Learning AOSP - Android Linux Device DriverLearning AOSP - Android Linux Device Driver
Learning AOSP - Android Linux Device Driver
 
Android Security Internals
Android Security InternalsAndroid Security Internals
Android Security Internals
 
Dpdk performance
Dpdk performanceDpdk performance
Dpdk performance
 
BPF Internals (eBPF)
BPF Internals (eBPF)BPF Internals (eBPF)
BPF Internals (eBPF)
 
Using and Customizing the Android Framework / part 4 of Embedded Android Work...
Using and Customizing the Android Framework / part 4 of Embedded Android Work...Using and Customizing the Android Framework / part 4 of Embedded Android Work...
Using and Customizing the Android Framework / part 4 of Embedded Android Work...
 
[DockerCon 2019] Hardening Docker daemon with Rootless mode
[DockerCon 2019] Hardening Docker daemon with Rootless mode[DockerCon 2019] Hardening Docker daemon with Rootless mode
[DockerCon 2019] Hardening Docker daemon with Rootless mode
 
eBPF Perf Tools 2019
eBPF Perf Tools 2019eBPF Perf Tools 2019
eBPF Perf Tools 2019
 
BPF - in-kernel virtual machine
BPF - in-kernel virtual machineBPF - in-kernel virtual machine
BPF - in-kernel virtual machine
 
Performance Wins with eBPF: Getting Started (2021)
Performance Wins with eBPF: Getting Started (2021)Performance Wins with eBPF: Getting Started (2021)
Performance Wins with eBPF: Getting Started (2021)
 
F9: A Secure and Efficient Microkernel Built for Deeply Embedded Systems
F9: A Secure and Efficient Microkernel Built for Deeply Embedded SystemsF9: A Secure and Efficient Microkernel Built for Deeply Embedded Systems
F9: A Secure and Efficient Microkernel Built for Deeply Embedded Systems
 
Meet cute-between-ebpf-and-tracing
Meet cute-between-ebpf-and-tracingMeet cute-between-ebpf-and-tracing
Meet cute-between-ebpf-and-tracing
 
Hardware accelerated Virtualization in the ARM Cortex™ Processors
Hardware accelerated Virtualization in the ARM Cortex™ ProcessorsHardware accelerated Virtualization in the ARM Cortex™ Processors
Hardware accelerated Virtualization in the ARM Cortex™ Processors
 
Anatomy of the loadable kernel module (lkm)
Anatomy of the loadable kernel module (lkm)Anatomy of the loadable kernel module (lkm)
Anatomy of the loadable kernel module (lkm)
 
Embedded Android : System Development - Part I
Embedded Android : System Development - Part IEmbedded Android : System Development - Part I
Embedded Android : System Development - Part I
 
Scheduling in Android
Scheduling in AndroidScheduling in Android
Scheduling in Android
 
ZgPHP 97 - Microservice architecture in Laravel
ZgPHP 97 - Microservice architecture in LaravelZgPHP 97 - Microservice architecture in Laravel
ZgPHP 97 - Microservice architecture in Laravel
 

Similar to Golang Project Layout and Practice

[EXTENDED] Ceph, Docker, Heroku Slugs, CoreOS and Deis Overview
[EXTENDED] Ceph, Docker, Heroku Slugs, CoreOS and Deis Overview[EXTENDED] Ceph, Docker, Heroku Slugs, CoreOS and Deis Overview
[EXTENDED] Ceph, Docker, Heroku Slugs, CoreOS and Deis Overview
Leo Lorieri
 
Let Grunt do the work, focus on the fun!
Let Grunt do the work, focus on the fun!Let Grunt do the work, focus on the fun!
Let Grunt do the work, focus on the fun!
Dirk Ginader
 
Delivering Go.CD with Terraform and Docker
Delivering Go.CD with Terraform and DockerDelivering Go.CD with Terraform and Docker
Delivering Go.CD with Terraform and Docker
Jorrit Salverda
 
Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018
Ortus Solutions, Corp
 
Into The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and dockerInto The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and docker
Ortus Solutions, Corp
 
如何透過 Go-kit 快速搭建微服務架構應用程式實戰
如何透過 Go-kit 快速搭建微服務架構應用程式實戰如何透過 Go-kit 快速搭建微服務架構應用程式實戰
如何透過 Go-kit 快速搭建微服務架構應用程式實戰
KAI CHU CHUNG
 
Go 1.10 Release Party - PDX Go
Go 1.10 Release Party - PDX GoGo 1.10 Release Party - PDX Go
Go 1.10 Release Party - PDX Go
Rodolfo Carvalho
 
Bangpypers april-meetup-2012
Bangpypers april-meetup-2012Bangpypers april-meetup-2012
Bangpypers april-meetup-2012
Deepak Garg
 
SF Grails - Ratpack - Compact Groovy Webapps - James Williams
SF Grails - Ratpack - Compact Groovy Webapps - James WilliamsSF Grails - Ratpack - Compact Groovy Webapps - James Williams
SF Grails - Ratpack - Compact Groovy Webapps - James Williams
Philip Stehlik
 
Google App Engine: Basic
Google App Engine: BasicGoogle App Engine: Basic
Google App Engine: Basic
KAI CHU CHUNG
 
I Just Want to Run My Code: Waypoint, Nomad, and Other Things
I Just Want to Run My Code: Waypoint, Nomad, and Other ThingsI Just Want to Run My Code: Waypoint, Nomad, and Other Things
I Just Want to Run My Code: Waypoint, Nomad, and Other Things
Michael Lange
 
The Fairy Tale of the One Command Build Script
The Fairy Tale of the One Command Build ScriptThe Fairy Tale of the One Command Build Script
The Fairy Tale of the One Command Build Script
Docker, Inc.
 
FP - Découverte de Play Framework Scala
FP - Découverte de Play Framework ScalaFP - Découverte de Play Framework Scala
FP - Découverte de Play Framework Scala
Kévin Margueritte
 
Developing with-devstack
Developing with-devstackDeveloping with-devstack
Developing with-devstack
Deepak Garg
 
betterCode Workshop: Effizientes DevOps-Tooling mit Go
betterCode Workshop:  Effizientes DevOps-Tooling mit GobetterCode Workshop:  Effizientes DevOps-Tooling mit Go
betterCode Workshop: Effizientes DevOps-Tooling mit Go
QAware GmbH
 
Dev ninja -> vagrant + virtualbox + chef-solo + git + ec2
Dev ninja  -> vagrant + virtualbox + chef-solo + git + ec2Dev ninja  -> vagrant + virtualbox + chef-solo + git + ec2
Dev ninja -> vagrant + virtualbox + chef-solo + git + ec2
Yros
 
Toolbox of a Ruby Team
Toolbox of a Ruby TeamToolbox of a Ruby Team
Toolbox of a Ruby Team
Arto Artnik
 
Настройка окружения для кросскомпиляции проектов на основе docker'a
Настройка окружения для кросскомпиляции проектов на основе docker'aНастройка окружения для кросскомпиляции проектов на основе docker'a
Настройка окружения для кросскомпиляции проектов на основе docker'a
corehard_by
 
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
Dirk Ginader
 
GDG Cloud Taipei meetup #50 - Build go kit microservices at kubernetes with ...
GDG Cloud Taipei meetup #50 - Build go kit microservices at kubernetes  with ...GDG Cloud Taipei meetup #50 - Build go kit microservices at kubernetes  with ...
GDG Cloud Taipei meetup #50 - Build go kit microservices at kubernetes with ...
KAI CHU CHUNG
 

Similar to Golang Project Layout and Practice (20)

[EXTENDED] Ceph, Docker, Heroku Slugs, CoreOS and Deis Overview
[EXTENDED] Ceph, Docker, Heroku Slugs, CoreOS and Deis Overview[EXTENDED] Ceph, Docker, Heroku Slugs, CoreOS and Deis Overview
[EXTENDED] Ceph, Docker, Heroku Slugs, CoreOS and Deis Overview
 
Let Grunt do the work, focus on the fun!
Let Grunt do the work, focus on the fun!Let Grunt do the work, focus on the fun!
Let Grunt do the work, focus on the fun!
 
Delivering Go.CD with Terraform and Docker
Delivering Go.CD with Terraform and DockerDelivering Go.CD with Terraform and Docker
Delivering Go.CD with Terraform and Docker
 
Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018
 
Into The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and dockerInto The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and docker
 
如何透過 Go-kit 快速搭建微服務架構應用程式實戰
如何透過 Go-kit 快速搭建微服務架構應用程式實戰如何透過 Go-kit 快速搭建微服務架構應用程式實戰
如何透過 Go-kit 快速搭建微服務架構應用程式實戰
 
Go 1.10 Release Party - PDX Go
Go 1.10 Release Party - PDX GoGo 1.10 Release Party - PDX Go
Go 1.10 Release Party - PDX Go
 
Bangpypers april-meetup-2012
Bangpypers april-meetup-2012Bangpypers april-meetup-2012
Bangpypers april-meetup-2012
 
SF Grails - Ratpack - Compact Groovy Webapps - James Williams
SF Grails - Ratpack - Compact Groovy Webapps - James WilliamsSF Grails - Ratpack - Compact Groovy Webapps - James Williams
SF Grails - Ratpack - Compact Groovy Webapps - James Williams
 
Google App Engine: Basic
Google App Engine: BasicGoogle App Engine: Basic
Google App Engine: Basic
 
I Just Want to Run My Code: Waypoint, Nomad, and Other Things
I Just Want to Run My Code: Waypoint, Nomad, and Other ThingsI Just Want to Run My Code: Waypoint, Nomad, and Other Things
I Just Want to Run My Code: Waypoint, Nomad, and Other Things
 
The Fairy Tale of the One Command Build Script
The Fairy Tale of the One Command Build ScriptThe Fairy Tale of the One Command Build Script
The Fairy Tale of the One Command Build Script
 
FP - Découverte de Play Framework Scala
FP - Découverte de Play Framework ScalaFP - Découverte de Play Framework Scala
FP - Découverte de Play Framework Scala
 
Developing with-devstack
Developing with-devstackDeveloping with-devstack
Developing with-devstack
 
betterCode Workshop: Effizientes DevOps-Tooling mit Go
betterCode Workshop:  Effizientes DevOps-Tooling mit GobetterCode Workshop:  Effizientes DevOps-Tooling mit Go
betterCode Workshop: Effizientes DevOps-Tooling mit Go
 
Dev ninja -> vagrant + virtualbox + chef-solo + git + ec2
Dev ninja  -> vagrant + virtualbox + chef-solo + git + ec2Dev ninja  -> vagrant + virtualbox + chef-solo + git + ec2
Dev ninja -> vagrant + virtualbox + chef-solo + git + ec2
 
Toolbox of a Ruby Team
Toolbox of a Ruby TeamToolbox of a Ruby Team
Toolbox of a Ruby Team
 
Настройка окружения для кросскомпиляции проектов на основе docker'a
Настройка окружения для кросскомпиляции проектов на основе docker'aНастройка окружения для кросскомпиляции проектов на основе docker'a
Настройка окружения для кросскомпиляции проектов на основе docker'a
 
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
 
GDG Cloud Taipei meetup #50 - Build go kit microservices at kubernetes with ...
GDG Cloud Taipei meetup #50 - Build go kit microservices at kubernetes  with ...GDG Cloud Taipei meetup #50 - Build go kit microservices at kubernetes  with ...
GDG Cloud Taipei meetup #50 - Build go kit microservices at kubernetes with ...
 

More from Bo-Yi Wu

Drone CI/CD 自動化測試及部署
Drone CI/CD 自動化測試及部署Drone CI/CD 自動化測試及部署
Drone CI/CD 自動化測試及部署
Bo-Yi Wu
 
用 Go 語言打造多台機器 Scale 架構
用 Go 語言打造多台機器 Scale 架構用 Go 語言打造多台機器 Scale 架構
用 Go 語言打造多台機器 Scale 架構
Bo-Yi Wu
 
Job Queue in Golang
Job Queue in GolangJob Queue in Golang
Job Queue in Golang
Bo-Yi Wu
 
Introduction to GitHub Actions
Introduction to GitHub ActionsIntroduction to GitHub Actions
Introduction to GitHub Actions
Bo-Yi Wu
 
Drone 1.0 Feature
Drone 1.0 FeatureDrone 1.0 Feature
Drone 1.0 Feature
Bo-Yi Wu
 
Drone CI/CD Platform
Drone CI/CD PlatformDrone CI/CD Platform
Drone CI/CD Platform
Bo-Yi Wu
 
GraphQL IN Golang
GraphQL IN GolangGraphQL IN Golang
GraphQL IN Golang
Bo-Yi Wu
 
Go 語言基礎簡介
Go 語言基礎簡介Go 語言基礎簡介
Go 語言基礎簡介
Bo-Yi Wu
 
drone continuous Integration
drone continuous Integrationdrone continuous Integration
drone continuous Integration
Bo-Yi Wu
 
Gorush: A push notification server written in Go
Gorush: A push notification server written in GoGorush: A push notification server written in Go
Gorush: A push notification server written in Go
Bo-Yi Wu
 
用 Drone 打造 輕量級容器持續交付平台
用 Drone 打造輕量級容器持續交付平台用 Drone 打造輕量級容器持續交付平台
用 Drone 打造 輕量級容器持續交付平台
Bo-Yi Wu
 
用 Go 語言 打造微服務架構
用 Go 語言打造微服務架構用 Go 語言打造微服務架構
用 Go 語言 打造微服務架構
Bo-Yi Wu
 
Introduction to Gitea with Drone
Introduction to Gitea with DroneIntroduction to Gitea with Drone
Introduction to Gitea with Drone
Bo-Yi Wu
 
運用 Docker 整合 Laravel 提升團隊開發效率
運用 Docker 整合 Laravel 提升團隊開發效率運用 Docker 整合 Laravel 提升團隊開發效率
運用 Docker 整合 Laravel 提升團隊開發效率
Bo-Yi Wu
 
用 Go 語言實戰 Push Notification 服務
用 Go 語言實戰 Push Notification 服務用 Go 語言實戰 Push Notification 服務
用 Go 語言實戰 Push Notification 服務
Bo-Yi Wu
 
用 Go 語言打造 DevOps Bot
用 Go 語言打造 DevOps Bot用 Go 語言打造 DevOps Bot
用 Go 語言打造 DevOps Bot
Bo-Yi Wu
 
A painless self-hosted Git service: Gitea
A painless self-hosted Git service: GiteaA painless self-hosted Git service: Gitea
A painless self-hosted Git service: Gitea
Bo-Yi Wu
 
Write microservice in golang
Write microservice in golangWrite microservice in golang
Write microservice in golang
Bo-Yi Wu
 
用 Docker 改善團隊合作模式
用 Docker 改善團隊合作模式用 Docker 改善團隊合作模式
用 Docker 改善團隊合作模式
Bo-Yi Wu
 
Git flow 與團隊合作
Git flow 與團隊合作Git flow 與團隊合作
Git flow 與團隊合作
Bo-Yi Wu
 

More from Bo-Yi Wu (20)

Drone CI/CD 自動化測試及部署
Drone CI/CD 自動化測試及部署Drone CI/CD 自動化測試及部署
Drone CI/CD 自動化測試及部署
 
用 Go 語言打造多台機器 Scale 架構
用 Go 語言打造多台機器 Scale 架構用 Go 語言打造多台機器 Scale 架構
用 Go 語言打造多台機器 Scale 架構
 
Job Queue in Golang
Job Queue in GolangJob Queue in Golang
Job Queue in Golang
 
Introduction to GitHub Actions
Introduction to GitHub ActionsIntroduction to GitHub Actions
Introduction to GitHub Actions
 
Drone 1.0 Feature
Drone 1.0 FeatureDrone 1.0 Feature
Drone 1.0 Feature
 
Drone CI/CD Platform
Drone CI/CD PlatformDrone CI/CD Platform
Drone CI/CD Platform
 
GraphQL IN Golang
GraphQL IN GolangGraphQL IN Golang
GraphQL IN Golang
 
Go 語言基礎簡介
Go 語言基礎簡介Go 語言基礎簡介
Go 語言基礎簡介
 
drone continuous Integration
drone continuous Integrationdrone continuous Integration
drone continuous Integration
 
Gorush: A push notification server written in Go
Gorush: A push notification server written in GoGorush: A push notification server written in Go
Gorush: A push notification server written in Go
 
用 Drone 打造 輕量級容器持續交付平台
用 Drone 打造輕量級容器持續交付平台用 Drone 打造輕量級容器持續交付平台
用 Drone 打造 輕量級容器持續交付平台
 
用 Go 語言 打造微服務架構
用 Go 語言打造微服務架構用 Go 語言打造微服務架構
用 Go 語言 打造微服務架構
 
Introduction to Gitea with Drone
Introduction to Gitea with DroneIntroduction to Gitea with Drone
Introduction to Gitea with Drone
 
運用 Docker 整合 Laravel 提升團隊開發效率
運用 Docker 整合 Laravel 提升團隊開發效率運用 Docker 整合 Laravel 提升團隊開發效率
運用 Docker 整合 Laravel 提升團隊開發效率
 
用 Go 語言實戰 Push Notification 服務
用 Go 語言實戰 Push Notification 服務用 Go 語言實戰 Push Notification 服務
用 Go 語言實戰 Push Notification 服務
 
用 Go 語言打造 DevOps Bot
用 Go 語言打造 DevOps Bot用 Go 語言打造 DevOps Bot
用 Go 語言打造 DevOps Bot
 
A painless self-hosted Git service: Gitea
A painless self-hosted Git service: GiteaA painless self-hosted Git service: Gitea
A painless self-hosted Git service: Gitea
 
Write microservice in golang
Write microservice in golangWrite microservice in golang
Write microservice in golang
 
用 Docker 改善團隊合作模式
用 Docker 改善團隊合作模式用 Docker 改善團隊合作模式
用 Docker 改善團隊合作模式
 
Git flow 與團隊合作
Git flow 與團隊合作Git flow 與團隊合作
Git flow 與團隊合作
 

Recently uploaded

UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
DianaGray10
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
Elena Simperl
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
Safe Software
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
Alan Dix
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
DianaGray10
 
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
UiPathCommunity
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Tobias Schneck
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
Cheryl Hung
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
Product School
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Product School
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
Abida Shariff
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
James Anderson
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
Jemma Hussein Allen
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
Frank van Harmelen
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
Bhaskar Mitra
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Inflectra
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 

Recently uploaded (20)

UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 

Golang Project Layout and Practice

  • 1. Go Project Layout and Practice Bo-Yi Wu 2019.08.29 ModernWeb
  • 2. About me • Software Engineer in Mediatek • Member of Drone CI/CD Platform • Member of Gitea Platform • Member of Gin Golang Framework • Teacher of Udemy Platform: Golang + Drone https://blog.wu-boy.com
  • 3. Agenda • Go in Mediatek • Go Project Layout • Go Practices • RESTful api and GraphQL • Model testing (Postgres, SQLite, MySQL) • Software Quality • Data Metrics • Go Testing
  • 4. Tech Stack • Initial Project using Go in 2018/01 • Golang • Easy to Learn • Performance • Deployment
  • 5. Repository folder • api • assets • cmd • configs • docker • pkg ├── api ├── assets │   └── dist ├── cmd │   └── ggz ├── configs ├── docker │   ├── server └── pkg ├── config ├── errors ├── fixtures ├── helper ├── middleware │   ├── auth │   └── header ├── model ├── module │   ├── mailer │   ├── metrics │   └── storage ├── router │   └── routes ├── schema └── version
  • 6. Root folder • .drone.yml (deploy config) • .revive.toml (golint config) • docker-compose.yml (DB, Redis and UI) • Makefile • go module config (go.mod and go.sum) • .env.example
  • 8. Improve Deployment Using Go Module Proxy https://github.com/gomods/athens
  • 11. GOFMT ?= gofmt "-s" GO ?= go TARGETS ?= linux darwin windows ARCHS ?= amd64 386 BUILD_DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") GOFILES := $(shell find . -name "*.go" -type f) TAGS ?= sqlite sqlite_unlock_notify ifneq ($(shell uname), Darwin)   EXTLDFLAGS = -extldflags "-static" $(null) else   EXTLDFLAGS = endif ifneq ($(DRONE_TAG),)   VERSION ?= $(subst v,,$(DRONE_TAG)) else   VERSION ?= $(shell git describe --tags --always') endif
  • 12. .env
  • 15. db: image: mysql restart: always volumes: - mysql-data:/var/lib/mysql environment: MYSQL_USER: example MYSQL_PASSWORD: example MYSQL_DATABASE: example MYSQL_ROOT_PASSWORD: example minio: image: minio/minio restart: always ports: volumes: - minio-data:/data environment: MINIO_ACCESS_KEY: minio123456 MINIO_SECRET_KEY: minio123456 command: server /data Development
  • 16. Productionapi: image: foo/bar restart: always ports: - 8080:8080 environment: - GGZ_METRICS_TOKEN=test-prometheus-token - GGZ_METRICS_ENABLED=true labels: - "traefik.enable=true" - "traefik.basic.frontend.rule=Host:${WEB_HOST}" - "traefik.basic.protocol=http"
  • 17. VersionCompile version info into Go binary
  • 18. Version • -X github.com/go-ggz/ggz/pkg/ version.Version=$(VERSION) • -X github.com/go-ggz/ggz/pkg/ version.BuildDate=$(BUILD_DATE) go build -o bin/api -ldflags
  • 19. var (   // Version number for git tag.   Version string   // BuildDate is the ISO 8601 day drone was built.   BuildDate string ) // PrintCLIVersion print server info func PrintCLIVersion() string {   return fmt.Sprintf(     "version %s, built on %s, %s",     Version,     BuildDate,     runtime.Version(),   ) }
  • 20. BUILD_DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") ifneq ($(DRONE_TAG),)   VERSION ?= $(subst v,,$(DRONE_TAG)) else   VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//') endif
  • 21. AssetsEmbed files in Go https://github.com/UnnoTed/fileb0x
  • 22. func ReadSource(origPath string) (content []byte, err error) {   content, err = ReadFile(origPath)   if err != nil {     log.Warn().Err(err).Msgf("Failed to read builtin %s file.", origPath)   }   if config.Server.Assets != "" && file.IsDir(config.Server.Assets) {     origPath = path.Join(config.Server.Assets, origPath)     if file.IsFile(origPath) {       content, err = ioutil.ReadFile(origPath)       if err != nil {         log.Warn().Err(err).Msgf("Failed to read custom %s file", origPath)       }     }   }   return content, err } Debug Setting
  • 23. // ViewHandler support dist handler from UI func ViewHandler() gin.HandlerFunc {   fileServer := http.FileServer(dist.HTTP)   data := []byte(time.Now().String())   etag := fmt.Sprintf("%x", md5.Sum(data))   return func(c *gin.Context) {     c.Header("Cache-Control", "public, max-age=31536000")     c.Header("ETag", etag)     if match := c.GetHeader("If-None-Match"); match != "" {       if strings.Contains(match, etag) {         c.Status(http.StatusNotModified)         return       }     }     fileServer.ServeHTTP(c.Writer, c.Request)   } } File Server Handler
  • 25. // Favicon represents the favicon. func Favicon(c *gin.Context) {   file, _ := dist.ReadFile("favicon.ico")   etag := fmt.Sprintf("%x", md5.Sum(file))   c.Header("ETag", etag)   c.Header("Cache-Control", "max-age=0")   if match := c.GetHeader("If-None-Match"); match != "" {     if strings.Contains(match, etag) {       c.Status(http.StatusNotModified)       return     }   }   c.Data(     http.StatusOK,     "image/x-icon",     file,   ) } NO Cache
  • 26. API
  • 27. /healthz • health check for load balancer func Heartbeat(c *gin.Context) {   c.AbortWithStatus(http.StatusOK)   c.String(http.StatusOK, "ok") }
  • 29. Command line package • Golang package: flag • urfave/cli • spf13/cobra
  • 30. ├── agent │   ├── config │   │   └── config.go │   └── main.go ├── notify │   └── main.go └── tcp-server ├── config │   └── config.go └── main.go
  • 32. Config management • Load config from File • .json • .ini • Load config from Environment Variables • .env
  • 33.   var envfile string flag.StringVar(&envfile, "env-file", ".env", "Read in a file of environment variables")   flag.Parse()   godotenv.Load(envfile) _ "github.com/joho/godotenv/autoload"
  • 34.   Logging struct {     Debug bool `envconfig:"GGZ_LOGS_DEBUG"`     Level string `envconfig:"GGZ_LOGS_LEVEL" default:"info"`     Color bool `envconfig:"GGZ_LOGS_COLOR"`     Pretty bool `envconfig:"GGZ_LOGS_PRETTY"`     Text bool `envconfig:"GGZ_LOGS_TEXT"`   }   // Server provides the server configuration.   Server struct {     Addr string `envconfig:"GGZ_SERVER_ADDR"`     Port string `envconfig:"GGZ_SERVER_PORT" default:"12000"`     Path string `envconfig:”GGZ_SERVER_PATH" default:"data"`   } github.com/kelseyhightower/envconfig
  • 35.   config, err := config.Environ()   if err != nil {     log.Fatal().       Err(err).       Msg("invalid configuration")   }   initLogging(config)   // check folder exist   if !file.IsDir(config.Server.Path) {     log.Fatal().       Str("path", config.Server.Path).       Msg("log folder not found")   } Load env from structure
  • 37. global: scrape_interval: 5s external_labels: monitor: 'my-monitor' scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - job_name: 'ggz-server' static_configs: - targets: ['ggz-server:8080'] bearer_token: 'test-prometheus-token'
  • 39. ├── ggz-redirect │ ├── Dockerfile.linux.amd64 │ ├── Dockerfile.linux.arm │ ├── Dockerfile.linux.arm64 │ ├── Dockerfile.windows.amd64 │ └── manifest.tmpl └── ggz-server ├── Dockerfile.linux.amd64 ├── Dockerfile.linux.arm ├── Dockerfile.linux.arm64 ├── Dockerfile.windows.amd64 └── manifest.tmpl
  • 41.   ctx := context.Background()   req := testcontainers.ContainerRequest{     Image: "goggz/ggz-server",     ExposedPorts: []string{"8080/tcp"},     WaitingFor: wait.ForLog("Starting shorten server on :8080")   }   ggzServer, err := testcontainers.GenericContainer(     ctx,     testcontainers.GenericContainerRequest{       ContainerRequest: req,       Started: true,     })   if err != nil {     t.Fatal(err)   } github.com/testcontainers/testcontainers-go
  • 42. /pkg
  • 43. ├── config ├── errors ├── fixtures ├── helper ├── middleware │ ├── auth │ └── header ├── model ├── module │ ├── metrics │ └── storage │ ├── disk │ └── minio ├── router │ └── routes ├── schema └── version
  • 45. // Type defines the type of an error type Type string const (   // Internal error   Internal Type = "internal"   // NotFound error means that a specific item does not exis   NotFound Type = "not_found"   // BadRequest error   BadRequest Type = "bad_request"   // Validation error   Validation Type = "validation"   // AlreadyExists error   AlreadyExists Type = "already_exists"   // Unauthorized error   Unauthorized Type = "unauthorized" )
  • 46. // ENotExists creates an error of type NotExist func ENotExists(msg string, err error, arg ...interface{}) error {   return New(NotFound, fmt.Sprintf(msg, arg...), err) } // EBadRequest creates an error of type BadRequest func EBadRequest(msg string, err error, arg ...interface{}) error {   return New(BadRequest, fmt.Sprintf(msg, arg...), err) } // EAlreadyExists creates an error of type AlreadyExists func EAlreadyExists(msg string, err error, arg ...interface{}) error {   return New(AlreadyExists, fmt.Sprintf(msg, arg...), err) }
  • 48. Rails-like test fixtures Write tests against a real database github.com/go-testfixtures/testfixtures
  • 50. - id: 1 email: test@gmail.com full_name: test avatar: http://example.com avatar_email: test@gmail.com - id: 2 email: test1234@gmail.com full_name: test1234 avatar: http://example.com avatar_email: test1234@gmail.com
  • 51. Unit Testing with Database
  • 52. func TestMain(m *testing.M) { // test program to do extra setup or teardown before or after testing. os.Exit(m.Run()) } https://golang.org/pkg/testing/#hdr-Main
  • 53. func MainTest(m *testing.M, pathToRoot string) {   var err error   fixturesDir := filepath.Join(pathToRoot, "pkg", "fixtures")   if err = createTestEngine(fixturesDir); err != nil {     fatalTestError("Error creating test engine: %vn", err)   }   os.Exit(m.Run()) } func createTestEngine(fixturesDir string) error {   var err error   x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")   if err != nil {     return err   }   x.ShowSQL(config.Server.Debug)   return InitFixtures(&testfixtures.SQLite{}, fixturesDir) } Testing with SQLite
  • 54. func TestIsUserExist(t *testing.T) {   assert.NoError(t, PrepareTestDatabase())   exists, err := IsUserExist(0, "test@gmail.com")   assert.NoError(t, err)   assert.True(t, exists)   exists, err = IsUserExist(0, "test123456@gmail.com")   assert.NoError(t, err)   assert.False(t, exists)   exists, err = IsUserExist(1, "test1234@gmail.com")   assert.NoError(t, err)   assert.True(t, exists)   exists, err = IsUserExist(1, "test123456@gmail.com")   assert.NoError(t, err)   assert.False(t, exists) } go test -v -run=TestIsUserExist ./pkg/models/
  • 56. Helper func • Encrypt and Decrypt • Regexp func • IsEmail, IsUsername • Zipfile
  • 58. func Secure(c *gin.Context) {   c.Header("Access-Control-Allow-Origin", "*")   c.Header("X-Frame-Options", "DENY")   c.Header("X-Content-Type-Options", "nosniff")   c.Header("X-XSS-Protection", "1; mode=block")   if c.Request.TLS != nil {     c.Header("Strict-Transport-Security", "max-age=31536000")   } }
  • 60. Use gorm or xorm
  • 62. // +build sqlite package model import (   _ "github.com/mattn/go-sqlite3" ) func init() {   EnableSQLite3 = true } go build -v -tags 'sqlite sqlite_unlock_notify'
  • 64. ├── cron ├── download ├── jwt ├── ldap ├── mailer │ ├── ses │ └── smtp ├── queue ├── metrics ├── redis └── storage ├── disk └── minio
  • 66. func NewCollector() Collector {   return Collector{     Users: prometheus.NewDesc(       namespace+"users",       "Number of Users",       nil, nil,     ), } // Collect returns the metrics with values func (c Collector) Collect(ch chan<- prometheus.Metric) {   stats := model.GetStatistic()   ch <- prometheus.MustNewConstMetric(     c.Users,     prometheus.GaugeValue,     float64(stats.Counter.User),   ) }
  • 68. func Metrics(token string) gin.HandlerFunc {   h := promhttp.Handler()   return func(c *gin.Context) {     if token == "" {       h.ServeHTTP(c.Writer, c.Request)       return     }     header := c.Request.Header.Get("Authorization")     if header == "" {       c.String(http.StatusUnauthorized, errInvalidToken.Error())       return     }     bearer := fmt.Sprintf("Bearer %s", token)     if header != bearer {       c.String(http.StatusUnauthorized, errInvalidToken.Error())       return     }     h.ServeHTTP(c.Writer, c.Request)   } }
  • 69.     c := metrics.NewCollector()     prometheus.MustRegister(c)     if config.Metrics.Enabled {       root.GET("/metrics", router.Metrics(config.Metrics.Token))     } Your prometheus token
  • 71. RESTful vs GraphQL See the Slide: GraphQL in Go
  • 72. var rootQuery = graphql.NewObject(   graphql.ObjectConfig{     Name: "RootQuery",     Description: "Root Query",     Fields: graphql.Fields{       "queryShortenURL": &queryShortenURL,       "queryMe": &queryMe,     },   }) var rootMutation = graphql.NewObject(   graphql.ObjectConfig{     Name: "RootMutation",     Description: "Root Mutation",     Fields: graphql.Fields{       "createUser": &createUser,     },   }) // Schema is the GraphQL schema served by the server. var Schema, _ = graphql.NewSchema(   graphql.SchemaConfig{     Query: rootQuery,     Mutation: rootMutation,   })
  • 73. Write the GraphQL Testing
  • 74.   assert.NoError(t, model.PrepareTestDatabase())   t.Run("user not login", func(t *testing.T) {     test := T{       Query: `{ queryMe { email } }`,       Schema: Schema,       Expected: &graphql.Result{         Data: map[string]interface{}{           "queryMe": nil,         },         Errors: []gqlerrors.FormattedError{           {             Message: errorYouAreNotLogin,           },         },       },     }   }) }
  • 76.
  • 77. Testable Code • Code Quality • Readability • Maintainability • Testability
  • 78. #1. Testing in Go func TestFooBar(t *testing.T) {} func ExampleFooBar(t *testing.T) {} func BenchmarkFooBar(b *testing.B) {} go test package_name
  • 79. #2. Benchmark Testing Profiling: CPU, Memory, Goroutine Block
  • 80. func BenchmarkPlaylyfeGraphQLMaster(b *testing.B) {   for i := 0; i < b.N; i++ {     context := map[string]interface{}{}     variables := map[string]interface{}{}     playlyfeExecutor.Execute(context, "{hello}", variables, "")   } } func BenchmarkGophersGraphQLMaster(b *testing.B) {   for i := 0; i < b.N; i++ {     ctx := context.Background()     variables := map[string]interface{}{}     gopherSchema.Exec(ctx, "{hello}", "", variables)   } } http://bit.ly/2L0CG3Q
  • 81.
  • 82. #3. Example Testing Examples on how to use your code
  • 83. func ExampleFooBar() {   fmt.Println(strings.Compare("a", "b"))   fmt.Println(strings.Compare("a", "a"))   fmt.Println(strings.Compare("b", "a"))   // Output:   // -1   // 0   // 1 }
  • 84. $ go test -v -tags=sqlite -run=ExampleFooBar ./pkg/model/... === RUN ExampleFooBar --- PASS: ExampleFooBar (0.00s) PASS ok github.com/go-ggz/ggz/pkg/model 0.022s
  • 85. #4. Subtests in Testing Package func (t *T) Run(name string, f func(t *T)) bool {} func (b *B) Run(name string, f func(b *B)) bool {}
  • 86.   tests := []struct {     name string     fields fields     args args   }{}   for _, tt := range tests {     t.Run(tt.name, func(t *testing.T) {       c := Collector{         Shortens: tt.fields.Shortens,         Users: tt.fields.Users,       }       c.Describe(tt.args.ch)     })   } }
  • 88. package metrics import (   "os"   "testing" ) func TestSkip(t *testing.T) {   if os.Getenv("DEBUG_MODE") == "true" {     t.Skipf("test skipped")   } }
  • 89. #6. Running Tests in Parallel Speedup your CI/CD Flow t.Parallel()
  • 90. func TestFooBar01(t *testing.T) {   t.Parallel()   time.Sleep(time.Second) } func TestFooBar02(t *testing.T) {   t.Parallel()   time.Sleep(time.Second * 2) } func TestFooBar03(t *testing.T) {   t.Parallel()   time.Sleep(time.Second * 3) }
  • 91. Just only use one package github.com/stretchr/testify
  • 94. END