Repository: grpc/grpc-go
File: credentials/tls/certprovider/pemfile/watcher.go (Go)

Code before:
	// CertFile is the file that holds the identity certificate.
	// Optional. If this is set, KeyFile must also be set.
	CertFile string
	// KeyFile is the file that holds identity private key.
	// Optional. If this is set, CertFile must also be set.
	KeyFile string
	// RootFile is the file that holds trusted root certificate(s).
	// Optional.
	RootFile string
	// SPIFFEBundleMapFile is the file that holds the spiffe bundle map.
	// If a given provider configures both the RootFile and the
	// SPIFFEBundleMapFile, the SPIFFEBundleMapFile will be preferred.
	// Optional.
	SPIFFEBundleMapFile string
	// RefreshDuration is the amount of time the plugin waits before checking
	// for updates in the specified files.
	// Optional. If not set, a default value (1 hour) will be used.
	RefreshDuration time.Duration
}

func (o Options) canonical() []byte {
	return []byte(fmt.Sprintf("%s:%s:%s:%s:%s", o.CertFile, o.KeyFile, o.RootFile, o.SPIFFEBundleMapFile, o.RefreshDuration))
}

func (o Options) validate() error {
	// Guard against SPIFFE bundle map usage
	if !envconfig.XDSSpiffeEnabled {
		o.SPIFFEBundleMapFile = ""
	}
	if o.CertFile == "" && o.KeyFile == "" && o.RootFile == "" && o.SPIFFEBundleMapFile == "" {
		return fmt.Errorf("pemfile: at least one credential file needs to be specified")
	}
	if keySpecified, certSpecified := o.KeyFile != "", o.CertFile != ""; keySpecified != certSpecified {
		return fmt.Errorf("pemfile: private key file and identity cert file should be both specified or not specified")
	}
	// C-core has a limitation that they cannot verify that a certificate file
	// matches a key file. So, the only way to get around this is to make sure
	// that both files are in the same directory and that they do an atomic
	// read. Even though Java/Go do not have this limitation, we want the
	// overall plugin behavior to be consistent across languages.
	if certDir, keyDir := filepath.Dir(o.CertFile), filepath.Dir(o.KeyFile); certDir != keyDir {
		return errors.New("pemfile: certificate and key file must be in the same directory")
	}
	return nil
}

// NewProvider returns a new certificate provider plugin that is configured to
// watch the PEM files specified in the passed in options.
func NewProvider(o Options) (certprovider.Provider, error) {
	if err := o.validate(); err != nil {
		return nil, err

Review comment (suggestion):
My reading of this sentence from the gRFC is that we don't even read the new field `spiffe_trust_bundle_map_file` from the bootstrap. So, this might need to be handled in `certprovider/pemfile/builder.go` in `pluginConfigFromJSON` instead of here. Because by the time we get here, we have already read the field from the bootstrap JSON.

"""
The xDS functionality will be guarded via the GRPC_EXPERIMENTAL_XDS_MTLS_SPIFFE environment variable. The new bootstrap field will not be read if this env var is unset. This env var guard will be removed when the feature has passed interop tests.
"""



Code after:
	// Optional. If this is set, KeyFile must also be set.
	CertFile string
	// KeyFile is the file that holds identity private key.
	// Optional. If this is set, CertFile must also be set.
	KeyFile string
	// RootFile is the file that holds trusted root certificate(s).
	// Optional.
	RootFile string
	// SPIFFEBundleMapFile is the file that holds the spiffe bundle map.
	// If a given provider configures both the RootFile and the
	// SPIFFEBundleMapFile, the SPIFFEBundleMapFile will be preferred.
	// Optional.
	SPIFFEBundleMapFile string
	// RefreshDuration is the amount of time the plugin waits before checking
	// for updates in the specified files.
	// Optional. If not set, a default value (1 hour) will be used.
	RefreshDuration time.Duration
}

func (o Options) canonical() []byte {
	return []byte(fmt.Sprintf("%s:%s:%s:%s:%s", o.CertFile, o.KeyFile, o.RootFile, o.SPIFFEBundleMapFile, o.RefreshDuration))
}

func (o Options) validate() error {
	if o.CertFile == "" && o.KeyFile == "" && o.RootFile == "" && o.SPIFFEBundleMapFile == "" {
		return fmt.Errorf("pemfile: at least one credential file needs to be specified")
	}
	if keySpecified, certSpecified := o.KeyFile != "", o.CertFile != ""; keySpecified != certSpecified {
		return fmt.Errorf("pemfile: private key file and identity cert file should be both specified or not specified")
	}
	// C-core has a limitation that they cannot verify that a certificate file
	// matches a key file. So, the only way to get around this is to make sure
	// that both files are in the same directory and that they do an atomic
	// read. Even though Java/Go do not have this limitation, we want the
	// overall plugin behavior to be consistent across languages.
	if certDir, keyDir := filepath.Dir(o.CertFile), filepath.Dir(o.KeyFile); certDir != keyDir {
		return errors.New("pemfile: certificate and key file must be in the same directory")
	}
	return nil
}

// NewProvider returns a new certificate provider plugin that is configured to
// watch the PEM files specified in the passed in options.
func NewProvider(o Options) (certprovider.Provider, error) {
	if err := o.validate(); err != nil {
		return nil, err
	}
	return newProvider(o), nil
}

// newProvider is used to create a new certificate provider plugin after