AddDataSource adds a data source to an instance.
(ctx context.Context, req *connect.Request[v1pb.AddDataSourceRequest])
| 802 | |
| 803 | // AddDataSource adds a data source to an instance. |
| 804 | func (s *InstanceService) AddDataSource(ctx context.Context, req *connect.Request[v1pb.AddDataSourceRequest]) (*connect.Response[v1pb.Instance], error) { |
| 805 | if req.Msg.DataSource == nil { |
| 806 | return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("data sources is required")) |
| 807 | } |
| 808 | // We only support add RO type datasouce to instance now, see more details in instance_service.proto. |
| 809 | if req.Msg.DataSource.Type != v1pb.DataSourceType_READ_ONLY { |
| 810 | return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("only support adding read-only data source")) |
| 811 | } |
| 812 | |
| 813 | dataSource, err := convertV1DataSource(req.Msg.DataSource) |
| 814 | if err != nil { |
| 815 | return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("failed to convert data source")) |
| 816 | } |
| 817 | |
| 818 | instance, err := getInstanceMessage(ctx, s.store, req.Msg.Name) |
| 819 | if err != nil { |
| 820 | return nil, err |
| 821 | } |
| 822 | if instance.Deleted { |
| 823 | return nil, connect.NewError(connect.CodeNotFound, errors.Errorf("instance %q has been deleted", req.Msg.Name)) |
| 824 | } |
| 825 | for _, ds := range instance.Metadata.GetDataSources() { |
| 826 | if ds.GetId() == req.Msg.DataSource.Id { |
| 827 | return nil, connect.NewError(connect.CodeNotFound, errors.New("data source already exists with the same name")) |
| 828 | } |
| 829 | } |
| 830 | if err := s.checkDataSource(ctx, instance, dataSource); err != nil { |
| 831 | return nil, err |
| 832 | } |
| 833 | if err := validateAndSanitizeDataSourceTLS(dataSource); err != nil { |
| 834 | return nil, connect.NewError(connect.CodeInvalidArgument, err) |
| 835 | } |
| 836 | metadata := proto.CloneOf(instance.Metadata) |
| 837 | metadata.DataSources = append(metadata.DataSources, dataSource) |
| 838 | if err := s.checkInstanceDataSources(ctx, instance, metadata.GetDataSources()); err != nil { |
| 839 | return nil, err |
| 840 | } |
| 841 | |
| 842 | if err := s.licenseService.IsFeatureEnabledForInstance(ctx, common.GetWorkspaceIDFromContext(ctx), v1pb.PlanFeature_FEATURE_INSTANCE_READ_ONLY_CONNECTION, instance); err != nil { |
| 843 | return nil, connect.NewError(connect.CodePermissionDenied, err) |
| 844 | } |
| 845 | |
| 846 | // Test connection. |
| 847 | if req.Msg.ValidateOnly { |
| 848 | err := func() error { |
| 849 | driver, err := s.dbFactory.GetDataSourceDriver( |
| 850 | ctx, instance, dataSource, |
| 851 | db.ConnectionContext{ |
| 852 | ReadOnly: dataSource.GetType() == storepb.DataSourceType_READ_ONLY, |
| 853 | }, |
| 854 | ) |
| 855 | if err != nil { |
| 856 | return connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to get database driver")) |
| 857 | } |
| 858 | defer driver.Close(ctx) |
| 859 | if err := driver.Ping(ctx); err != nil { |
| 860 | return connect.NewError(connect.CodeInvalidArgument, errors.Wrapf(err, "invalid datasource %s", dataSource.GetType())) |
| 861 | } |
no test coverage detected