Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --disable-admin-operations flag in Block Bucket UI #6601

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions cmd/thanos/tools_bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,14 @@ type bucketLsConfig struct {
}

type bucketWebConfig struct {
webRoutePrefix string
webExternalPrefix string
webPrefixHeaderName string
webDisableCORS bool
interval time.Duration
label string
timeout time.Duration
webRoutePrefix string
webExternalPrefix string
webPrefixHeaderName string
webDisableCORS bool
interval time.Duration
label string
timeout time.Duration
disableAdminOperations bool
}

type bucketReplicateConfig struct {
Expand Down Expand Up @@ -203,6 +204,8 @@ func (tbc *bucketWebConfig) registerBucketWebFlag(cmd extkingpin.FlagClause) *bu
cmd.Flag("timeout", "Timeout to download metadata from remote storage").Default("5m").DurationVar(&tbc.timeout)

cmd.Flag("label", "External block label to use as group title").StringVar(&tbc.label)

cmd.Flag("disable-admin-operations", "Restrict access to Block Mark deletion and no compaction").Default("false").BoolVar(&tbc.disableAdminOperations)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this flag be in Compactor (cmd/thanos/compact.go) instead? As that is the component that exposes blocks UI

Copy link
Contributor Author

@harsh-ps-2003 harsh-ps-2003 Aug 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I lack on a broader knowledge of codebase, but as far as I can understand, If i add the flag in compact.go then the flag will be available in Compactor UI instead of Bucket UI. I can tell this for sure that adding the flag in tools_bucket.go is only affecting Bucket UI. I think we want this flag specifically in Bucket UI not in the Compactor UI right? @yeya24 @GiedriusS @saswatamcode

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack got confused for a bit. So Thanos users can access the Block UI via two ways I think,

  • tools bucket web (i.e which is covered by the flag you added)
  • compact (which isn't covered)

So let's add the same flag there too, as you can mark deletion and no compaction there too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay! i will add the same flag for Compactor as well as Bucket

return tbc
}

Expand Down
27 changes: 19 additions & 8 deletions pkg/api/blocks/v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ import (

// BlocksAPI is a very simple API used by Thanos Block Viewer.
type BlocksAPI struct {
baseAPI *api.BaseAPI
logger log.Logger
globalBlocksInfo *BlocksInfo
loadedBlocksInfo *BlocksInfo
disableCORS bool
bkt objstore.Bucket
baseAPI *api.BaseAPI
logger log.Logger
globalBlocksInfo *BlocksInfo
loadedBlocksInfo *BlocksInfo
disableCORS bool
bkt objstore.Bucket
disableAdminOperations bool
}

type BlocksInfo struct {
Expand Down Expand Up @@ -72,8 +73,9 @@ func NewBlocksAPI(logger log.Logger, disableCORS bool, label string, flagsMap ma
Blocks: []metadata.Meta{},
Label: label,
},
disableCORS: disableCORS,
bkt: bkt,
disableCORS: disableCORS,
bkt: bkt,
disableAdminOperations: flagsMap["disable-admin-operations"] == "true",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just pass the bool directly in NewBlockAPI, instead of using this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actaully flagsMap map[string]string will give a string "true" with flagsMap["disable-admin-operations"]. thus I am simply converting it to boolean.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be cleaner and more consistent with other such options like disableCors, and we wouldn't need to check a string here at all
But actually, I think we don't even need any changes in BlocksAPI here. The flag value will just directly be used in UI anyway, so we don't need a separate API. See my other comment #6601 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay! thanks for the review. i will look into it!

}
}

Expand All @@ -84,9 +86,18 @@ func (bapi *BlocksAPI) Register(r *route.Router, tracer opentracing.Tracer, logg

r.Get("/blocks", instr("blocks", bapi.blocks))
r.Post("/blocks/mark", instr("blocks_mark", bapi.markBlock))
r.Get("/flags", instr("flags_disableAdminOperations", bapi.disableAdminOperationsHandler))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to add a new API for this. We already register api/v1/status/flags as a common endpoint for all components in pkg/api

Copy link
Contributor Author

@harsh-ps-2003 harsh-ps-2003 Aug 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be cleaner and more consistent with other such options like disableCors, and we wouldn't need to check a string here at all
But actually, I think we don't even need any changes in BlocksAPI here. The flag value will just directly be used in UI anyway, so we don't need a separate API. See my other comment #6601 (comment)

@saswatamcode @GiedriusS @yeya24 one problem that I am facing when using /status/flags endpoint is that I am not able to figure out how to get the flags value inside the markBlock function of v1.go. I need it so as to restrict access to the API when the flag is enabled.

Copy link
Member

@saswatamcode saswatamcode Aug 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack! Reading the issue seems like we would need the option passed here as well. To restrict access, both via UI and API. Ignore this comment then,

But actually, I think we don't even need any changes in BlocksAPI here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the clarification!

}

func (bapi *BlocksAPI) disableAdminOperationsHandler(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
response := map[string]bool{"disableAdminOperations": bapi.disableAdminOperations}
return response, nil, nil, func() {}
}

func (bapi *BlocksAPI) markBlock(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
if bapi.disableAdminOperations {
return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.New("Admin operations are disabled")}, func() {}
}
idParam := r.FormValue("id")
actionParam := r.FormValue("action")
detailParam := r.FormValue("detail")
Expand Down
58 changes: 37 additions & 21 deletions pkg/ui/react-app/src/thanos/pages/blocks/BlockDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, useState } from 'react';
import React, { FC, useEffect, useState } from 'react';
import { Block } from './block';
import styles from './blocks.module.css';
import moment from 'moment';
Expand All @@ -13,6 +13,7 @@ export interface BlockDetailsProps {
export const BlockDetails: FC<BlockDetailsProps> = ({ block, selectBlock }) => {
const [modalAction, setModalAction] = useState<string>('');
const [detailValue, setDetailValue] = useState<string | null>(null);
const [disableAdminOperations, setDisableAdminOperations] = useState<boolean>(false);

const submitMarkBlock = async (action: string, ulid: string, detail: string | null) => {
try {
Expand Down Expand Up @@ -40,6 +41,17 @@ export const BlockDetails: FC<BlockDetailsProps> = ({ block, selectBlock }) => {
}
};

const fetchDisableAdminOperations = async () => {
const response = await fetch('/api/v1/flags');
const result = await response.json();
setDisableAdminOperations(result.data.disableAdminOperations);
console.log('disableAdminOperations', result.data.disableAdminOperations);
};

useEffect(() => {
fetchDisableAdminOperations();
}, []);

Comment on lines +44 to +54
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try to do this from /status/flags endpoint directly. Take a look at how we use it for defaultStep in PanelList : https://github.com/thanos-io/thanos/blob/main/pkg/ui/react-app/src/pages/graph/PanelList.tsx#L161

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its actually very convinent. i use this in blockDetails.tsx and it worked like a charm :

  const { response: flagsRes } = useFetch<FlagMap>(`/api/v1/status/flags`);
  const disableAdminOperations = flagsRes?.data?.['disable-admin-operations'] || false;

return (
<div className={`${styles.blockDetails} ${block && styles.open}`}>
{block && (
Expand Down Expand Up @@ -100,26 +112,30 @@ export const BlockDetails: FC<BlockDetailsProps> = ({ block, selectBlock }) => {
<Button>Download meta.json</Button>
</a>
</div>
<div style={{ marginTop: '12px' }}>
<Button
onClick={() => {
setModalAction('DELETION');
setDetailValue('');
}}
>
Mark Deletion
</Button>
</div>
<div style={{ marginTop: '12px' }}>
<Button
onClick={() => {
setModalAction('NO_COMPACTION');
setDetailValue('');
}}
>
Mark No Compaction
</Button>
</div>
{!disableAdminOperations && (
<div style={{ marginTop: '12px' }}>
<Button
onClick={() => {
setModalAction('DELETION');
setDetailValue('');
}}
>
Mark Deletion
</Button>
</div>
)}
{!disableAdminOperations && (
<div style={{ marginTop: '12px' }}>
<Button
onClick={() => {
setModalAction('NO_COMPACTION');
setDetailValue('');
}}
>
Mark No Compaction
</Button>
</div>
)}
<Modal isOpen={!!modalAction}>
<ModalBody>
<ModalHeader toggle={() => setModalAction('')}>
Expand Down