fix(namespace): create ns qa feedback [EE-2226] (#10474)

pull/10486/head
Ali 1 year ago committed by GitHub
parent bcb3f918d1
commit 07ec2ffe5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -11,6 +11,7 @@ type K8sNamespaceDetails struct {
Name string `json:"Name"`
Annotations map[string]string `json:"Annotations"`
ResourceQuota *K8sResourceQuota `json:"ResourceQuota"`
Owner string `json:"Owner"`
}
type K8sResourceQuota struct {

@ -63,15 +63,21 @@ func (kcl *KubeClient) GetNamespace(name string) (portainer.K8sNamespaceInfo, er
// CreateNamespace creates a new ingress in a given namespace in a k8s endpoint.
func (kcl *KubeClient) CreateNamespace(info models.K8sNamespaceDetails) error {
portainerLabels := map[string]string{
"io.portainer.kubernetes.resourcepool.name": info.Name,
"io.portainer.kubernetes.resourcepool.owner": info.Owner,
}
var ns v1.Namespace
ns.Name = info.Name
ns.Annotations = info.Annotations
ns.Labels = portainerLabels
resourceQuota := &v1.ResourceQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "portainer-rq-" + info.Name,
Namespace: info.Name,
Labels: portainerLabels,
},
Spec: v1.ResourceQuotaSpec{
Hard: v1.ResourceList{},

@ -17,7 +17,7 @@
class="searchInput"
ng-model="$ctrl.state.textFilter"
ng-change="$ctrl.onTextFilterChange()"
placeholder="Search for a namespace..."
placeholder="Search..."
auto-focus
ng-model-options="{ debounce: 300 }"
data-cy="k8sNamespace-namespaceSearchInput"

@ -5,6 +5,7 @@ import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { notifySuccess } from '@/portainer/services/notifications';
import { useCurrentEnvironment } from '@/react/hooks/useCurrentEnvironment';
import { useEnvironmentRegistries } from '@/react/portainer/environments/queries/useEnvironmentRegistries';
import { useCurrentUser } from '@/react/hooks/useUser';
import { Widget, WidgetBody } from '@@/Widget';
@ -30,6 +31,7 @@ export function CreateNamespaceForm() {
const { data: registries } = useEnvironmentRegistries(environmentId, {
hideDefault: true,
});
const { user } = useCurrentUser();
// for namespace create, show ingress classes that are allowed in the current environment.
// the ingressClasses show the none option, so we don't need to add it here.
const { data: ingressClasses } = useIngressControllerClassMapQuery({
@ -65,7 +67,7 @@ export function CreateNamespaceForm() {
<Formik
enableReinitialize
initialValues={initialValues}
onSubmit={handleSubmit}
onSubmit={(values) => handleSubmit(values, user.Username)}
validateOnMount
validationSchema={getNamespaceValidationSchema(
memoryLimit,
@ -78,9 +80,9 @@ export function CreateNamespaceForm() {
</Widget>
);
function handleSubmit(values: CreateNamespaceFormValues) {
function handleSubmit(values: CreateNamespaceFormValues, userName: string) {
const createNamespacePayload: CreateNamespacePayload =
transformFormValuesToNamespacePayload(values);
transformFormValuesToNamespacePayload(values, userName);
const updateRegistriesPayload: UpdateRegistryPayload[] =
values.registries.flatMap((registryFormValues) => {
// find the matching registry from the cluster registries

@ -15,6 +15,7 @@ export type CreateNamespaceFormValues = {
export type CreateNamespacePayload = {
Name: string;
Owner: string;
ResourceQuota: ResourceQuotaPayload;
};

@ -1,12 +1,14 @@
import { CreateNamespaceFormValues, CreateNamespacePayload } from './types';
export function transformFormValuesToNamespacePayload(
createNamespaceFormValues: CreateNamespaceFormValues
createNamespaceFormValues: CreateNamespaceFormValues,
owner: string
): CreateNamespacePayload {
const memoryInBytes =
Number(createNamespaceFormValues.resourceQuota.memory) * 10 ** 6;
return {
Name: createNamespaceFormValues.name,
Owner: owner,
ResourceQuota: {
enabled: createNamespaceFormValues.resourceQuota.enabled,
cpu: createNamespaceFormValues.resourceQuota.cpu,

@ -97,9 +97,7 @@ export function NamespaceInnerForm({
}
errors={errors.registries}
/>
{storageClasses.length > 0 && (
<StorageQuotaFormSection storageClasses={storageClasses} />
)}
{storageClasses.length > 0 && <StorageQuotaFormSection />}
<NamespaceSummary
initialValues={initialValues}
values={values}

@ -8,6 +8,7 @@ import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { InlineLoader } from '@@/InlineLoader';
import { FormControl } from '@@/form-components/FormControl';
import { FormSection } from '@@/form-components/FormSection';
import { TextTip } from '@@/Tip/TextTip';
import { RegistriesSelector } from './RegistriesSelector';
@ -24,10 +25,13 @@ export function RegistriesFormSection({ values, onChange, errors }: Props) {
});
return (
<FormSection title="Registries">
<TextTip color="blue" className="mb-2">
Define which registries can be used by users who have access to this
namespace.
</TextTip>
<FormControl
inputId="registries"
label="Select registries"
required
errors={errors}
>
{registriesQuery.isLoading && (

@ -35,22 +35,12 @@ export function ResourceQuotaFormSection({
return (
<FormSection title="Resource Quota">
{values.enabled ? (
<TextTip color="blue">
A namespace is a logical abstraction of a Kubernetes cluster, to
provide for more flexible management of resources. Best practice is to
set a quota assignment as this ensures greatest security/stability;
alternatively, you can disable assigning a quota for unrestricted
access (not recommended).
</TextTip>
) : (
<TextTip color="blue">
A namespace is a logical abstraction of a Kubernetes cluster, to
provide for more flexible management of resources. Resource
over-commit is disabled, please assign a capped limit of resources to
this namespace.
</TextTip>
)}
<TextTip color="blue">
A resource quota sets boundaries on the compute resources a namespace
can use. It&apos;s good practice to set a quota for a namespace to
manage resources effectively. Alternatively, you can disable assigning a
quota for unrestricted access (not recommended).
</TextTip>
<SwitchField
data-cy="k8sNamespaceCreate-resourceAssignmentToggle"

@ -1,15 +1,9 @@
import { StorageClass } from '@/react/portainer/environments/types';
import { FormSection } from '@@/form-components/FormSection';
import { TextTip } from '@@/Tip/TextTip';
import { StorageQuotaItem } from './StorageQuotaItem';
interface Props {
storageClasses: StorageClass[];
}
export function StorageQuotaFormSection({ storageClasses }: Props) {
export function StorageQuotaFormSection() {
return (
<FormSection title="Storage">
<TextTip color="blue">
@ -19,9 +13,7 @@ export function StorageQuotaFormSection({ storageClasses }: Props) {
this namespace.
</TextTip>
{storageClasses.map((storageClass) => (
<StorageQuotaItem key={storageClass.Name} storageClass={storageClass} />
))}
<StorageQuotaItem />
</FormSection>
);
}

@ -1,23 +1,18 @@
import { Database } from 'lucide-react';
import { StorageClass } from '@/react/portainer/environments/types';
import { FeatureId } from '@/react/portainer/feature-flags/enums';
import { Icon } from '@@/Icon';
import { FormSectionTitle } from '@@/form-components/FormSectionTitle';
import { SwitchField } from '@@/form-components/SwitchField';
type Props = {
storageClass: StorageClass;
};
export function StorageQuotaItem({ storageClass }: Props) {
export function StorageQuotaItem() {
return (
<div key={storageClass.Name}>
<div>
<FormSectionTitle>
<div className="vertical-center text-muted inline-flex gap-1 align-top">
<Icon icon={Database} className="!mt-0.5 flex-none" />
<span>{storageClass.Name}</span>
<span>standard</span>
</div>
</FormSectionTitle>
<hr className="mt-2 mb-0 w-full" />

Loading…
Cancel
Save