code: func AttachTags(c context.Context, inst *Instance, tags []*api.Tag) error {
  	return Txn(c, "AttachTags", func(c context.Context) error {
  		if err := CheckInstanceReady(c, inst); err != nil {
  			return err
  		}
  
  		_, missing, err := checkExistingTags(c, inst, tags)
  		if err != nil {
  			return err
  		}
  
  		events := Events{}
  		now := clock.Now(c).UTC()
  		who := string(auth.CurrentIdentity(c))
  		for _, t := range missing {
  			t.RegisteredBy = who
  			t.RegisteredTs = now
  			events.Emit(&api.Event{
  				Kind:     api.EventKind_INSTANCE_TAG_ATTACHED,
  				Package:  inst.Package.StringID(),
  				Instance: inst.InstanceID,
  				Tag:      t.Tag,
  				Who:      who,
  				When:     google.NewTimestamp(now),
  			})
  		}
  
  		if err := datastore.Put(c, missing); err != nil {
  			return transient.Tag.Apply(err)
  		}
  		return events.Flush(c)
  	})
  }
docs: // AttachTags transactionally attaches a bunch of tags to an instance.
  //
  // Assumes inputs are already validated. Launches a transaction inside (and thus
  // can't be a part of a transaction itself). Updates 'inst' in-place with the
  // most recent instance state.
  //
  // Returns gRPC-tagged errors:
  //    NotFound if there's no such instance or package.
  //    FailedPrecondition if some processors are still running.
  //    Aborted if some processors have failed.
  //    Internal on tag ID collision.