diff --git a/components/arm/fake/fake.go b/components/arm/fake/fake.go index 1a812659b8c..2ca262603d3 100644 --- a/components/arm/fake/fake.go +++ b/components/arm/fake/fake.go @@ -31,14 +31,15 @@ var fakeModelJSON []byte // AttrConfig is used for converting config attributes. type AttrConfig struct { - FailNew bool `json:"fail_new"` - ArmModel string `json:"arm-model"` + FailNew bool `json:"fail_new"` + FailValidate bool `json:"fail_validate"` + ArmModel string `json:"arm-model"` } // Validate ensures all parts of the config are valid. func (config *AttrConfig) Validate(path string) error { - if config.FailNew { - return errors.New("whoops") + if config.FailValidate { + return errors.New("whoops! failed to validate") } return nil } @@ -66,7 +67,13 @@ func NewArm(cfg config.Component, logger golog.Logger) (arm.LocalArm, error) { var model referenceframe.Model var err error if cfg.ConvertedAttributes != nil { - switch cfg.ConvertedAttributes.(*AttrConfig).ArmModel { + converted := cfg.ConvertedAttributes.(*AttrConfig) + + if converted.FailNew { + return nil, errors.New("whoops! failed to start up") + } + + switch converted.ArmModel { case xarm.ModelName6DOF: model, err = xarm.Model(cfg.Name, 6) case xarm.ModelName7DOF: diff --git a/robot/impl/local_robot.go b/robot/impl/local_robot.go index 529cecfb8c3..77066a85569 100644 --- a/robot/impl/local_robot.go +++ b/robot/impl/local_robot.go @@ -494,7 +494,7 @@ func (r *localRobot) newService(ctx context.Context, config config.Service) (int // If service model/type not found then print list of valid models they can choose from if f == nil { validModels := registry.FindValidServiceModels(rName) - return nil, errors.Errorf("unknown service subtype: %s and/or model: %s use one of the following valid models: %s", + return nil, errors.Errorf("unknown service type: %s and/or model: %s use one of the following valid models: %s", rName.Subtype, config.Model, strings.Join(validModels, ", ")) } @@ -537,7 +537,7 @@ func (r *localRobot) newResource(ctx context.Context, config config.Component) ( rName := config.ResourceName() f := registry.ComponentLookup(rName.Subtype, config.Model) if f == nil { - return nil, errors.Errorf("unknown component subtype: %s and/or model: %s", rName.Subtype, config.Model) + return nil, errors.Errorf("unknown component type: %s and/or model: %s", rName.Subtype, config.Model) } deps, err := r.getDependencies(rName) @@ -554,7 +554,7 @@ func (r *localRobot) newResource(ctx context.Context, config config.Component) ( } if err != nil { - return nil, errors.Errorf("error building resource %s/%s/%s: %s", config.Model, rName.Subtype, config.Name, err) + return nil, err } c := registry.ResourceSubtypeLookup(rName.Subtype) @@ -571,7 +571,6 @@ func (r *localRobot) newResource(ctx context.Context, config config.Component) ( func (r *localRobot) updateDefaultServices(ctx context.Context) { resources := map[resource.Name]interface{}{} for _, n := range r.ResourceNames() { - // TODO(RSDK-333) if not found, could mean a name clash or a remote service res, err := r.ResourceByName(n) if err != nil { r.Logger().Debugw("not found while grabbing all resources during default svc refresh", "resource", res, "error", err) diff --git a/robot/impl/local_robot_test.go b/robot/impl/local_robot_test.go index bad5fa2f274..f73b0d7c117 100644 --- a/robot/impl/local_robot_test.go +++ b/robot/impl/local_robot_test.go @@ -1443,7 +1443,15 @@ func TestResourceStartsOnReconfigure(t *testing.T) { test.That(t, r, test.ShouldNotBeNil) noBase, err := r.ResourceByName(base.Named("fake0")) - test.That(t, err, test.ShouldBeError, rutils.NewResourceNotFoundError(base.Named("fake0"))) + test.That( + t, + err, + test.ShouldBeError, + rutils.NewResourceNotAvailableError( + base.Named("fake0"), + errors.New("component build error: unknown component type: rdk:component:base and/or model: random"), + ), + ) test.That(t, noBase, test.ShouldBeNil) noSvc, err := r.ResourceByName(datamanager.Named("fake1")) diff --git a/robot/impl/resource_manager.go b/robot/impl/resource_manager.go index f8aca0125d5..aa34b0ecc66 100644 --- a/robot/impl/resource_manager.go +++ b/robot/impl/resource_manager.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "os" + "reflect" "strings" "sync" @@ -52,6 +53,7 @@ type resourceManager struct { type resourcePlaceholder struct { real interface{} config interface{} + err error } type resourceManagerOptions struct { @@ -364,7 +366,8 @@ func (manager *resourceManager) completeConfig( if c, ok := wrap.config.(config.Component); ok { iface, err := manager.processComponent(ctx, r, c, wrap.real, robot) if err != nil { - manager.logger.Errorw("error building component", "error", err) + manager.logger.Errorw("error building component", "resource", c.ResourceName(), "model", c.Model, "error", err) + wrap.err = errors.Wrap(err, "component build error") continue } manager.resources.AddNode(r, iface) @@ -375,7 +378,8 @@ func (manager *resourceManager) completeConfig( } else if rc, ok := wrap.config.(config.Remote); ok { rr, err := manager.processRemote(ctx, rc) if err != nil { - manager.logger.Errorw("error connecting to remote", "error", err) + manager.logger.Errorw("error connecting to remote", "remote", rc.Name, "error", err) + wrap.err = errors.Wrap(err, "remote connection error") continue } manager.addRemote(ctx, rr, rc, robot) @@ -406,11 +410,14 @@ func (manager *resourceManager) completeConfig( } s, ok := wrap.config.(config.Service) if !ok { - manager.logger.Errorw("service config is not a service config", "resource", r) + err := errors.New("service config is not a service config") + manager.logger.Errorw(err.Error(), "resource", r) + wrap.err = errors.Wrap(err, "service build error") } iface, err := manager.processService(ctx, s, wrap.real, robot) if err != nil { - manager.logger.Errorw("error building service", "error", err) + manager.logger.Errorw("error building service", "resource", s.ResourceName(), "model", s.Model, "error", err) + wrap.err = errors.Wrap(err, "service build error") continue } manager.resources.AddNode(r, iface) @@ -501,7 +508,7 @@ func (manager *resourceManager) processRemote(ctx context.Context, err = errors.New("must use Config.AllowInsecureCreds to connect to a non-TLS secured robot") } } - return nil, errors.Wrapf(err, "couldn't connect to robot remote (%s)", config.Address) + return nil, errors.Errorf("couldn't connect to robot remote (%s): %s", config.Address, err) } manager.logger.Debugw("connected now to remote", "remote", config.Name) return robotClient, nil @@ -514,7 +521,11 @@ func (manager *resourceManager) RemoteByName(name string) (robot.Robot, bool) { if iface, ok := manager.resources.Node(rName); ok { part, ok := iface.(robot.Robot) if !ok { - manager.logger.Errorw("tried to access remote but its not a robot interface", "remote_name", name, "type", iface) + if ph, ok := iface.(*resourcePlaceholder); ok { + manager.logger.Errorw("remote not available", "remote", name, "err", ph.err) + } else { + manager.logger.Errorw("tried to access remote but its not a robot interface", "remote", name, "type", reflect.TypeOf(iface)) + } } return part, ok } @@ -564,6 +575,7 @@ func (manager *resourceManager) markChildrenForUpdate(ctx context.Context, rName wrapper := &resourcePlaceholder{ real: nil, config: originalConfig, + err: errors.New("resource not updated yet"), } manager.resources.AddNode(x, wrapper) } @@ -630,6 +642,7 @@ func (manager *resourceManager) wrapResource(name resource.Name, config interfac wrapper = &resourcePlaceholder{ real: part, config: config, + err: errors.New("resource not initialized yet"), } } // the first thing we need to do is seek if the resource name already exists as an unknownType, if so @@ -685,7 +698,7 @@ func (manager *resourceManager) wrapResource(name resource.Name, config interfac } // updateResourceGraph using the difference between current config -// and next we create resource wrappers to be consumed ny completeConfig later on +// and next we create resource wrappers to be consumed by completeConfig later on // Ideally at the end of this function we should have a complete graph representation of the configuration. func (manager *resourceManager) updateResources( ctx context.Context, @@ -752,9 +765,11 @@ func (manager *resourceManager) updateResources( func (manager *resourceManager) ResourceByName(name resource.Name) (interface{}, error) { robotPart, ok := manager.resources.Node(name) if ok && robotPart != nil { - if _, ok = robotPart.(*resourcePlaceholder); !ok { + ph, ok := robotPart.(*resourcePlaceholder) + if !ok { return robotPart, nil } + return nil, rutils.NewResourceNotAvailableError(name, ph.err) } // if we haven't found a resource of this name then we are going to look into remote resources to find it. if !ok && !name.ContainsRemoteNames() { diff --git a/utils/errors.go b/utils/errors.go index 5dcaf06a80a..430b4b0a4f4 100644 --- a/utils/errors.go +++ b/utils/errors.go @@ -11,6 +11,11 @@ func NewResourceNotFoundError(name resource.Name) error { return errors.Errorf("resource %q not found", name) } +// NewResourceNotAvailableError is used when a resource is not available because of some error. +func NewResourceNotAvailableError(name resource.Name, err error) error { + return errors.Wrapf(err, "resource %q not available", name) +} + // NewRemoteResourceClashError is used when you are more than one resource with the same name exist. func NewRemoteResourceClashError(name string) error { return errors.Errorf("more that one remote resources with name %q exists", name)