这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

基础代码的源码学习

Dapr 基础代码的源码学习

基础代码是 Dapr 代码中最基础的部分,这些代码已经是 dapr 自身逻辑的组成部分,但处于比较偏底层,也不是 dapr 的主要链路,通常代码量也不大。

基础代码在依赖关系中位于工具类代码和类库类代码之上。

1 - version的源码学习

Dapr version package的源码学习

代码实现

version 的代码超级简单,就一个 version.go,内容也只有一点点:

// Values for these are injected by the build.
var (
   version = "edge"
   commit  string
)

// Version returns the Dapr version. This is either a semantic version
// number or else, in the case of unreleased code, the string "edge".
func Version() string {
   return version
}

// Commit returns the git commit SHA for the code that Dapr was built from.
func Commit() string {
   return commit
}
  • version:要不就是语义话版本,比如 1.0.0 这种,要不就是 edge 表示未发布的代码
  • commit:build的时候的 git commit

如何注入

Values for these are injected by the build.

那是怎么注入的呢? Build 总不能调用代码,而且这两个值也是private。

Dapr 下的 Makefile 文件中:

# git rev-list -1 HEAD 得到的 git commit 的 hash 值
# 如:63147334aa246d76f9f65708c257460567a1cff4
GIT_COMMIT  = $(shell git rev-list -1 HEAD)
# git describe --always --abbrev=7 --dirty 得到的是版本信息
# 如:v1.0.0-rc.4-5-g6314733
GIT_VERSION = $(shell git describe --always --abbrev=7 --dirty)

ifdef REL_VERSION
   DAPR_VERSION := $(REL_VERSION)
else
   DAPR_VERSION := edge
endif

BASE_PACKAGE_NAME := github.com/dapr/dapr

DEFAULT_LDFLAGS:=-X $(BASE_PACKAGE_NAME)/pkg/version.commit=$(GIT_VERSION) -X $(BASE_PACKAGE_NAME)/pkg/version.version=$(DAPR_VERSION)

ifeq ($(origin DEBUG), undefined)
  BUILDTYPE_DIR:=release
  LDFLAGS:="$(DEFAULT_LDFLAGS) -s -w"
else ifeq ($(DEBUG),0)
  BUILDTYPE_DIR:=release
  LDFLAGS:="$(DEFAULT_LDFLAGS) -s -w"
else
  BUILDTYPE_DIR:=debug
  GCFLAGS:=-gcflags="all=-N -l"
  LDFLAGS:="$(DEFAULT_LDFLAGS)"
  $(info Build with debugger information)
endif

define genBinariesForTarget
.PHONY: $(5)/$(1)
$(5)/$(1):
	CGO_ENABLED=$(CGO) GOOS=$(3) GOARCH=$(4) go build $(GCFLAGS) -ldflags=$(LDFLAGS) \
	-o $(5)/$(1) $(2)/;
endef

TODO:没看懂,有时间详细研究一下这个makefile。

2 - modes的源码学习

Dapr modes package的源码学习

代码实现

modes 的代码超级简单,就一个 modes.go,内容也只有一点点:

// DaprMode is the runtime mode for Dapr.
type DaprMode string

const (
	// KubernetesMode is a Kubernetes Dapr mode
	KubernetesMode DaprMode = "kubernetes"
	// StandaloneMode is a Standalone Dapr mode
	StandaloneMode DaprMode = "standalone"
)

Dapr有两种运行模式

  • kubernetes 模式
  • standalone 模式

运行模式的总结

两种模式的差异:

  1. 配置文件读取的方式:

    • standalone 模式下读取本地文件,文件路径由命令行参数 config 指定。
    • kubernetes 模式下读取k8s中存储的CRD,CRD的名称由命令行参数 config 指定。
    config := flag.String("config", "", "Path to config file, or name of a configuration object")
    
  2. TODO

3 - cors的源码学习

Dapr cors package的源码学习

代码实现

cors 的代码超级简单,就一个 cors.go,内容也只有一点点:

// DefaultAllowedOrigins is the default origins allowed for the Dapr HTTP servers
const DefaultAllowedOrigins = "*"

AllowedOrigins配置的读取

