From 88cd5e941f686e0f37f35a2b5e3ef7ad88cf562d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20R=C3=BChl?= Date: Mon, 21 Aug 2023 09:43:38 +0200 Subject: [PATCH] refactor: make NewClient return an error --- .gitignore | 1 + client.go | 48 +++------ client_test.go | 7 +- config.go | 150 ++++++++++++++------------ config_test.go | 20 ++-- errors/errors.go | 5 + examples/accesslevel/accesslevel.go | 5 +- examples/browse/browse.go | 5 +- examples/crypto/crypto.go | 10 +- examples/datetime/datetime.go | 5 +- examples/history-read/history-read.go | 5 +- examples/method/method.go | 5 +- examples/monitor/monitor.go | 5 +- examples/read/read.go | 5 +- examples/regread/regread.go | 5 +- examples/subscribe/subscribe.go | 5 +- examples/translate/translate.go | 5 +- examples/trigger/trigger.go | 5 +- examples/udt/udt.go | 5 +- examples/write/write.go | 5 +- uatest/method_test.go | 5 +- uatest/namespace_test.go | 5 +- uatest/read_test.go | 5 +- uatest/read_unknow_node_id_test.go | 5 +- uatest/reconnection_test.go | 5 +- uatest/stats_test.go | 5 +- uatest/timeout_test.go | 10 +- uatest/write_test.go | 5 +- 28 files changed, 216 insertions(+), 135 deletions(-) diff --git a/.gitignore b/.gitignore index 6a9856f3..971ffc61 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build/ dist/ __pycache__/ .vscode/ +.idea/ diff --git a/client.go b/client.go index 17cadd21..cf69be3e 100644 --- a/client.go +++ b/client.go @@ -30,7 +30,10 @@ import ( // GetEndpoints returns the available endpoint descriptions for the server. func GetEndpoints(ctx context.Context, endpoint string, opts ...Option) ([]*ua.EndpointDescription, error) { opts = append(opts, AutoReconnect(false)) - c := NewClient(endpoint, opts...) + c, err := NewClient(endpoint, opts...) + if err != nil { + return nil, err + } if err := c.Dial(ctx); err != nil { return nil, err } @@ -133,15 +136,6 @@ type Client struct { // monitorOnce ensures only one connection monitor is running monitorOnce sync.Once - - // cfgerr contains an error that was captured in ApplyConfig. - // Since the API does not allow to bubble the error up in NewClient - // and we don't want to break existing code right away we carry the - // error here and bubble it up during Dial and Connect. - // - // Note: Starting with v0.5 NewClient will return the error and this - // variable needs to be removed. - cfgerr error } // NewClient creates a new Client. @@ -155,10 +149,11 @@ type Client struct { // #Option for details. // // https://godoc.org/github.com/gopcua/opcua#Option -// -// Note: Starting with v0.5 this function will return an error. -func NewClient(endpoint string, opts ...Option) *Client { - cfg := ApplyConfig(opts...) +func NewClient(endpoint string, opts ...Option) (*Client, error) { + cfg, err := ApplyConfig(opts...) + if err != nil { + return nil, err + } c := Client{ endpointURL: endpoint, cfg: cfg, @@ -167,7 +162,6 @@ func NewClient(endpoint string, opts ...Option) *Client { pendingAcks: make([]*ua.SubscriptionAcknowledgement, 0), pausech: make(chan struct{}, 2), resumech: make(chan struct{}, 2), - cfgerr: cfg.Error(), // todo(fs): remove with v0.5.0 and return the error } c.pauseSubscriptions(context.Background()) c.setPublishTimeout(uasc.MaxTimeout) @@ -175,7 +169,7 @@ func NewClient(endpoint string, opts ...Option) *Client { c.setSecureChannel(nil) c.setSession(nil) c.setNamespaces([]string{}) - return &c + return &c, nil } // reconnectAction is a list of actions for the client reconnection logic. @@ -194,11 +188,6 @@ const ( // Connect establishes a secure channel and creates a new session. func (c *Client) Connect(ctx context.Context) error { - // todo(fs): remove with v0.5.0 - if c.cfgerr != nil { - return c.cfgerr - } - // todo(fs): the secure channel is 'nil' during a re-connect // todo(fs): but we expect this method to be called once during startup // todo(fs): so this is probably safe @@ -503,16 +492,16 @@ func (c *Client) monitor(ctx context.Context) { // populated in the previous step. activeSubs = 0 - for _, id := range subsToRepublish { - if err := c.republishSubscription(ctx, id, availableSeqs[id]); err != nil { - dlog.Printf("republish of subscription %d failed", id) - subsToRecreate = append(subsToRecreate, id) + for _, subID := range subsToRepublish { + if err := c.republishSubscription(ctx, subID, availableSeqs[subID]); err != nil { + dlog.Printf("republish of subscription %d failed", subID) + subsToRecreate = append(subsToRecreate, subID) } activeSubs++ } - for _, id := range subsToRecreate { - if err := c.recreateSubscription(ctx, id); err != nil { + for _, subID := range subsToRecreate { + if err := c.recreateSubscription(ctx, subID); err != nil { dlog.Printf("recreate subscripitions failed: %v", err) action = recreateSession continue @@ -555,11 +544,6 @@ func (c *Client) monitor(ctx context.Context) { // Dial establishes a secure channel. func (c *Client) Dial(ctx context.Context) error { - // todo(fs): remove with v0.5.0 - if c.cfgerr != nil { - return c.cfgerr - } - stats.Client().Add("Dial", 1) if c.SecureChannel() != nil { diff --git a/client_test.go b/client_test.go index a1b12d82..b64ad50c 100644 --- a/client_test.go +++ b/client_test.go @@ -11,8 +11,11 @@ import ( ) func TestClient_Send_DoesNotPanicWhenDisconnected(t *testing.T) { - c := NewClient("opc.tcp://example.com:4840") - err := c.Send(context.Background(), &ua.ReadRequest{}, func(i interface{}) error { + c, err := NewClient("opc.tcp://example.com:4840") + if err != nil { + t.Fatal(err) + } + err = c.Send(context.Background(), &ua.ReadRequest{}, func(i interface{}) error { return nil }) verify.Values(t, "", err, ua.StatusBadServerNotConnected) diff --git a/config.go b/config.go index fd92a318..3b18ac51 100644 --- a/config.go +++ b/config.go @@ -56,18 +56,6 @@ type Config struct { dialer *uacp.Dialer sechan *uasc.Config session *uasc.SessionConfig - err error -} - -func (cfg *Config) setError(err error) { - if cfg.err != nil { - return - } - cfg.err = err -} - -func (cfg *Config) Error() error { - return cfg.err } // NewDialer creates a uacp.Dialer from the config options @@ -80,68 +68,76 @@ func NewDialer(cfg *Config) *uacp.Dialer { // ApplyConfig applies the config options to the default configuration. // todo(fs): Can we find a better name? -// -// Note: Starting with v0.5 this function will return an error. -func ApplyConfig(opts ...Option) *Config { +func ApplyConfig(opts ...Option) (*Config, error) { cfg := &Config{ sechan: DefaultClientConfig(), session: DefaultSessionConfig(), } + var foundErrors []error for _, opt := range opts { - opt(cfg) + if err := opt(cfg); err != nil { + foundErrors = append(foundErrors, err) + } } - return cfg + return cfg, errors.Join(foundErrors...) } // Option is an option function type to modify the configuration. -type Option func(*Config) +type Option func(*Config) error // ApplicationName sets the application name in the session configuration. func ApplicationName(s string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.session.ClientDescription.ApplicationName = ua.NewLocalizedText(s) + return nil } } // ApplicationURI sets the application uri in the session configuration. func ApplicationURI(s string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.session.ClientDescription.ApplicationURI = s + return nil } } // AutoReconnect sets the auto reconnect state of the secure channel. func AutoReconnect(b bool) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.sechan.AutoReconnect = b + return nil } } // ReconnectInterval is interval duration between each reconnection attempt. func ReconnectInterval(d time.Duration) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.sechan.ReconnectInterval = d + return nil } } // Lifetime sets the lifetime of the secure channel in milliseconds. func Lifetime(d time.Duration) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.sechan.Lifetime = uint32(d / time.Millisecond) + return nil } } // Locales sets the locales in the session configuration. func Locales(locale ...string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.session.LocaleIDs = locale + return nil } } // ProductURI sets the product uri in the session configuration. func ProductURI(s string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.session.ClientDescription.ProductURI = s + return nil } } @@ -154,95 +150,103 @@ var randomRequestID func() uint32 = nil // is the caller's responsibility to initialize the random number // generator properly. func RandomRequestID() Option { - return func(cfg *Config) { + return func(cfg *Config) error { if randomRequestID != nil { cfg.sechan.RequestIDSeed = randomRequestID() } else { cfg.sechan.RequestIDSeed = uint32(rand.Int31()) } + return nil } } // RemoteCertificate sets the server certificate. func RemoteCertificate(cert []byte) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.sechan.RemoteCertificate = cert + return nil } } // RemoteCertificateFile sets the server certificate from the file // in PEM or DER encoding. func RemoteCertificateFile(filename string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { if filename == "" { - return + return nil } cert, err := loadCertificate(filename) if err != nil { - cfg.setError(err) - return + return err } cfg.sechan.RemoteCertificate = cert + return nil } } // SecurityMode sets the security mode for the secure channel. func SecurityMode(m ua.MessageSecurityMode) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.sechan.SecurityMode = m + return nil } } // SecurityModeString sets the security mode for the secure channel. // Valid values are "None", "Sign", and "SignAndEncrypt". func SecurityModeString(s string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.sechan.SecurityMode = ua.MessageSecurityModeFromString(s) + return nil } } // SecurityPolicy sets the security policy uri for the secure channel. func SecurityPolicy(s string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.sechan.SecurityPolicyURI = ua.FormatSecurityPolicyURI(s) + return nil } } // SessionName sets the name in the session configuration. func SessionName(s string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.session.SessionName = s + return nil } } // SessionTimeout sets the timeout in the session configuration. func SessionTimeout(d time.Duration) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.session.SessionTimeout = d + return nil } } // PrivateKey sets the RSA private key in the secure channel configuration. func PrivateKey(key *rsa.PrivateKey) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.sechan.LocalKey = key + return nil } } // PrivateKeyFile sets the RSA private key in the secure channel configuration // from a PEM or DER encoded file. func PrivateKeyFile(filename string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { if filename == "" { - return + return nil } key, err := loadPrivateKey(filename) if err != nil { - cfg.setError(err) - return + return err } cfg.sechan.LocalKey = key + return nil } } @@ -271,26 +275,27 @@ func loadPrivateKey(filename string) (*rsa.PrivateKey, error) { // Certificate sets the client X509 certificate in the secure channel configuration. // It also detects and sets the ApplicationURI from the URI within the certificate. func Certificate(cert []byte) Option { - return func(cfg *Config) { + return func(cfg *Config) error { setCertificate(cert, cfg) + return nil } } -// Certificate sets the client X509 certificate in the secure channel configuration +// CertificateFile sets the client X509 certificate in the secure channel configuration // from the PEM or DER encoded file. It also detects and sets the ApplicationURI // from the URI within the certificate. func CertificateFile(filename string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { if filename == "" { - return + return nil } cert, err := loadCertificate(filename) if err != nil { - cfg.setError(err) - return + return err } setCertificate(cert, cfg) + return nil } } @@ -333,7 +338,7 @@ func setCertificate(cert []byte, cfg *Config) { // SecurityFromEndpoint sets the server-related security parameters from // a chosen endpoint (received from GetEndpoints()) func SecurityFromEndpoint(ep *ua.EndpointDescription, authType ua.UserTokenType) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.sechan.SecurityPolicyURI = ep.SecurityPolicyURI cfg.sechan.SecurityMode = ep.SecurityMode cfg.sechan.RemoteCertificate = ep.ServerCertificate @@ -359,13 +364,14 @@ func SecurityFromEndpoint(ep *ua.EndpointDescription, authType ua.UserTokenType) setPolicyID(cfg.session.UserIdentityToken, t.PolicyID) cfg.session.AuthPolicyURI = t.SecurityPolicyURI - return + return nil } if cfg.session.UserIdentityToken == nil { cfg.session.UserIdentityToken = &ua.AnonymousIdentityToken{PolicyID: defaultAnonymousPolicyID} cfg.session.AuthPolicyURI = ua.SecurityPolicyURINone } + return nil } } @@ -389,12 +395,13 @@ func setPolicyID(t interface{}, policy string) { // todo(fs): AuthXXX methods since this approach requires context // todo(fs): and ordering? func AuthPolicyID(policy string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { if cfg.session.UserIdentityToken == nil { log.Printf("policy ID needs to be set after the policy type is chosen, no changes made. Call SecurityFromEndpoint() or an AuthXXX() option first") - return + return nil } setPolicyID(cfg.session.UserIdentityToken, policy) + return nil } } @@ -402,7 +409,7 @@ func AuthPolicyID(policy string) Option { // Note: PolicyID still needs to be set outside of this method, typically through // the SecurityFromEndpoint() Option func AuthAnonymous() Option { - return func(cfg *Config) { + return func(cfg *Config) error { if cfg.session.UserIdentityToken == nil { cfg.session.UserIdentityToken = &ua.AnonymousIdentityToken{} } @@ -411,8 +418,9 @@ func AuthAnonymous() Option { if !ok { // todo(fs): should we Fatal here? log.Printf("non-anonymous authentication already configured, ignoring") - return + return nil } + return nil } } @@ -420,7 +428,7 @@ func AuthAnonymous() Option { // Note: PolicyID still needs to be set outside of this method, typically through // the SecurityFromEndpoint() Option func AuthUsername(user, pass string) Option { - return func(cfg *Config) { + return func(cfg *Config) error { if cfg.session.UserIdentityToken == nil { cfg.session.UserIdentityToken = &ua.UserNameIdentityToken{} } @@ -429,11 +437,12 @@ func AuthUsername(user, pass string) Option { if !ok { // todo(fs): should we Fatal here? log.Printf("non-username authentication already configured, ignoring") - return + return nil } t.UserName = user cfg.session.AuthPassword = pass + return nil } } @@ -441,7 +450,7 @@ func AuthUsername(user, pass string) Option { // Note: PolicyID still needs to be set outside of this method, typically through // the SecurityFromEndpoint() Option func AuthCertificate(cert []byte) Option { - return func(cfg *Config) { + return func(cfg *Config) error { if cfg.session.UserIdentityToken == nil { cfg.session.UserIdentityToken = &ua.X509IdentityToken{} } @@ -450,10 +459,11 @@ func AuthCertificate(cert []byte) Option { if !ok { // todo(fs): should we Fatal here? log.Printf("non-certificate authentication already configured, ignoring") - return + return nil } t.CertificateData = cert + return nil } } @@ -461,7 +471,7 @@ func AuthCertificate(cert []byte) Option { // Note: PolicyID still needs to be set outside of this method, typically through // the SecurityFromEndpoint() Option func AuthIssuedToken(tokenData []byte) Option { - return func(cfg *Config) { + return func(cfg *Config) error { if cfg.session.UserIdentityToken == nil { cfg.session.UserIdentityToken = &ua.IssuedIdentityToken{} } @@ -469,67 +479,75 @@ func AuthIssuedToken(tokenData []byte) Option { t, ok := cfg.session.UserIdentityToken.(*ua.IssuedIdentityToken) if !ok { log.Printf("non-issued token authentication already configured, ignoring") - return + return nil } // todo(dw): not correct; need to read spec t.TokenData = tokenData + return nil } } // RequestTimeout sets the timeout for all requests over SecureChannel func RequestTimeout(t time.Duration) Option { - return func(cfg *Config) { + return func(cfg *Config) error { cfg.sechan.RequestTimeout = t + return nil } } // Dialer sets the uacp.Dialer to establish the connection to the server. func Dialer(d *uacp.Dialer) Option { - return func(cfg *Config) { + return func(cfg *Config) error { initDialer(cfg) cfg.dialer = d + return nil } } // DialTimeout sets the timeout for establishing the UACP connection. // Defaults to DefaultDialTimeout. Set to zero for no timeout. func DialTimeout(d time.Duration) Option { - return func(cfg *Config) { + return func(cfg *Config) error { initDialer(cfg) cfg.dialer.Dialer.Timeout = d + return nil } } // MaxMessageSize sets the maximum message size for the UACP handshake. func MaxMessageSize(n uint32) Option { - return func(cfg *Config) { + return func(cfg *Config) error { initDialer(cfg) cfg.dialer.ClientACK.MaxMessageSize = n + return nil } } // MaxChunkCount sets the maximum chunk count for the UACP handshake. func MaxChunkCount(n uint32) Option { - return func(cfg *Config) { + return func(cfg *Config) error { initDialer(cfg) cfg.dialer.ClientACK.MaxChunkCount = n + return nil } } // ReceiveBufferSize sets the receive buffer size for the UACP handshake. func ReceiveBufferSize(n uint32) Option { - return func(cfg *Config) { + return func(cfg *Config) error { initDialer(cfg) cfg.dialer.ClientACK.ReceiveBufSize = n + return nil } } // SendBufferSize sets the send buffer size for the UACP handshake. func SendBufferSize(n uint32) Option { - return func(cfg *Config) { + return func(cfg *Config) error { initDialer(cfg) cfg.dialer.ClientACK.SendBufSize = n + return nil } } diff --git a/config_test.go b/config_test.go index 42d72043..602db96a 100644 --- a/config_test.go +++ b/config_test.go @@ -170,6 +170,7 @@ func TestOptions(t *testing.T) { name string opt Option cfg *Config + err error }{ { name: `ApplicationName("a")`, @@ -297,9 +298,8 @@ func TestOptions(t *testing.T) { { name: `CertificateFile() error`, opt: CertificateFile("x"), - cfg: &Config{ - err: notFoundError("certificate", "x"), - }, + cfg: &Config{}, + err: notFoundError("certificate", "x"), }, { name: `Lifetime(10ms)`, @@ -359,9 +359,8 @@ func TestOptions(t *testing.T) { { name: `PrivateKeyFile() error`, opt: PrivateKeyFile("x"), - cfg: &Config{ - err: notFoundError("private key", "x"), - }, + cfg: &Config{}, + err: notFoundError("private key", "x"), }, { name: `ProductURI("a")`, @@ -432,9 +431,8 @@ func TestOptions(t *testing.T) { { name: `RemoteCertificateFile() error`, opt: RemoteCertificateFile("x"), - cfg: &Config{ - err: notFoundError("certificate", "x"), - }, + cfg: &Config{}, + err: notFoundError("certificate", "x"), }, { name: `RequestTimeout(5s)`, @@ -803,8 +801,8 @@ func TestOptions(t *testing.T) { return "" } - cfg := ApplyConfig(tt.opt) - if got, want := errstr(cfg.Error()), errstr(tt.cfg.err); got != "" || want != "" { + cfg, err := ApplyConfig(tt.opt) + if got, want := errstr(err), errstr(tt.err); got != "" || want != "" { if got != want { t.Fatalf("got error %q want %q", got, want) } diff --git a/errors/errors.go b/errors/errors.go index bfc84dac..7280f35e 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -34,6 +34,11 @@ func Unwrap(err error) error { return errors.Unwrap(err) } +// Join wraps errors.Join +func Join(errs ...error) error { + return errors.Join(errs...) +} + // Equal returns true if the two errors have the same error message. // // todo(fs): the reason we need this function and cannot just use diff --git a/examples/accesslevel/accesslevel.go b/examples/accesslevel/accesslevel.go index 39bc2349..a6ee4532 100644 --- a/examples/accesslevel/accesslevel.go +++ b/examples/accesslevel/accesslevel.go @@ -25,7 +25,10 @@ func main() { ctx := context.Background() - c := opcua.NewClient(*endpoint) + c, err := opcua.NewClient(*endpoint) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/browse/browse.go b/examples/browse/browse.go index 2287662c..27826b2d 100644 --- a/examples/browse/browse.go +++ b/examples/browse/browse.go @@ -174,7 +174,10 @@ func main() { ctx := context.Background() - c := opcua.NewClient(*endpoint) + c, err := opcua.NewClient(*endpoint) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/crypto/crypto.go b/examples/crypto/crypto.go index dc7b6203..76b92d93 100644 --- a/examples/crypto/crypto.go +++ b/examples/crypto/crypto.go @@ -63,7 +63,10 @@ func main() { opts := clientOptsFromFlags(endpoints) // Create a Client with the selected options - c := opcua.NewClient(*endpoint, opts...) + c, err := opcua.NewClient(*endpoint, opts...) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } @@ -86,7 +89,10 @@ func main() { log.Fatalf("Error detaching session: %s", err) } - d := opcua.NewClient(*endpoint, opts...) + d, err := opcua.NewClient(*endpoint, opts...) + if err != nil { + log.Fatal(err) + } // Create a channel only and do not activate it automatically d.Dial(ctx) diff --git a/examples/datetime/datetime.go b/examples/datetime/datetime.go index ad2ae770..dca80633 100644 --- a/examples/datetime/datetime.go +++ b/examples/datetime/datetime.go @@ -47,7 +47,10 @@ func main() { opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), } - c := opcua.NewClient(ep.EndpointURL, opts...) + c, err := opcua.NewClient(ep.EndpointURL, opts...) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/history-read/history-read.go b/examples/history-read/history-read.go index d3cc2b53..c0ca3c06 100644 --- a/examples/history-read/history-read.go +++ b/examples/history-read/history-read.go @@ -24,7 +24,10 @@ func main() { ctx := context.Background() - c := opcua.NewClient(*endpoint) + c, err := opcua.NewClient(*endpoint) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/method/method.go b/examples/method/method.go index 802dcd7c..7a16585a 100644 --- a/examples/method/method.go +++ b/examples/method/method.go @@ -24,7 +24,10 @@ func main() { ctx := context.Background() - c := opcua.NewClient(*endpoint, opcua.SecurityMode(ua.MessageSecurityModeNone)) + c, err := opcua.NewClient(*endpoint, opcua.SecurityMode(ua.MessageSecurityModeNone)) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/monitor/monitor.go b/examples/monitor/monitor.go index 8700cbe7..ef0f39ca 100644 --- a/examples/monitor/monitor.go +++ b/examples/monitor/monitor.go @@ -63,7 +63,10 @@ func main() { opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), } - c := opcua.NewClient(ep.EndpointURL, opts...) + c, err := opcua.NewClient(ep.EndpointURL, opts...) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/read/read.go b/examples/read/read.go index a921e025..e33763ff 100644 --- a/examples/read/read.go +++ b/examples/read/read.go @@ -28,7 +28,10 @@ func main() { ctx := context.Background() - c := opcua.NewClient(*endpoint, opcua.SecurityMode(ua.MessageSecurityModeNone)) + c, err := opcua.NewClient(*endpoint, opcua.SecurityMode(ua.MessageSecurityModeNone)) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/regread/regread.go b/examples/regread/regread.go index ca2ca8b8..01eb0139 100644 --- a/examples/regread/regread.go +++ b/examples/regread/regread.go @@ -25,7 +25,10 @@ func main() { ctx := context.Background() - c := opcua.NewClient(*endpoint, opcua.SecurityMode(ua.MessageSecurityModeNone)) + c, err := opcua.NewClient(*endpoint, opcua.SecurityMode(ua.MessageSecurityModeNone)) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/subscribe/subscribe.go b/examples/subscribe/subscribe.go index 32134d23..8127cd9a 100644 --- a/examples/subscribe/subscribe.go +++ b/examples/subscribe/subscribe.go @@ -60,7 +60,10 @@ func main() { opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), } - c := opcua.NewClient(ep.EndpointURL, opts...) + c, err := opcua.NewClient(ep.EndpointURL, opts...) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/translate/translate.go b/examples/translate/translate.go index 3f9c6ffe..48415a0e 100644 --- a/examples/translate/translate.go +++ b/examples/translate/translate.go @@ -27,7 +27,10 @@ func main() { ctx := context.Background() - c := opcua.NewClient(*endpoint) + c, err := opcua.NewClient(*endpoint) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/trigger/trigger.go b/examples/trigger/trigger.go index e6f685f1..beafb3b3 100644 --- a/examples/trigger/trigger.go +++ b/examples/trigger/trigger.go @@ -59,7 +59,10 @@ func main() { opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), } - c := opcua.NewClient(ep.EndpointURL, opts...) + c, err := opcua.NewClient(ep.EndpointURL, opts...) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/udt/udt.go b/examples/udt/udt.go index b7eebdda..6fa3ad5a 100644 --- a/examples/udt/udt.go +++ b/examples/udt/udt.go @@ -55,7 +55,10 @@ func main() { opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), } - c := opcua.NewClient(ep.EndpointURL, opts...) + c, err := opcua.NewClient(ep.EndpointURL, opts...) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/examples/write/write.go b/examples/write/write.go index 62f9dd98..ca97d24f 100644 --- a/examples/write/write.go +++ b/examples/write/write.go @@ -26,7 +26,10 @@ func main() { ctx := context.Background() - c := opcua.NewClient(*endpoint) + c, err := opcua.NewClient(*endpoint) + if err != nil { + log.Fatal(err) + } if err := c.Connect(ctx); err != nil { log.Fatal(err) } diff --git a/uatest/method_test.go b/uatest/method_test.go index ea0febae..77172714 100644 --- a/uatest/method_test.go +++ b/uatest/method_test.go @@ -61,7 +61,10 @@ func TestCallMethod(t *testing.T) { srv := NewServer("method_server.py") defer srv.Close() - c := opcua.NewClient(srv.Endpoint, srv.Opts...) + c, err := opcua.NewClient(srv.Endpoint, srv.Opts...) + if err != nil { + t.Fatal(err) + } if err := c.Connect(ctx); err != nil { t.Fatal(err) } diff --git a/uatest/namespace_test.go b/uatest/namespace_test.go index ca53f9c3..ddec547b 100644 --- a/uatest/namespace_test.go +++ b/uatest/namespace_test.go @@ -18,7 +18,10 @@ func TestNamespace(t *testing.T) { srv := NewServer("rw_server.py") defer srv.Close() - c := opcua.NewClient(srv.Endpoint, srv.Opts...) + c, err := opcua.NewClient(srv.Endpoint, srv.Opts...) + if err != nil { + t.Fatal(err) + } if err := c.Connect(ctx); err != nil { t.Fatal(err) } diff --git a/uatest/read_test.go b/uatest/read_test.go index f425aa16..b23c46cf 100644 --- a/uatest/read_test.go +++ b/uatest/read_test.go @@ -33,7 +33,10 @@ func TestRead(t *testing.T) { srv := NewServer("rw_server.py") defer srv.Close() - c := opcua.NewClient(srv.Endpoint, srv.Opts...) + c, err := opcua.NewClient(srv.Endpoint, srv.Opts...) + if err != nil { + t.Fatal(err) + } if err := c.Connect(ctx); err != nil { t.Fatal(err) } diff --git a/uatest/read_unknow_node_id_test.go b/uatest/read_unknow_node_id_test.go index a729f98a..b5fd4249 100644 --- a/uatest/read_unknow_node_id_test.go +++ b/uatest/read_unknow_node_id_test.go @@ -20,7 +20,10 @@ func TestReadUnknowNodeID(t *testing.T) { srv := NewServer("read_unknow_node_id_server.py") defer srv.Close() - c := opcua.NewClient(srv.Endpoint, srv.Opts...) + c, err := opcua.NewClient(srv.Endpoint, srv.Opts...) + if err != nil { + t.Fatal(err) + } if err := c.Connect(ctx); err != nil { t.Fatal(err) } diff --git a/uatest/reconnection_test.go b/uatest/reconnection_test.go index f6d20508..e919c53c 100644 --- a/uatest/reconnection_test.go +++ b/uatest/reconnection_test.go @@ -27,7 +27,10 @@ func TestAutoReconnection(t *testing.T) { srv := NewServer("reconnection_server.py") defer srv.Close() - c := opcua.NewClient(srv.Endpoint, srv.Opts...) + c, err := opcua.NewClient(srv.Endpoint, srv.Opts...) + if err != nil { + t.Fatal(err) + } if err := c.Connect(ctx); err != nil { t.Fatal(err) } diff --git a/uatest/stats_test.go b/uatest/stats_test.go index 070a351b..f15bf333 100644 --- a/uatest/stats_test.go +++ b/uatest/stats_test.go @@ -28,7 +28,10 @@ func TestStats(t *testing.T) { srv := NewServer("rw_server.py") defer srv.Close() - c := opcua.NewClient(srv.Endpoint, srv.Opts...) + c, err := opcua.NewClient(srv.Endpoint, srv.Opts...) + if err != nil { + t.Fatal(err) + } if err := c.Connect(ctx); err != nil { t.Fatal(err) } diff --git a/uatest/timeout_test.go b/uatest/timeout_test.go index 0d29eba5..8a83c96d 100644 --- a/uatest/timeout_test.go +++ b/uatest/timeout_test.go @@ -21,13 +21,19 @@ const ( ) func TestClientTimeoutViaOptions(t *testing.T) { - c := opcua.NewClient(tcpNoRstTestServer, opcua.DialTimeout(forceTimeoutDuration)) + c, err := opcua.NewClient(tcpNoRstTestServer, opcua.DialTimeout(forceTimeoutDuration)) + if err != nil { + t.Fatal(err) + } connectAndValidate(t, c, context.Background(), forceTimeoutDuration) } func TestClientTimeoutViaContext(t *testing.T) { - c := opcua.NewClient(tcpNoRstTestServer) + c, err := opcua.NewClient(tcpNoRstTestServer) + if err != nil { + t.Fatal(err) + } ctx, cancel := context.WithTimeout(context.Background(), forceTimeoutDuration) defer cancel() diff --git a/uatest/write_test.go b/uatest/write_test.go index b6c0d1cc..f1b76b7b 100644 --- a/uatest/write_test.go +++ b/uatest/write_test.go @@ -32,7 +32,10 @@ func TestWrite(t *testing.T) { srv := NewServer("rw_server.py") defer srv.Close() - c := opcua.NewClient(srv.Endpoint, srv.Opts...) + c, err := opcua.NewClient(srv.Endpoint, srv.Opts...) + if err != nil { + t.Fatal(err) + } if err := c.Connect(ctx); err != nil { t.Fatal(err) }