Skip to content

Commit

Permalink
console2: kv capacity (#795)
Browse files Browse the repository at this point in the history
  • Loading branch information
brig authored Jan 12, 2024
1 parent 2c2962b commit 679cd7a
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 6 deletions.
12 changes: 12 additions & 0 deletions console2/src/api/org/project/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,22 @@ export interface PaginatedProjectEntries {
next: boolean;
}

export interface KVCapacity {
size: number;
maxSize?: number;
}

export const get = (orgName: ConcordKey, projectName: ConcordKey): Promise<ProjectEntry> => {
return fetchJson<ProjectEntry>(`/api/v2/org/${orgName}/project/${projectName}`);
};

export const getCapacity = (
orgName: ConcordKey,
projectName: ConcordKey
): Promise<KVCapacity> => {
return fetchJson(`/api/v1/org/${orgName}/project/${projectName}/kv/capacity`);
};

export const list = async (
orgName: ConcordKey,
offset: number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ import {
import { LoadingDispatch } from '../../../App';
import { useApi } from '../../../hooks/useApi';
import { useCallback } from 'react';
import { get as apiGet, ProjectEntry } from '../../../api/org/project';
import { Divider, Header, Segment } from 'semantic-ui-react';
import {get as apiGet, getCapacity as apiGetCapacity, KVCapacity, ProjectEntry} from '../../../api/org/project';
import {Divider, Header, Icon, Progress, Segment} from 'semantic-ui-react';
import EntityId from '../../molecules/EntityId';

interface ExternalProps {
Expand All @@ -47,11 +47,13 @@ interface ExternalProps {
const ProjectSettings = ({ orgName, projectName, forceRefresh }: ExternalProps) => {
const dispatch = React.useContext(LoadingDispatch);

const fetchData = useCallback(() => {
return apiGet(orgName, projectName);
const fetchData = useCallback(async () => {
const project = await apiGet(orgName, projectName);
const capacity = await apiGetCapacity(orgName, projectName);
return { ...project, ...capacity };
}, [orgName, projectName]);

const { data, error, isLoading } = useApi<ProjectEntry>(fetchData, {
const { data, error, isLoading } = useApi<ProjectEntry & KVCapacity>(fetchData, {
fetchOnMount: true,
forceRequest: forceRefresh,
dispatch: dispatch
Expand All @@ -67,6 +69,11 @@ const ProjectSettings = ({ orgName, projectName, forceRefresh }: ExternalProps)
<EntityId id={data?.id} />
</Header>

<Segment disabled={isLoading}>
<Header as="h4">KV capacity</Header>
<Capacity org={data?.orgName} project={data?.name} current={data?.size} max={data?.maxSize} />
</Segment>

<Segment disabled={isLoading}>
<Header as="h4">Allow payload archives</Header>
{data && (
Expand Down Expand Up @@ -135,4 +142,45 @@ const ProjectSettings = ({ orgName, projectName, forceRefresh }: ExternalProps)
);
};

interface CapacityProps {
org?: ConcordKey;
project?: ConcordKey;
current?: number;
max?: number;
}

const Capacity = ({ org, project, current, max }: CapacityProps) => {
if (org === undefined || project === undefined) {
return (
<div>
<em>Loading</em>
</div>
);
}

return (
<>
{(current !== undefined && max !== undefined) &&
<div>
<Progress
percent={(current / max) * 100}
size={'tiny'}
color={'red'}
style={{ width: '30%' }}
/>
</div>
}
<Header size="tiny" style={{ marginTop: '0px', color: 'rgba(0, 0, 0, 0.5)' }}>
<a
href={`/api/v1/org/${org}/project/${project}/kv`}
rel="noopener noreferrer"
target="_blank">
<Icon name={'database'} color={'blue'} link={true}/>
Used {current} of {max || 'Unlimited'}
</a>
</Header>
</>
);
};

export default ProjectSettings;
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,12 @@ public CheckResult<KvRule, Integer> check(

return CheckResult.error(new CheckResult.Item<>(rule, count));
}

public Integer getMaxEntries() {
if (rule == null) {
return null;
}

return rule.maxEntries();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.walmartlabs.concord.server.org.project;

/*-
* *****
* Concord
* -----
* Copyright (C) 2017 - 2023 Walmart Inc.
* -----
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =====
*/

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;

import javax.annotation.Nullable;

@Value.Immutable
@JsonSerialize(as = ImmutableProjectKvCapacity.class)
@JsonDeserialize(as = ImmutableProjectKvCapacity.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
public interface ProjectKvCapacity {

long size();

@Nullable
Integer maxSize();

static ImmutableProjectKvCapacity.Builder builder() {
return ImmutableProjectKvCapacity.builder();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* =====
*/

import com.walmartlabs.concord.policyengine.PolicyEngine;
import com.walmartlabs.concord.process.loader.model.ProcessDefinition;
import com.walmartlabs.concord.server.OperationResult;
import com.walmartlabs.concord.server.audit.AuditAction;
Expand Down Expand Up @@ -57,6 +58,7 @@ public class ProjectManager {
private final ProjectDao projectDao;
private final RepositoryDao repositoryDao;
private final SecretDao secretDao;
private final KvDao kvDao;
private final ProjectRepositoryManager projectRepositoryManager;
private final ProjectAccessManager accessManager;
private final AuditLog auditLog;
Expand All @@ -69,6 +71,7 @@ public ProjectManager(OrganizationManager orgManager,
ProjectDao projectDao,
RepositoryDao repositoryDao,
SecretDao secretDao,
KvDao kvDao,
ProjectRepositoryManager projectRepositoryManager,
ProjectAccessManager accessManager,
AuditLog auditLog,
Expand All @@ -79,6 +82,7 @@ public ProjectManager(OrganizationManager orgManager,
this.projectDao = projectDao;
this.repositoryDao = repositoryDao;
this.secretDao = secretDao;
this.kvDao = kvDao;
this.projectRepositoryManager = projectRepositoryManager;
this.accessManager = accessManager;
this.auditLog = auditLog;
Expand Down Expand Up @@ -142,6 +146,25 @@ public ProjectOperationResult createOrUpdate(String orgName, ProjectEntry entry)
}
}

public ProjectKvCapacity getKvCapacity(String orgName, String projectName) {
OrganizationEntry org = orgManager.assertAccess(orgName, false);

ProjectEntry project = accessManager.assertAccess(org.getId(), null, projectName, ResourceAccessLevel.READER, false);

long currentSize = kvDao.count(project.getId());

PolicyEngine policy = policyManager.get(org.getId(), project.getId(), UserPrincipal.assertCurrent().getId());
Integer maxEntries = null;
if (policy != null) {
maxEntries = policy.getKvPolicy().getMaxEntries();
}

return ProjectKvCapacity.builder()
.size(currentSize)
.maxSize(maxEntries)
.build();
}

private UUID insert(UUID orgId, String orgName, ProjectEntry entry) {
UserEntry owner = getOwner(entry.getOwner(), UserPrincipal.assertCurrent().getUser());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* *****
* Concord
* -----
* Copyright (C) 2017 - 2018 Walmart Inc.
* Copyright (C) 2017 - 2023 Walmart Inc.
* -----
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -138,6 +138,19 @@ public List<KvEntry> findKV(@PathParam("orgName") @ConcordKey String orgName,
return kvDao.list(project.getId(), offset, limit, filter);
}

/**
* Get the KV capacity.
*/
@GET
@Path("/{orgName}/project/{projectName}/kv/capacity")
@Produces(MediaType.APPLICATION_JSON)
@Operation(description = "Get a project's KV capacity", operationId = "getProjectKVCapacity")
public ProjectKvCapacity getCapacity(@PathParam("orgName") @ConcordKey String orgName,
@PathParam("projectName") @ConcordKey String projectName) {

return projectManager.getKvCapacity(orgName, projectName);
}

@GET
@Path("/{orgName}/project/{projectName}/cfg{path: (.*)?}")
@Produces(MediaType.APPLICATION_JSON)
Expand Down

0 comments on commit 679cd7a

Please sign in to comment.