AllowedOrigins 配置在启动时通过命令行参数 allowed-origins 传入,默认值为 DefaultAllowedOrigins ("*")。然后传入给 NewRuntimeConfig()方法:

func FromFlags() (*DaprRuntime, error) {
allowedOrigins := flag.String("allowed-origins", cors.DefaultAllowedOrigins, "Allowed HTTP origins")

	runtimeConfig := NewRuntimeConfig(*appID, placementAddresses, *controlPlaneAddress, *allowedOrigins ......)
}

之后保存在 NewRuntimeConfig 的 AllowedOrigins 字段中:

func NewRuntimeConfig(
   id string, placementAddresses []string,
   controlPlaneAddress, allowedOrigins ......) *Config {
   return &Config{
   	AllowedOrigins:      allowedOrigins,
   	......
   }

AllowedOrigins配置的使用

pkg/http/server.go 的 useCors() 方法:

func (s *server) useCors(next fasthttp.RequestHandler) fasthttp.RequestHandler {
   if s.config.AllowedOrigins == cors_dapr.DefaultAllowedOrigins {
      return next
   }

   log.Infof("enabled cors http middleware")
   origins := strings.Split(s.config.AllowedOrigins, ",")
   corsHandler := s.getCorsHandler(origins)
   return corsHandler.CorsMiddleware(next)
}

4 - proto的源码学习

Dapr proto package的源码学习

5 - config的源码学习

Dapr config package的源码学习

6 - credentials的源码学习

Dapr credentials package的源码学习

6.1 - certchain.go的源码学习

credentials 结构体持有证书相关的各种 path

Dapr credentials package中的 certchain.go 文件的源码学习,credentials 结构体持有证书相关的各种 path。

CertChain 结构体定义

CertChain 结构体持有证书信任链的PEM值:

// CertChain holds the certificate trust chain PEM values
type CertChain struct {
	RootCA []byte
	Cert   []byte
	Key    []byte
}

装载证书的LoadFromDisk 方法

LoadFromDisk 方法从给定目录中读取 CertChain:

// LoadFromDisk retruns a CertChain from a given directory
func LoadFromDisk(rootCertPath, issuerCertPath, issuerKeyPath string) (*CertChain, error) {
   rootCert, err := ioutil.ReadFile(rootCertPath)
   if err != nil {
      return nil, err
   }
   cert, err := ioutil.ReadFile(issuerCertPath)
   if err != nil {
      return nil, err
   }
   key, err := ioutil.ReadFile(issuerKeyPath)
   if err != nil {
      return nil, err
   }
   return &CertChain{
      RootCA: rootCert,
      Cert:   cert,
      Key:    key,
   }, nil
}

使用场景

placement 的 main.go 中,如果 mTLS 开启了,则会读取 tls 证书:

func loadCertChains(certChainPath string) *credentials.CertChain {
   tlsCreds := credentials.NewTLSCredentials(certChainPath)

   log.Info("mTLS enabled, getting tls certificates")
   // try to load certs from disk, if not yet there, start a watch on the local filesystem
   chain, err := credentials.LoadFromDisk(tlsCreds.RootCertPath(), tlsCreds.CertPath(), tlsCreds.KeyPath())
	......
}

operator 的 operator.go 中,也会判断,如果 MTLSEnabled :

var certChain *credentials.CertChain
if o.config.MTLSEnabled {
   log.Info("mTLS enabled, getting tls certificates")
   // try to load certs from disk, if not yet there, start a watch on the local filesystem
   chain, err := credentials.LoadFromDisk(o.config.Credentials.RootCertPath(), o.config.Credentials.CertPath(), o.config.Credentials.KeyPath())
   ......
}

备注:上面两段代码重复度极高,最好能重构一下。

sentry 中也有调用:

func (c *defaultCA) validateAndBuildTrustBundle() (*trustRootBundle, error) {
	var (
		issuerCreds     *certs.Credentials
		rootCertBytes   []byte
		issuerCertBytes []byte
	)

	// certs exist on disk or getting created, load them when ready
	if !shouldCreateCerts(c.config) {
		err := detectCertificates(c.config.RootCertPath)
		if err != nil {
			return nil, err
		}

		certChain, err := credentials.LoadFromDisk(c.config.RootCertPath, c.config.IssuerCertPath, c.config.IssuerKeyPath)
		if err != nil {
			return nil, errors.Wrap(err, "error loading cert chain from disk")
		}

TODO: 证书相关的细节后面单独细看。

6.2 - credentials.go的源码学习

credentials 结构体持有证书相关的各种 path

Dapr credentials package中的 credentials.go文件的源码学习,credentials 结构体持有证书相关的各种 path。

TLSCredentials 结构体定义

只有一个字段 credentialsPath:

// TLSCredentials holds paths for credentials
type TLSCredentials struct {
   credentialsPath string
}

构造方法很简单:

// NewTLSCredentials returns a new TLSCredentials
func NewTLSCredentials(path string) TLSCredentials {
   return TLSCredentials{
      credentialsPath: path,
   }
}

获取相关 path 的方法

获取 credentialsPath,这个path中保存有 TLS 证书:

// Path returns the directory holding the TLS credentials
func (t *TLSCredentials) Path() string {
   return t.credentialsPath
}

分别获取 root cert / cert / cert key 的 path:

// RootCertPath returns the file path for the root cert
func (t *TLSCredentials) RootCertPath() string {
   return filepath.Join(t.credentialsPath, RootCertFilename)
}

// CertPath returns the file path for the cert
func (t *TLSCredentials) CertPath() string {
   return filepath.Join(t.credentialsPath, IssuerCertFilename)
}

// KeyPath returns the file path for the cert key
func (t *TLSCredentials) KeyPath() string {
   return filepath.Join(t.credentialsPath, IssuerKeyFilename)
}

6.3 - tls.go的源码学习

从 cert/key 中装载 tls.config 对象

Dapr credentials package中的 tls.go文件的源码学习,从 cert/key 中装载 tls.config 对象。

TLSConfigFromCertAndKey() 方法

TLSConfigFromCertAndKey() 方法从 PEM 格式中有效的 cert/key 对中返回 tls.config 对象:

// TLSConfigFromCertAndKey return a tls.config object from valid cert/key pair in PEM format.
func TLSConfigFromCertAndKey(certPem, keyPem []byte, serverName string, rootCA *x509.CertPool) (*tls.Config, error) {
	cert, err := tls.X509KeyPair(certPem, keyPem)
	if err != nil {
		return nil, err
	}

	// nolint:gosec
	config := &tls.Config{
		InsecureSkipVerify: false,
		RootCAs:            rootCA,
		ServerName:         serverName,
		Certificates:       []tls.Certificate{cert},
	}

	return config, nil
}

6.4 - grpc.go的源码学习

获取服务器端选项和客户端选项

Dapr credentials package中的 grpc.go文件的源码学习,获取服务器端选项和客户端选项。

GetServerOptions() 方法

func GetServerOptions(certChain *CertChain) ([]grpc.ServerOption, error) {
	opts := []grpc.ServerOption{}
	if certChain == nil {
		return opts, nil
	}

	cp := x509.NewCertPool()
	cp.AppendCertsFromPEM(certChain.RootCA)

	cert, err := tls.X509KeyPair(certChain.Cert, certChain.Key)
	if err != nil {
		return opts, nil
	}

	// nolint:gosec
	config := &tls.Config{
		ClientCAs: cp,
		// Require cert verification
		ClientAuth:   tls.RequireAndVerifyClientCert,
		Certificates: []tls.Certificate{cert},
	}
	opts = append(opts, grpc.Creds(credentials.NewTLS(config)))

	return opts, nil
}

GetClientOptions() 方法

func GetClientOptions(certChain *CertChain, serverName string) ([]grpc.DialOption, error) {
	opts := []grpc.DialOption{}
	if certChain != nil {
		cp := x509.NewCertPool()
		ok := cp.AppendCertsFromPEM(certChain.RootCA)
		if !ok {
			return nil, errors.New("failed to append PEM root cert to x509 CertPool")
		}
		config, err := TLSConfigFromCertAndKey(certChain.Cert, certChain.Key, serverName, cp)
		if err != nil {
			return nil, errors.Wrap(err, "failed to create tls config from cert and key")
		}
		opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(config)))
	} else {
		opts = append(opts, grpc.WithInsecure())
	}
	return opts, nil
}

TODO: 好吧,细节后面看,加密我不熟。