Compare commits

..

20 Commits

Author SHA1 Message Date
Ryan Zhang
f9fea8b53a Merge pull request #285 from wonderflow/dockerpackage
Add Image upload for Docker Hub and Github Docker package
2020-09-16 19:32:07 -07:00
Hongchao Deng
21c58c0aa2 Merge pull request #286 from oam-dev/warning
Add warning for not play around it for now
2020-09-16 16:43:43 -07:00
Lei Zhang (Harry)
d5a8b54503 Add warning for not play around it for now 2020-09-16 16:39:06 -07:00
天元
c390928368 split release and daily build 2020-09-16 22:57:56 +08:00
天元
c4897008dc add docker image build 2020-09-16 21:50:56 +08:00
天元
fe09a85e53 add docker build 2020-09-16 21:34:20 +08:00
Sun Jianbo
cc5e3dc6a2 Merge pull request #281 from oam-dev/install-dependency
vela core install dependencies
2020-09-16 17:29:02 +08:00
Ryan Zhang
8a9470b9b3 fix build error 2020-09-16 01:12:50 -07:00
Ryan Zhang
e0a21b2bd4 Merge pull request #268 from wonderflow/template
update chart and refactor default workload type
2020-09-15 22:52:32 -07:00
Ryan Zhang
dfeff25a31 install dependencies 2020-09-15 22:04:52 -07:00
天元
9510db0ace update chart and refactor default workload 2020-09-16 12:09:29 +08:00
Sun Jianbo
1576d1ff2b Merge pull request #280 from hanxie-crypto/feature03
Update code and page optimization ,fix bug
2020-09-16 11:37:03 +08:00
hanxie
764e1bbb79 Update code and page optimization ,fix bug 2020-09-16 11:03:16 +08:00
Sun Jianbo
83e718c4cd Merge pull request #261 from hanxie-crypto/feature01
update dashboard ux
2020-09-16 10:50:13 +08:00
Ryan Zhang
16fd51a213 adjust the helm setting and README (#269)
* add dependancy installer and fix e2e

* fix build

* add kubebuilder
2020-09-15 15:08:17 +08:00
Sun Jianbo
1744b4752c Merge pull request #270 from zzxwill/website
Remove website related files
2020-09-13 11:44:31 +08:00
zzxwill
7ad7848856 Remove website related files
Removed website related files and planning moving
the website to oam-dev/kubevela.io
2020-09-13 11:29:44 +08:00
Sun Jianbo
6df4e192f9 Merge pull request #267 from zzxwill/env
Rename `vela env switch` to `vela env set`
2020-09-11 21:40:36 +08:00
zzxwill
bc3169a1b5 Rename vela env switch to vela env set
Renaming and update cli documentation
Fix #235
2020-09-11 19:46:45 +08:00
hanxie
b026cf20f4 update dashboard ux 2020-09-11 14:43:08 +08:00
201 changed files with 3366 additions and 3194 deletions

25
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Publish to Registry
on:
push:
branches:
- master
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Publish to Github Docker Package Registry
uses: elgohr/Publish-Docker-Github-Action@2.21
with:
name: oam-dev/kubevela/vela-core
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry: docker.pkg.github.com
tags: "latest"
- name: Publish to Docker Hub Registry
uses: elgohr/Publish-Docker-Github-Action@2.21
with:
name: oamdev/vela-core
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
tags: "latest"

View File

@@ -34,6 +34,9 @@ jobs:
with:
version: "v0.7.0"
- name: install Kubebuilder
uses: RyanSiu1995/kubebuilder-action@v1
- name: Run Make
run: make

View File

@@ -30,5 +30,8 @@ jobs:
with:
version: "v0.7.0"
- name: install Kubebuilder
uses: RyanSiu1995/kubebuilder-action@v1
- name: Run Make test
run: make test

View File

@@ -61,4 +61,19 @@ jobs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./bin/vela-windows-amd64.exe
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-windows-amd64.exe
asset_content_type: binary/octet-stream
asset_content_type: binary/octet-stream
- name: Publish to Github Docker Package Registry
uses: elgohr/Publish-Docker-Github-Action@2.21
with:
name: oam-dev/kubevela/vela-core
username: $GITHUB_ACTOR
password: ${{ secrets.GITHUB_TOKEN }}
registry: docker.pkg.github.com
tags: "${{ steps.get_version.outputs.VERSION }}"
- name: Publish to Docker Hub Registry
uses: elgohr/Publish-Docker-Github-Action@2.21
with:
name: oamdev/vela-core
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
tags: "${{ steps.get_version.outputs.VERSION }}"

View File

@@ -80,10 +80,10 @@ Trait env init
Create env succeed, current env is default
------------------------------
Trait env switch
should show env switch message
Trait env set
should show env set message
kubevela/e2e/commonContext.go:40
Switch env succeed, current env is default
Set env succeed, current env is default
------------------------------
Trait run

View File

@@ -2,6 +2,8 @@
The Open Application Platform based on Kubernetes and OAM.
:rotating_light: **Warning: The project is still under heavy development, its UI/UX is also for demo purpose, please don't look inside unless you know what you are doing** Please contact @wonderflow if you are interested in its full story or becoming one of the boostrap contributors/maintainers. :rotating_light:
## Install
### Prerequisites
@@ -37,34 +39,27 @@ get 2 trait definitions from cluster, syncing...2 trait definitions successfully
## Demos
#### Create ENV
#### Check workloads
```
$ vela env init test --namespace test
Create env succeed, current env is test
$ vela env ls
NAME CURRENT NAMESPACE
default default
test * test
$ vela env switch default
Switch env succeed, current env is default
$ vela env delete test
test deleted
$ vela env delete default
Error: you can't delete current using default
$ vela workloads
NAME DEFINITION
backend containerizeds.standard.oam.dev
task jobs
webservice containerizeds.standard.oam.dev
```
#### workload run
```shell script
$ vela comp run -t deployment app123 -p 80 --image nginx:1.9.4
$ vela comp run -t webservice app123 -p 80 --image nginx:1.9.4
Creating AppConfig app123
SUCCEED
$ vela comp status app123
$ vela comp ls
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
app123 app123 deployment Deployed 2020-08-27 10:56:41 +0800 CST
```
#### app
@@ -72,15 +67,6 @@ $ vela comp status app123
```
$ vela app ls
app123
poc08032042
poc1039
$ vela comp ls
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
ccc ccc deployment Deployed 2020-08-27 10:56:41 +0800 CST
com1 com1 Deployed 2020-08-26 16:45:50 +0800 CST
com2 com1 Deployed 2020-08-26 16:45:50 +0800 CST
myapp myapp route,scale Deployed 2020-08-19 15:11:17 +0800 CST
$ vela app delete app123
Deleting AppConfig "app123"

View File

@@ -56,10 +56,11 @@ type Capability struct {
}
type Chart struct {
Repo string `json:"repo"`
URL string `json:"url"`
Name string `json:"name"`
Version string `json:"version"`
Repo string `json:"repo"`
URL string `json:"url"`
Name string `json:"name"`
Namespace string `json:"namespace,omitempty"`
Version string `json:"version"`
}
type Installation struct {

View File

@@ -0,0 +1,44 @@
#!/bin/bash
# Copyright 2019 The Knative Authors
#
# 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.
#!/usr/bin/env bash
# Download and unpack cert-manager
CERT_MANAGER_VERSION=1.0.0
ARCHIVE_DOWNLOAD_URL=https://github.com/jetstack/cert-manager/archive/v${CERT_MANAGER_VERSION}.tar.gz
YAML_URL=https://github.com/jetstack/cert-manager/releases/download/v${CERT_MANAGER_VERSION}/cert-manager.yaml
wget $ARCHIVE_DOWNLOAD_URL
tar xzf v${CERT_MANAGER_VERSION}.tar.gz
(
# subshell in downloaded directory
cd cert-manager-${CERT_MANAGER_VERSION} || exit
# Copy the CRD yaml file
cp deploy/manifests/00-crds.yaml ../cert-manager-crds.yaml
)
# Download the cert-manager yaml file
wget $YAML_URL
# Clean up.
rm -rf cert-manager-${CERT_MANAGER_VERSION}
rm v${CERT_MANAGER_VERSION}.tar.gz
# Add enable-certificate-owner-ref option to cert-manager's controller.
# The option is to cleans up secret(certificate) by adding ownerref.
patch -l cert-manager.yaml owner-ref.patch

View File

@@ -0,0 +1,465 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.4
creationTimestamp: null
name: servicemonitors.monitoring.coreos.com
spec:
group: monitoring.coreos.com
names:
kind: ServiceMonitor
listKind: ServiceMonitorList
plural: servicemonitors
singular: servicemonitor
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: ServiceMonitor defines monitoring for a set of services.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Specification of desired Service selection for target discovery
by Prometheus.
properties:
endpoints:
description: A list of endpoints allowed as part of this ServiceMonitor.
items:
description: Endpoint defines a scrapeable endpoint serving Prometheus
metrics.
properties:
basicAuth:
description: 'BasicAuth allow an endpoint to authenticate over
basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints'
properties:
password:
description: The secret in the service monitor namespace
that contains the password for authentication.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
username:
description: The secret in the service monitor namespace
that contains the username for authentication.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
type: object
bearerTokenFile:
description: File to read bearer token for scraping targets.
type: string
bearerTokenSecret:
description: Secret to mount to read bearer token for scraping
targets. The secret needs to be in the same namespace as the
service monitor and accessible by the Prometheus Operator.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
honorLabels:
description: HonorLabels chooses the metric's labels on collisions
with target labels.
type: boolean
honorTimestamps:
description: HonorTimestamps controls whether Prometheus respects
the timestamps present in scraped data.
type: boolean
interval:
description: Interval at which metrics should be scraped
type: string
metricRelabelings:
description: MetricRelabelConfigs to apply to samples before
ingestion.
items:
description: 'RelabelConfig allows dynamic rewriting of the
label set, being applied to samples before ingestion. It
defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
description: Action to perform based on regex matching.
Default is 'replace'
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex replace
is performed if the regular expression matches. Regex
capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
params:
additionalProperties:
items:
type: string
type: array
description: Optional HTTP URL parameters
type: object
path:
description: HTTP path to scrape for metrics.
type: string
port:
description: Name of the service port this endpoint refers to.
Mutually exclusive with targetPort.
type: string
proxyUrl:
description: ProxyURL eg http://proxyserver:2195 Directs scrapes
to proxy through this endpoint.
type: string
relabelings:
description: 'RelabelConfigs to apply to samples before scraping.
More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config'
items:
description: 'RelabelConfig allows dynamic rewriting of the
label set, being applied to samples before ingestion. It
defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
description: Action to perform based on regex matching.
Default is 'replace'
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex replace
is performed if the regular expression matches. Regex
capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
scheme:
description: HTTP scheme to use for scraping.
type: string
scrapeTimeout:
description: Timeout after which the scrape is ended
type: string
targetPort:
anyOf:
- type: integer
- type: string
description: Name or number of the pod port this endpoint refers
to. Mutually exclusive with port.
x-kubernetes-int-or-string: true
tlsConfig:
description: TLS configuration to use when scraping the endpoint
properties:
ca:
description: Stuct containing the CA cert to use for the
targets.
properties:
configMap:
description: ConfigMap containing data to use for the
targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap or its
key must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret or its key
must be defined
type: boolean
required:
- key
type: object
type: object
caFile:
description: Path to the CA cert in the Prometheus container
to use for the targets.
type: string
cert:
description: Struct containing the client cert file for
the targets.
properties:
configMap:
description: ConfigMap containing data to use for the
targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap or its
key must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret or its key
must be defined
type: boolean
required:
- key
type: object
type: object
certFile:
description: Path to the client cert file in the Prometheus
container for the targets.
type: string
insecureSkipVerify:
description: Disable target certificate validation.
type: boolean
keyFile:
description: Path to the client key file in the Prometheus
container for the targets.
type: string
keySecret:
description: Secret containing the client key file for the
targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
serverName:
description: Used to verify the hostname for the targets.
type: string
type: object
type: object
type: array
jobLabel:
description: The label to use to retrieve the job name from.
type: string
namespaceSelector:
description: Selector to select which namespaces the Endpoints objects
are discovered from.
properties:
any:
description: Boolean describing whether all namespaces are selected
in contrast to a list restricting them.
type: boolean
matchNames:
description: List of namespace names.
items:
type: string
type: array
type: object
podTargetLabels:
description: PodTargetLabels transfers labels on the Kubernetes Pod
onto the target.
items:
type: string
type: array
sampleLimit:
description: SampleLimit defines per-scrape limit on number of scraped
samples that will be accepted.
format: int64
type: integer
selector:
description: Selector to select Endpoints objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that
contains values, a key, and an operator that relates the key
and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to
a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
type: string
values:
description: values is an array of string values. If the
operator is In or NotIn, the values array must be non-empty.
If the operator is Exists or DoesNotExist, the values
array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator
is "In", and the values array contains only "value". The requirements
are ANDed.
type: object
type: object
targetLabels:
description: TargetLabels transfers labels on the Kubernetes Service
onto the target.
items:
type: string
type: array
required:
- endpoints
- selector
type: object
required:
- spec
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,36 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: backend
annotations:
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
definition.oam.dev/kind: "Containerized"
spec:
definitionRef:
name: containerizeds.standard.oam.dev
childResourceKinds:
- apiVersion: apps/v1
kind: Deployment
- apiVersion: v1
kind: Service
extension:
template: |
#Template: {
apiVersion: "standard.oam.dev/v1alpha1"
kind: "Containerized"
metadata:
name: backend.name
spec: {
containers: [{
image: backend.image
name: backend.name
}]
}
}
backend: {
name: string
// +usage=specify app image
// +short=i
image: string
}

View File

@@ -1,42 +0,0 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: backend-service
annotations:
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
definition.oam.dev/kind: "ContainerizedWorkload"
spec:
definitionRef:
name: containerizeds.standard.oam.dev
childResourceKinds:
- apiVersion: apps/v1
kind: Deployment
- apiVersion: v1
kind: Service
extension:
template: |
#Template: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ContainerizedWorkload"
metadata: name: containerized.name
spec: {
containers: [{
image: containerized.image
name: containerized.name
ports: [{
containerPort: containerized.port
protocol: "TCP"
name: "default"
}]
}]
}
}
containerized: {
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}

View File

@@ -1,43 +0,0 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: containerizeds.standard.oam.dev
annotations:
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
definition.oam.dev/kind: "ContainerizedWorkload"
spec:
definitionRef:
name: containerizeds.standard.oam.dev
childResourceKinds:
- apiVersion: apps/v1
kind: Deployment
- apiVersion: v1
kind: Service
extension:
template: |
#Template: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ContainerizedWorkload"
metadata:
name: containerized.name
spec: {
containers: [{
image: containerized.image
name: containerized.name
ports: [{
containerPort: containerized.port
protocol: "TCP"
name: "default"
}]
}]
}
}
containerized: {
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}

View File

@@ -2,6 +2,10 @@ apiVersion: core.oam.dev/v1alpha2
kind: ScopeDefinition
metadata:
name: healthscopes.core.oam.dev
annotations:
definition.oam.dev/apiVersion: core.oam.dev/v1alpha2
definition.oam.dev/kind: HealthScope
namespace: default
spec:
workloadRefsPath: spec.workloadRefs
allowComponentOverlap: true

View File

@@ -5,6 +5,7 @@ metadata:
definition.oam.dev/apiVersion: core.oam.dev/v1alpha2
definition.oam.dev/kind: ManualScalerTrait
name: manualscalertraits.core.oam.dev
namespace: default
spec:
appliesToWorkloads:
- core.oam.dev/v1alpha2.ContainerizedWorkload

View File

@@ -2,6 +2,10 @@ apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: metricstraits.standard.oam.dev
namespace: default
annotations:
definition.oam.dev/apiVersion: core.oam.dev/v1alpha2
definition.oam.dev/kind: MetricsTrait
spec:
appliesToWorkloads:
- containerizedworkloads.core.oam.dev
@@ -10,32 +14,4 @@ spec:
- statefulsets.apps
definitionRef:
name: metricstraits.standard.oam.dev
workloadRefPath: spec.workloadRef
extension:
template: |
#Template: {
apiVersion: "standard.oam.dev/v1alpha1"
kind: "MetricsTrait"
metadata:
name: metricstraits.name
spec: {
containers: [{
image: containerized.image
name: containerized.name
ports: [{
containerPort: containerized.port
protocol: "TCP"
name: "default"
}]
}]
}
}
containerized: {
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}
workloadRefPath: spec.workloadRef

View File

@@ -14,34 +14,34 @@ spec:
- logging
template: |
#Template: {
apiVersion: "v1"
kind: "Job"
metadata: name: task
spec: {
parallelism: taskSpec.count
completions: taskSpec.count
template:
spec:
containers: [{
image: taskSpec.image
name: taskSpec.name
ports: [{
containerPort: taskSpec.port
protocol: "TCP"
name: "default"
}]
}]
}
}
taskSpec: {
// +usage=specify number of tasks to run in parallel
// +short=c
count: *1 | int
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}
apiVersion: "v1"
kind: "Job"
metadata: name: task.name
spec: {
parallelism: task.count
completions: task.count
template:
spec:
containers: [{
image: task.image
name: task.name
ports: [{
containerPort: task.port
protocol: "TCP"
name: "default"
}]
}]
}
}
task: {
// +usage=specify number of tasks to run in parallel
// +short=c
count: *1 | int
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: vela-config
namespace: default
data:
certificates.cert-manager.io: |
{
"repo": "jetstack",
"urL": "https://charts.jetstack.io",
"name": "cert-manager",
"namespace": "cert-manager",
"version": "1.0.0"
}

View File

@@ -1,10 +1,10 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: web-service
name: webservice
annotations:
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
definition.oam.dev/kind: "ContainerizedWorkload"
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
definition.oam.dev/kind: "Containerized"
spec:
definitionRef:
name: containerizeds.standard.oam.dev
@@ -16,23 +16,24 @@ spec:
extension:
template: |
#Template: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ContainerizedWorkload"
apiVersion: "standard.oam.dev/v1alpha1"
kind: "Containerized"
metadata:
name: containerized.name
name: webservice.name
spec: {
containers: [{
image: containerized.image
name: containerized.name
ports: [{
containerPort: containerized.port
protocol: "TCP"
name: "default"
}]
image: webservice.image
name: webservice.name
ports: [{
containerPort: webservice.port
protocol: "TCP"
name: "default"
}]
}]
}
}
containerized: {
webservice: {
name: string
// +usage=specify app image
// +short=i
@@ -40,4 +41,4 @@ spec:
// +usage=specify port for container
// +short=p
port: *6379 | int
}
}

View File

@@ -6,7 +6,7 @@ replicaCount: 1
useWebhook: true
image:
repository: oamdev/vela-core
tag: 0.1
tag: 0.3
pullPolicy: IfNotPresent
imagePullSecrets: []

View File

@@ -18,13 +18,16 @@ import (
"github.com/oam-dev/trait-injector/pkg/plugin"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
velacore "github.com/oam-dev/kubevela/api/v1alpha1"
velacontroller "github.com/oam-dev/kubevela/pkg/controller"
"github.com/oam-dev/kubevela/pkg/controller/dependency"
velawebhook "github.com/oam-dev/kubevela/pkg/webhook"
)
@@ -32,6 +35,7 @@ var scheme = runtime.NewScheme()
func init() {
_ = clientgoscheme.AddToScheme(scheme)
_ = crdv1.AddToScheme(scheme)
_ = oamcore.AddToScheme(scheme)
_ = monitoring.AddToScheme(scheme)
_ = velacore.AddToScheme(scheme)
@@ -93,6 +97,17 @@ func main() {
os.Exit(1)
}
k8sClient, err := client.New(ctrl.GetConfigOrDie(), client.Options{Scheme: scheme})
if err != nil {
setupLog.Error(err, "unable to create a kubernetes client")
os.Exit(1)
}
if err = dependency.Install(k8sClient); err != nil {
setupLog.Error(err, "unable to install the dependency")
os.Exit(1)
}
if useWebhook {
setupLog.Info("vela webhook enabled, will serving at :" + strconv.Itoa(webhookPort))
oamwebhook.Add(mgr)

View File

@@ -129,6 +129,7 @@ func newCommand() *cobra.Command {
fset := flag.NewFlagSet("logs", flag.ContinueOnError)
klog.InitFlags(fset)
_ = fset.Set("v", "-1")
return cmds
}

View File

@@ -12,9 +12,7 @@ export default defineConfig({
hmr: true,
},
locale: {
// default zh-CN
default: 'en-US',
// default true, when it is true, will use `navigator.language` overwrite default
antd: false,
baseNavigator: false,
},
@@ -36,13 +34,11 @@ export default defineConfig({
routes: [
{
path: '/',
// redirect: `/${envname}/ApplicationList`,
redirect: `/ApplicationList`,
},
{
name: 'ApplicationList',
icon: 'table',
// path: `/${envname}/ApplicationList`,
path: `/ApplicationList`,
component: './ApplicationList',
},
@@ -77,25 +73,6 @@ export default defineConfig({
icon: 'table',
path: '/Workload',
routes: [
// {
// name: 'Deployment',
// icon: 'table',
// path: '/Workload/Deployment',
// component: './Workload/Deployment',
// },
// {
// name: 'Containerized',
// icon: 'smile',
// path: '/Workload/Containerized',
// component: './Workload/Containerized',
// },
// {
// name: 'Detail',
// icon: 'smile',
// path: '/Workload/Detail',
// component: './Workload/Detail',
// hideInMenu: true,
// },
{
name: 'WorkloadItem',
icon: 'smile',
@@ -109,25 +86,6 @@ export default defineConfig({
name: 'Traits',
icon: 'table',
routes: [
// {
// name: 'Scale',
// icon: 'table',
// path: '/Traits/Scale',
// component: './Traits/Scale',
// },
// {
// name: 'Rollout',
// icon: 'smile',
// path: '/Traits/Rollout',
// component: './Traits/Rollout',
// },
// {
// name: 'Detail',
// icon: 'smile',
// path: '/Traits/Detail',
// component: './Traits/Detail',
// hideInMenu: true,
// },
{
name: 'TraitItem',
icon: 'smile',
@@ -136,12 +94,6 @@ export default defineConfig({
},
],
},
// {
// name: 'Release',
// icon: 'table',
// path: '/Release',
// component: './Release',
// },
{
name: 'Capability',
icon: 'table',
@@ -183,8 +135,13 @@ export default defineConfig({
],
// Theme for antd: https://ant.design/docs/react/customize-theme-cn
theme: {
// ...darkTheme,
// 主题配置
'primary-color': defaultSettings.primaryColor,
'link-color': defaultSettings.linkColor,
'link-hover-color': defaultSettings.linkHoverColor,
'disabled-bg': defaultSettings.disabledBg,
'disabled-color': defaultSettings.disabledColor,
'btn-disable-color': defaultSettings.btnDisableColor,
},
// @ts-ignore
title: false,

View File

@@ -1,14 +1,19 @@
const proSettings = {
navTheme: 'dark',
// 拂晓蓝
primaryColor: '#1890ff',
// 主题颜色配置
primaryColor: '#1B58F4', // 全局主色
linkColor: '#1B58F4', // 链接色
linkHoverColor: '#1B58F4',
disabledBg: '#EBEBEB', // 失效背景色,
disabledColor: '#BEBEBE', // 失效文本色,
btnDisableColor: '#A4A4A4', // 禁用btn文字颜色
layout: 'side',
contentWidth: 'Fluid',
fixedHeader: false,
fixSiderbar: true,
colorWeak: false,
menu: {
locale: true,
locale: false,
},
title: 'Micro App Engine',
pwa: false,

View File

@@ -8,11 +8,8 @@
export default {
dev: {
'/api': {
target: 'http://123.56.222.218:8081/',
target: 'http://30.11.171.17:38081/',
changeOrigin: true,
// pathRewrite: {
// "^/api": "",
// },
},
},
test: {

View File

@@ -35,34 +35,40 @@ export default class CreateTraitItem extends React.PureComponent {
return this.formRefStep2.current.getFieldsValue();
};
validateFields = () => {
return this.formRefStep2.current.validateFields();
};
setDefaultValue = (traitType) => {
this.formRefStep2.current.setFieldsValue({ name: traitType });
this.traitSelectChange(traitType);
};
traitSelectChange = async (value, isType = 1) => {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
payload: {
traitName: value,
},
});
this.setState({
parameters: res.parameters,
});
if (isType === 2) {
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
} else if (isType) {
// 进行默认值填写
const parameters = _.get(res, 'parameters', []);
if (parameters.length) {
const initialObj = {};
parameters.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
this.formRefStep2.current.setFieldsValue(initialObj);
if (value) {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
payload: {
traitName: value,
},
});
this.setState({
parameters: res.parameters,
});
if (isType === 2) {
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
} else if (isType) {
// 进行默认值填写
const parameters = _.get(res, 'parameters', []);
if (parameters.length) {
const initialObj = {};
parameters.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
this.formRefStep2.current.setFieldsValue(initialObj);
}
}
}
};
@@ -77,8 +83,12 @@ export default class CreateTraitItem extends React.PureComponent {
name="control-ref"
className="traitItem"
>
<Form.Item name="name" label="Trait">
<Select placeholder="Select a Trait" allowClear onChange={this.traitSelectChange}>
<Form.Item
name="name"
label="Trait"
rules={[{ required: true, message: 'Please Select a Trait!' }]}
>
<Select placeholder="Select a Trait" onChange={this.traitSelectChange}>
{availableTraitList.map((item) => {
return (
<Option value={item.name} key={item.name}>
@@ -92,8 +102,34 @@ export default class CreateTraitItem extends React.PureComponent {
<div className="relativeBox">
{this.state.parameters ? (
this.state.parameters.map((item) => {
return (
<Form.Item name={item.name} label={item.name} key={item.name}>
return item.type === 4 ? (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
]}
>
<Input />
</Form.Item>
) : (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
]}
>
<Input />
</Form.Item>
);

View File

@@ -23,9 +23,8 @@ export default class WorkSpaceDropDown extends React.Component {
return env.current === '*';
});
this.setState({
envs: envs,
workSpaceName: envName,
namespace: namespace,
namespace,
});
this.props.dispatch({
type: 'globalData/currentEnv',

View File

@@ -1,18 +1,30 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Row, Col, Modal, Select, message } from 'antd';
import { Button, Row, Col, Modal, Select, message, Breadcrumb, Form, Input } from 'antd';
import './index.less';
import { connect } from 'dva';
import { Link } from 'umi';
import _ from 'lodash';
const { Option } = Select;
const layout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
@connect(({ loading, applist, globalData }) => ({
loadingAll: loading.models.applist,
currentEnv: globalData.currentEnv,
returnObj: applist.returnObj,
}))
class Trait extends React.Component {
formRefStep2 = React.createRef();
constructor(props) {
super(props);
this.state = {
@@ -37,28 +49,61 @@ class Trait extends React.Component {
};
showModal = () => {
this.setState({
visible: true,
});
this.setState(
{
visible: true,
},
() => {
if (this.formRefStep2.current) {
this.formRefStep2.current.resetFields();
}
},
);
};
handleOk = () => {
const { selectValue } = this.state;
if (selectValue) {
this.setState({
visible: false,
handleOk = async () => {
await this.formRefStep2.current.validateFields();
const { title } = this.props.propsObj;
if (title) {
const submitObj = {
name: title,
flags: [],
};
const submitData = this.formRefStep2.current.getFieldValue();
Object.keys(submitData).forEach((currentKey) => {
if (currentKey !== 'name' && currentKey !== 'appName' && submitData[currentKey]) {
submitObj.flags.push({
name: currentKey,
value: submitData[currentKey].toString(),
});
}
});
const { history } = this.props.propsObj;
history.push({
pathname: '/ApplicationList/ApplicationListDetail',
state: {
appName: selectValue,
envName: this.props.currentEnv,
traitType: this.props.propsObj.title,
},
});
} else {
message.warn('please select a application');
const { currentEnv: envName } = this.props;
const { appName } = submitData;
if (envName && appName) {
const res = await this.props.dispatch({
type: 'trait/attachOneTraits',
payload: {
envName,
appName,
params: submitObj,
},
});
if (res) {
this.setState({
visible: false,
});
message.success(res);
const { history } = this.props.propsObj;
history.push({
pathname: '/ApplicationList/ApplicationListDetail',
state: {
appName,
envName,
},
});
}
}
}
};
@@ -77,98 +122,175 @@ class Trait extends React.Component {
onSearch = () => {};
render() {
const { btnValue, title, settings, btnIsShow, crdInfo, appliesTo } = this.props.propsObj;
const { btnValue, title, settings = [], btnIsShow, crdInfo, appliesTo } = this.props.propsObj;
const initialObj = {};
if (settings.length) {
settings.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
}
const appList = _.get(this.props, 'returnObj', []);
return (
<PageContainer>
<Row>
<Col span="11">
<div className="deployment">
<Row>
<Col span="22">
<p className="title">{title}</p>
{crdInfo ? (
<p>
{crdInfo.apiVersion}
<span>,kind=</span>
{crdInfo.kind}
</p>
) : (
<p />
)}
</Col>
</Row>
<Row>
<Col span="22">
<p className="title">Applies To</p>
<p>{Array.isArray(appliesTo) ? appliesTo.join(', ') : appliesTo}</p>
</Col>
</Row>
<p className="title">Configurable Properties:</p>
{settings.map((item, index) => {
return (
<Row key={index.toString()}>
<Col span="8">
<p>{item.name}</p>
</Col>
<Col span="16">
<p>{item.default || item.usage}</p>
</Col>
</Row>
);
})}
</div>
<Button
type="primary"
className="create-button"
onClick={this.showModal}
style={{ display: btnIsShow ? 'block' : 'none' }}
>
{btnValue}
</Button>
<Modal
title="Select a Application"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Next
</Button>,
]}
>
<Select
showSearch
allowClear
value={this.state.selectValue}
style={{ width: '100%' }}
placeholder="Select a Application"
optionFilterProp="children"
onChange={this.onChange}
onSearch={this.onSearch}
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Traits</Breadcrumb.Item>
<Breadcrumb.Item>{title}</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Row>
<Col span="11">
<div className="deployment">
<Row>
<Col span="22">
<p className="title">{title}</p>
{crdInfo ? (
<p>
{crdInfo.apiVersion}
<span>,kind=</span>
{crdInfo.kind}
</p>
) : (
<p />
)}
</Col>
</Row>
<Row>
<Col span="22">
<p className="title">Applies To:</p>
<p>{Array.isArray(appliesTo) ? appliesTo.join(', ') : appliesTo}</p>
</Col>
</Row>
<p className="title">Configurable Properties:</p>
{settings.map((item, index) => {
return (
<Row key={index.toString()}>
<Col span="8">
<p>{item.name}</p>
</Col>
<Col span="16">
<p>{item.default || item.usage}</p>
</Col>
</Row>
);
})}
</div>
<Button
type="primary"
className="create-button"
onClick={this.showModal}
style={{ display: btnIsShow ? 'block' : 'none' }}
>
{appList.length ? (
appList.map((item) => {
return (
<Option key={item.name} value={item.name}>
{item.name}
</Option>
);
})
) : (
<Fragment />
)}
</Select>
</Modal>
</Col>
</Row>
</PageContainer>
{btnValue}
</Button>
<Modal
title="Attach Trait"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Submit
</Button>,
]}
>
<Form
labelAlign="left"
{...layout}
ref={this.formRefStep2}
name="control-ref"
className="traitItem"
initialValues={initialObj}
>
<Form.Item
label="Target"
name="appName"
rules={[{ required: true, message: 'Please Select a Application!' }]}
>
<Select
showSearch
allowClear
value={this.state.selectValue}
style={{ width: '100%' }}
placeholder="Select a Application"
optionFilterProp="children"
onChange={this.onChange}
onSearch={this.onSearch}
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{appList.length ? (
appList.map((item) => {
return (
<Option key={item.name} value={item.name}>
{item.name}
</Option>
);
})
) : (
<Fragment />
)}
</Select>
</Form.Item>
<div className="relativeBox">
<Form.Item label="Properties" />
{settings ? (
settings.map((item) => {
return item.type === 4 ? (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{
pattern: /^[0-9]*$/,
message: `${item.name} only use digits(0-9).`,
},
]}
>
<Input />
</Form.Item>
) : (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
]}
>
<Input />
</Form.Item>
);
})
) : (
<></>
)}
</div>
</Form>
</Modal>
</Col>
</Row>
</PageContainer>
</div>
);
}
}

View File

@@ -1,69 +1,70 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Row, Col } from 'antd';
import { Button, Row, Col, Breadcrumb } from 'antd';
import { Link } from 'umi';
import './index.less';
export default class Workload extends React.PureComponent {
render() {
const {
btnValue,
pathname,
title,
crdInfo,
state,
settings,
hrefAddress,
btnIsShow,
} = this.props.propsObj;
const { btnValue, pathname, title, crdInfo, state, settings, btnIsShow } = this.props.propsObj;
return (
<PageContainer>
<Row>
<Col span="11">
<div className="deployment">
<a href={hrefAddress}>?</a>
<Row>
<Col span="22">
<p className="title">{title}</p>
{crdInfo ? (
<p>
{crdInfo.apiVersion}
<span>,kind=</span>
{crdInfo.kind}
</p>
) : (
<p />
)}
</Col>
</Row>
<p className="title">Configurable Settings:</p>
{settings.map((item, index) => {
if (item.name === 'name') {
return <Fragment key={index.toString()} />;
}
return (
<Row key={index.toString()}>
<Col span="8">
<p>{item.name}</p>
</Col>
<Col span="16">
{
// eslint-disable-next-line consistent-return
}
<p>{item.default || item.usage}</p>
</Col>
</Row>
);
})}
</div>
<Link to={{ pathname, state }} style={{ display: btnIsShow ? 'block' : 'none' }}>
<Button type="primary" className="create-button">
{btnValue}
</Button>
</Link>
</Col>
</Row>
</PageContainer>
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Workloads</Breadcrumb.Item>
<Breadcrumb.Item>{title}</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Row>
<Col span="11">
<div className="deployment">
<Row>
<Col span="22">
<p className="title">{title}</p>
{crdInfo ? (
<p>
{crdInfo.apiVersion}
<span>,kind=</span>
{crdInfo.kind}
</p>
) : (
<p />
)}
</Col>
</Row>
<p className="title">Configurable Settings:</p>
{settings.map((item, index) => {
if (item.name === 'name') {
return <Fragment key={index.toString()} />;
}
return (
<Row key={index.toString()}>
<Col span="8">
<p>{item.name}</p>
</Col>
<Col span="16">
{
// eslint-disable-next-line consistent-return
}
<p>{item.default || item.usage}</p>
</Col>
</Row>
);
})}
</div>
<Link to={{ pathname, state }} style={{ display: btnIsShow ? 'block' : 'none' }}>
<Button type="primary" className="create-button">
{btnValue}
</Button>
</Link>
</Col>
</Row>
</PageContainer>
</div>
);
}
}

View File

@@ -29,13 +29,6 @@ beforeEach(async () => {
describe('Ant Design Pro E2E test', () => {
const testPage = (path) => async () => {
await page.goto(`${BASE_URL}${path}`);
await page.waitForSelector('footer', {
timeout: 2000,
});
const haveFooter = await page.evaluate(
() => document.getElementsByTagName('footer').length > 0,
);
expect(haveFooter).toBeTruthy();
};
const routers = formatter(RouterConfig);
@@ -46,12 +39,5 @@ describe('Ant Design Pro E2E test', () => {
it('topmenu should have footer', async () => {
const params = '?navTheme=light&layout=topmenu';
await page.goto(`${BASE_URL}${params}`);
await page.waitForSelector('footer', {
timeout: 2000,
});
const haveFooter = await page.evaluate(
() => document.getElementsByTagName('footer').length > 0,
);
expect(haveFooter).toBeTruthy();
});
});

View File

@@ -69,3 +69,19 @@ ol {
.ant-page-header-heading {
display: none !important;
}
.ant-pro-page-container-warp {
display: none;
}
.ant-pro-basicLayout-content {
margin: 0 !important;
}
.ant-pro-basicLayout-content .ant-pro-page-container {
margin: 0 !important;
}
.breadCrumb {
padding: 12px 24px;
background: #fff;
}
.ant-breadcrumb a:hover {
color: #1b58f4 !important;
}

View File

@@ -4,7 +4,7 @@
* https://github.com/ant-design/ant-design-pro-layout
*/
import ProLayout from '@ant-design/pro-layout';
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { Link, useIntl, connect, history } from 'umi';
import RightContent from '@/components/GlobalHeader/RightContent';
import {
@@ -16,15 +16,6 @@ import {
} from '@ant-design/icons';
import _ from 'lodash';
// const menuDataRender = (menuList) => {
// return menuList.map((item) => {
// const localItem = {
// ...item,
// children: item.children ? menuDataRender(item.children) : undefined,
// };
// return localItem;
// });
// };
const AddIcon = (menuData) => {
return menuData.map((item) => {
const name = _.get(item, 'name', '');
@@ -52,18 +43,37 @@ const AddIcon = (menuData) => {
const BasicLayout = (props) => {
const { settings, dispatch, menus } = props;
const [currentSelectKeys, setCurrentSelectedKeys] = useState('');
const getCurrentSelectKeys = () => {
const pathnameCur = props.history.location.pathname;
if (pathnameCur) {
if (pathnameCur.includes('Application')) {
setCurrentSelectedKeys(['applist']);
} else if (pathnameCur.includes('Capability')) {
setCurrentSelectedKeys(['Capability']);
} else if (pathnameCur.includes('System/Env')) {
setCurrentSelectedKeys(['Env']);
} else if (pathnameCur.includes('Workload')) {
const arr = pathnameCur.split('/');
const key = arr[arr.length - 1];
setCurrentSelectedKeys([key]);
} else if (pathnameCur.includes('Traits')) {
const arr = pathnameCur.split('/');
const key = arr[arr.length - 1];
setCurrentSelectedKeys([key]);
}
}
};
useEffect(() => {
if (dispatch) {
// dispatch({
// type: 'user/fetchCurrent',
// });
// dispatch({
// type: 'settings/getSetting',
// });
dispatch({
type: 'menus/getMenuData',
});
}
props.history.listen((route) => {
getCurrentSelectKeys(route.pathname);
});
// setCurrentSelectedKeys('applist')
}, []);
const { formatMessage } = useIntl();
@@ -75,9 +85,19 @@ const BasicLayout = (props) => {
if (menuItemProps.isUrl || !menuItemProps.path) {
return defaultDom;
}
return <Link to={menuItemProps.path}>{defaultDom}</Link>;
// return <Link to={menuItemProps.path}>{defaultDom}</Link>;
return (
<div
onClick={() => {
setCurrentSelectedKeys([menuItemProps.key]);
history.push(menuItemProps.path);
}}
>
{defaultDom}
</div>
);
}}
selectedKeys={currentSelectKeys}
breadcrumbRender={(routers = []) => [
{
path: '/',

View File

@@ -2,17 +2,15 @@ import { getapplist, createApp, getAppDetail, deleteApp } from '@/services/appli
const TestModel = {
namespace: 'applist',
state: {
// initailState: '8880'
},
state: {},
effects: {
*getList({ payload }, { call, put }) {
const res = yield call(getapplist, payload);
// getlist是引入services层那个js文件的getlist方法payload是后台要求传递的参数res就是后台返过来的数据
yield put({
type: 'addList', // 这就是reducer的addNum方法put用来触发reducer中的方法payload是传过去的参数。同时也能触发同等级effects中的方法
type: 'addList',
payload: {
returnObj: res, // 把后台返回的数据赋值给num,假如哪个reducer中的方法是由这里effects去触发的哪个num名必须是这里的名字num如果reducer中的方法不是这触发那名字可以随意取
returnObj: res,
},
});
},

View File

@@ -10,9 +10,7 @@ import {
const TestModel = {
namespace: 'capability',
state: {
// initailState: '8880'
},
state: {},
effects: {
*getCapabilityCenterlist({ payload }, { call }) {
const res = yield call(getCapabilityCenterlist, payload);

View File

@@ -6,7 +6,7 @@ const globalModel = {
effects: {
*currentEnv({ payload }, { put }) {
yield put({
type: 'setCurrentEnv', // 这就是reducer的addNum方法put用来触发reducer中的方法payload是传过去的参数。同时也能触发同等级effects中的方法
type: 'setCurrentEnv',
payload,
});
},

View File

@@ -1,36 +1,31 @@
import { capabilityList } from '@/services/capability.js';
import { getTraits } from '@/services/trait.js';
import { getWorkload } from '@/services/workload.js';
function getMenuList(response) {
function getMenuList(workload, trait) {
let workloadList = [];
let traitList = [];
// eslint-disable-next-line no-param-reassign
response = response.filter((item) => {
return item.status === 'installed';
});
response.forEach((item) => {
if (item.type === 'workload') {
workloadList.push(item.name);
} else if (item.type === 'trait') {
traitList.push(item.name);
}
});
// 在此之前要对workloadList和traitList进行一次去重操作
workloadList = workloadList.map((item) => {
// eslint-disable-next-line no-param-reassign
item = item.charAt(0).toUpperCase() + item.slice(1);
return {
name: item,
path: `/Workload/${item}`,
};
});
traitList = traitList.map((item) => {
// eslint-disable-next-line no-param-reassign
item = item.charAt(0).toUpperCase() + item.slice(1);
return {
name: item,
path: `/Traits/${item}`,
};
});
if (workload) {
workloadList = workload.map((item) => {
let name1 = item.name;
name1 = name1.charAt(0).toUpperCase() + name1.slice(1);
return {
name: name1,
path: `/Workload/${name1}`,
key: name1,
};
});
}
if (trait) {
traitList = trait.map((item) => {
let name1 = item.name;
name1 = name1.charAt(0).toUpperCase() + name1.slice(1);
return {
name: name1,
path: `/Traits/${name1}`,
key: name1,
};
});
}
// 只是动态生成侧边栏(name,path,icon)路由还是config.js里面配置的路由
const menuList = [
{
@@ -41,6 +36,7 @@ function getMenuList(response) {
name: 'ApplicationList',
icon: 'Table',
path: `/ApplicationList`,
key: 'applist',
},
{
name: 'ApplicationList.ApplicationListDetail',
@@ -76,13 +72,10 @@ function getMenuList(response) {
},
],
},
// {
// name: 'Release',
// path: '/Release',
// },
{
name: 'Capability',
path: '/Capability',
key: 'Capability',
},
{
path: '/System',
@@ -91,6 +84,7 @@ function getMenuList(response) {
{
name: 'Env',
path: '/System/Env',
key: 'Env',
},
],
},
@@ -110,8 +104,9 @@ const TestModel = {
},
effects: {
*getMenuData({ payload }, { call, put }) {
let response = yield call(capabilityList, payload);
response = getMenuList(response);
const workloadList = yield call(getWorkload, payload);
const traitList = yield call(getTraits, payload);
const response = getMenuList(workloadList, traitList);
yield put({
type: 'saveMenuData',
payload: response,

View File

@@ -2,9 +2,7 @@ import { getTraitByName, getTraits, attachOneTraits, deleteOneTrait } from '@/se
const TestModel = {
namespace: 'trait',
state: {
// initailState: '8880'
},
state: {},
effects: {
*getTraitByName({ payload }, { call }) {
const res = yield call(getTraitByName, payload);

View File

@@ -2,9 +2,7 @@ import { createWorkload, getWorkload, getWorkloadByName } from '@/services/workl
const TestModel = {
namespace: 'workload',
state: {
// initailState: '8880'
},
state: {},
effects: {
*createWorkload({ payload }, { call }) {
const res = yield call(createWorkload, payload);

View File

@@ -43,8 +43,6 @@ const Topology = () => {
],
};
// const width = document.getElementById('container').scrollWidth;
// const height = document.getElementById('container').scrollHeight || 500;
const width = 1000;
const height = 400;

View File

@@ -1,11 +1,22 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import './index.less';
import { Button, Row, Col, Tabs, Popconfirm, message, Tooltip, Modal, Spin } from 'antd';
import {
Button,
Row,
Col,
Tabs,
Popconfirm,
message,
Tooltip,
Modal,
Spin,
Breadcrumb,
} from 'antd';
import { connect } from 'dva';
import _ from 'lodash';
import { Link } from 'umi';
import CreateTraitItem from '../../../components/AttachOneTrait/index.jsx';
import Topology from './Topology.jsx';
const { TabPane } = Tabs;
@@ -54,18 +65,18 @@ class TableList extends React.Component {
const traits = await this.props.dispatch({
type: 'trait/getTraits',
});
this.setState({
traitList: traits,
});
if (traits) {
this.setState({
traitList: traits,
});
}
const workloadType = _.get(res, 'Workload.workload.kind', '');
if (workloadType && workloadType === 'ContainerizedWorkload') {
this.getAcceptTrait('containerized');
} else if (workloadType && workloadType === 'Deployment') {
this.getAcceptTrait('deployment');
}
// 如果traitType存在是从特定trait跳转来新增单个trait的
if (traitType && times === 1) {
// this.createTrait(traitType)
await this.setState({
visible: true,
});
@@ -88,7 +99,7 @@ class TableList extends React.Component {
deleteApp = async (e) => {
e.stopPropagation();
const { currentEnv: envName } = this.props;
const { envName } = this.state;
const { appDetailData } = this.state;
const appName = _.get(appDetailData, 'Workload.workload.metadata.name', '');
if (appName && envName) {
@@ -138,6 +149,7 @@ class TableList extends React.Component {
};
handleOk = async () => {
await this.child.validateFields();
const submitData = this.child.getSelectValue();
if (submitData.name) {
const submitObj = {
@@ -187,26 +199,10 @@ class TableList extends React.Component {
gotoWorkloadDetail = (e) => {
e.stopPropagation();
const appName = _.get(this.props, 'location.state.appName', '');
const envName = _.get(this.props, 'location.state.envName', '');
if (appName && envName) {
this.props.history.push({
pathname: '/ApplicationList/WorkloadDetail',
state: { appName, envName },
});
}
};
gotoTraitDetail = (e, traitItem) => {
gotoTraitDetail = (e) => {
e.stopPropagation();
const appName = _.get(this.props, 'location.state.appName', '');
const envName = _.get(this.props, 'location.state.envName', '');
if (appName && envName) {
this.props.history.push({
pathname: '/ApplicationList/TraitDetail',
state: { traitItem, appName, envName },
});
}
};
render() {
@@ -214,277 +210,277 @@ class TableList extends React.Component {
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
const Traits = _.get(this.state.appDetailData, 'Traits', []);
let containers = {};
// if (Workload.kind === 'ContainerizedWorkload') {
// containers = _.get(Workload, 'spec.containers[0]', {});
// } else if (Workload.kind === 'Deployment') {
// containers = _.get(Workload, 'spec.template.spec.containers[0]', {});
// }
containers = _.get(Workload, 'spec.containers[0]', {});
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
const colorObj = {
Deployed: '#4CAF51',
Staging: '#F44337',
UNKNOWN: '#1890ff',
};
return (
<PageContainer>
<Spin spinning={loadingAll}>
<div className="card-container app-detial">
<h2>{_.get(Workload, 'metadata.name')}</h2>
<p style={{ marginBottom: '20px' }}>
{Workload.apiVersion}, Kind={Workload.kind}
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<Row>
<Col span="11">
<div className="summaryBox1" onClick={(e) => this.gotoWorkloadDetail(e)}>
{/* <div className="summaryBox1"> */}
<Row>
<Col span="22">
<p className="title">{Workload.kind}</p>
<p>{Workload.apiVersion}</p>
</Col>
<Col span="2">
{/* <a href="JavaScript:;">?</a> */}
<p className="title hasCursor" onClick={this.hrefClick}>
?
</p>
</Col>
</Row>
<p className="title">
Name:<span>{_.get(Workload, 'metadata.name')}</span>
</p>
<p className="title">Settings:</p>
<Row>
{Object.keys(containers).map((currentKey) => {
if (currentKey === 'ports') {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>{_.get(containers[currentKey], '[0].containerPort', '')}</p>
</Col>
</Fragment>
);
// eslint-disable-next-line no-else-return
} else if (currentKey === 'name') {
return <Fragment key={currentKey} />;
}
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{containers[currentKey]}</p>
</Col>
</Fragment>
);
})}
</Row>
</div>
<div className="summaryBox2">
<p className="title">Status:</p>
<p>{status}</p>
{/* <Row>
<Col span="8">
<p>Available Replicas</p>
<p>Ready Replicas</p>
</Col>
<Col span="16">
<p>1</p>
<p>1</p>
</Col>
</Row> */}
</div>
<Popconfirm
title="Are you sure delete this app?"
onConfirm={(e) => this.deleteApp(e)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button danger>Delete</Button>
</Popconfirm>
</Col>
<Col span="1" />
<Col span="10">
{Traits.length ? (
Traits.map((item, index) => {
const traitItem = _.get(item, 'trait', {});
const annotations = _.get(traitItem, 'metadata.annotations', {});
let traitType = 1;
const spec = _.get(traitItem, 'spec', {});
if (traitItem.kind === 'Ingress') {
traitType = 2;
}
return (
<div
className="summaryBox"
onClick={(e) => this.gotoTraitDetail(e, traitItem)}
key={index.toString()}
>
<Row>
<Col span="22">
<p className="title">{traitItem.kind}</p>
<p>{traitItem.apiVersion}</p>
</Col>
<Col span="2">
<p
className="title hasCursor"
onClick={(e) => {
e.stopPropagation();
}}
>
?
</p>
</Col>
</Row>
<Row>
{Object.keys(annotations).map((currentKey3) => {
return (
<Fragment key={currentKey3}>
<Col span="8">
<p>{currentKey3}:</p>
</Col>
<Col span="8">
<p>{annotations[currentKey3]}</p>
</Col>
</Fragment>
);
})}
</Row>
<p className="title">Properties:</p>
<Row>
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>domain</p>
</Col>
<Col span="16">
<p>{_.get(spec, 'rules[0].host', '')}</p>
</Col>
<Col span="8">
<p>service</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.serviceName',
'',
)}
</p>
</Col>
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>ApplicationListDetail</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Spin spinning={loadingAll}>
<div className="card-container app-detial">
<h2>{_.get(Workload, 'metadata.name')}</h2>
<p style={{ marginBottom: '20px' }}>
{Workload.apiVersion}, Kind={Workload.kind}
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<Row>
<Col span="11">
<div
className="summaryBox1"
onClick={(e) => this.gotoWorkloadDetail(e)}
style={{ background: colorObj[status] || '#1890ff' }}
>
<Row>
<Col span="22">
<p className="title">{Workload.kind}</p>
<p>{Workload.apiVersion}</p>
</Col>
<Col span="2">
<p className="title hasCursor" onClick={this.hrefClick}>
?
</p>
</Col>
</Row>
<p className="title">
Name:<span>{_.get(Workload, 'metadata.name')}</span>
</p>
<p className="title">Settings:</p>
<Row>
{Object.keys(containers).map((currentKey) => {
if (currentKey === 'ports') {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.servicePort',
'',
)}
</p>
<p>{_.get(containers[currentKey], '[0].containerPort', '')}</p>
</Col>
</Fragment>
) : (
Object.keys(spec).map((currentKey) => {
);
// eslint-disable-next-line no-else-return
} else if (currentKey === 'name') {
return <Fragment key={currentKey} />;
// eslint-disable-next-line no-else-return
} else if (currentKey === 'env') {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>env</p>
</Col>
<Col span="16">
<p>{_.get(containers[currentKey], '[0].value', '')}</p>
</Col>
</Fragment>
);
}
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{containers[currentKey]}</p>
</Col>
</Fragment>
);
})}
</Row>
</div>
<Popconfirm
title="Are you sure delete this app?"
onConfirm={(e) => this.deleteApp(e)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button danger>Delete</Button>
</Popconfirm>
</Col>
<Col span="1" />
<Col span="10">
{Traits.length ? (
Traits.map((item, index) => {
const traitItem = _.get(item, 'trait', {});
const annotations = _.get(traitItem, 'metadata.annotations', {});
let traitType = 1;
const spec = _.get(traitItem, 'spec', {});
if (traitItem.kind === 'Ingress') {
traitType = 2;
}
return (
<div
className="summaryBox"
onClick={(e) => this.gotoTraitDetail(e, traitItem)}
key={index.toString()}
>
<Row>
<Col span="22">
<p className="title">{traitItem.kind}</p>
<p>{traitItem.apiVersion}</p>
</Col>
<Col span="2">
<p
className="title hasCursor"
onClick={(e) => {
e.stopPropagation();
}}
>
?
</p>
</Col>
</Row>
<Row>
{Object.keys(annotations).map((currentKey3) => {
return (
<Fragment key={currentKey}>
<Fragment key={currentKey3}>
<Col span="8">
<p>{currentKey}</p>
<p>{currentKey3}:</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
<Col span="8">
<p>{annotations[currentKey3]}</p>
</Col>
</Fragment>
);
})
)}
{/* {Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
})}
</Row>
<p className="title">Properties:</p>
<Row>
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>{currentKey}</p>
<p>domain</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
<p>{_.get(spec, 'rules[0].host', '')}</p>
</Col>
<Col span="8">
<p>service</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.serviceName',
'',
)}
</p>
</Col>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.servicePort',
'',
)}
</p>
</Col>
</Fragment>
);
})} */}
</Row>
<div style={{ clear: 'both', height: '32px' }}>
<Popconfirm
title="Are you sure delete this trait?"
onConfirm={(e) => this.deleteTrait(e, item)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button
danger
className="floatRight"
onClick={(e) => {
e.stopPropagation();
}}
) : (
Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
</Col>
</Fragment>
);
})
)}
</Row>
<div style={{ clear: 'both', height: '32px' }}>
<Popconfirm
title="Are you sure delete this trait?"
onConfirm={(e) => this.deleteTrait(e, item)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
Delete
</Button>
</Popconfirm>
<Button
danger
className="floatRight"
onClick={(e) => {
e.stopPropagation();
}}
>
Delete
</Button>
</Popconfirm>
</div>
</div>
</div>
);
})
) : (
<Fragment />
)}
<Tooltip placement="top" title="Attach Trait">
<p
className="hasCursor"
style={{
fontSize: '30px',
display: 'inline-flex',
}}
onClick={this.createTrait}
>
+
</p>
</Tooltip>
</Col>
</Row>
</TabPane>
<TabPane tab="Topology" key="2">
{/* <p>Topology</p> */}
<Topology />
</TabPane>
</Tabs>
</div>
<Modal
title="attach a trait"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Confirm
</Button>,
]}
>
<CreateTraitItem
onRef={(ref) => {
this.child = ref;
}}
availableTraitList={this.state.availableTraitList}
initialValues={{}}
/>
</Modal>
</Spin>
</PageContainer>
);
})
) : (
<Fragment />
)}
<Tooltip placement="top" title="Attach Trait">
<p
className="hasCursor"
style={{
fontSize: '30px',
display: 'inline-flex',
}}
onClick={this.createTrait}
>
+
</p>
</Tooltip>
</Col>
</Row>
</TabPane>
<TabPane tab="Topology" key="2">
<p>Topology</p>
</TabPane>
</Tabs>
</div>
<Modal
title="Attach a Trait"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Confirm
</Button>,
]}
>
<CreateTraitItem
onRef={(ref) => {
this.child = ref;
}}
availableTraitList={this.state.availableTraitList}
initialValues={{}}
/>
</Modal>
</Spin>
</PageContainer>
</div>
);
}
}

View File

@@ -19,7 +19,6 @@
font-weight: 500;
font-size: 16px;
line-height: 36px;
// color: #fff;
}
p {
margin: 0;

View File

@@ -1,7 +1,7 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import './index.less';
import { Button, Row, Col, Form, Input, Select, Steps, message } from 'antd';
import { Button, Row, Col, Form, Input, Select, Steps, message, Breadcrumb } from 'antd';
import { connect } from 'dva';
import { Link } from 'umi';
import _ from 'lodash';
@@ -73,12 +73,8 @@ class TableList extends React.Component {
this.setState({
traitList: traits,
});
// 如果直接跳转到第二步,需要设置值
const traitType = _.get(this.props, 'location.state.TraitType', '');
if (traitType) {
// let availableTraitList = traits.filter((item)=>{
// return item.name === traitType
// })
this.setState({
availableTraitList: traits,
traitNum: [
@@ -117,7 +113,12 @@ class TableList extends React.Component {
});
};
onFinishStep2 = () => {
onFinishStep2 = async () => {
const asyncValidateArray = [];
this.state.traitNum.forEach((item) => {
asyncValidateArray.push(item.refname.validateFields());
});
await Promise.all(asyncValidateArray);
const newTraitNum = this.state.traitNum.map((item) => {
// eslint-disable-next-line no-param-reassign
item.initialData = item.refname.getSelectValue();
@@ -163,7 +164,11 @@ class TableList extends React.Component {
};
createApp = async () => {
const { step1SubmitObj, traitNum } = this.state;
const { traitNum } = this.state;
const { step1SubmitObj } = this.state;
if (step1SubmitObj.env_name !== this.props.currentEnv) {
step1SubmitObj.env_name = this.props.currentEnv;
}
const submitObj = _.cloneDeep(step1SubmitObj);
const { workload_name: workloadName } = step1SubmitObj;
submitObj.flags.push({
@@ -289,9 +294,6 @@ class TableList extends React.Component {
this.state.traitNum = this.state.traitNum.filter((item) => {
return item.uniq !== uniq;
});
// this.setState(()=>({
// traitNum: this.state.traitNum
// }));
this.setState((prev) => ({
traitNum: prev.traitNum,
}));
@@ -320,6 +322,11 @@ class TableList extends React.Component {
name="workload_name"
label="Name"
rules={[
{
pattern: /^[a-z0-9-_]+$/,
message:
'Names can only use digits(0-9),lowercase letters(a-z),and dashes(-),Underline.',
},
{
required: true,
message: 'Please input name!',
@@ -356,8 +363,11 @@ class TableList extends React.Component {
)}
</Select>
</Form.Item>
<Form.Item label="Settings" />
</div>
<Form.Item
label="Settings"
style={{ background: 'rgba(0, 0, 0, 0.04)', paddingLeft: '16px' }}
/>
<div className="relativeBox">
<p className="hasMore">?</p>
{Array.isArray(workloadSettings) && workloadSettings.length ? (
@@ -365,7 +375,7 @@ class TableList extends React.Component {
if (item.name === 'name') {
return <Fragment key={item.name} />;
}
return (
return item.type === 4 ? (
<Form.Item
name={item.name}
label={item.name}
@@ -375,6 +385,22 @@ class TableList extends React.Component {
required: item.required,
message: `Please input ${item.name}!`,
},
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
]}
>
<Input />
</Form.Item>
) : (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required,
message: `Please input ${item.name}!`,
},
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
]}
>
<Input />
@@ -548,11 +574,6 @@ class TableList extends React.Component {
</Row>
</div>
<div className="buttonBox">
{/* <Link to="/ApplicationList">
<Button type="primary" className="floatRight">
Confirm
</Button>
</Link> */}
<Button
type="primary"
className="floatRight"
@@ -570,16 +591,29 @@ class TableList extends React.Component {
);
}
return (
<PageContainer>
<div className="create-container create-app">
<Steps current={current}>
<Step title="Step 1" description="Choose Workload" />
<Step title="Step 2" description="Attach Trait" />
<Step title="Step 3" description="Review and confirm" />
</Steps>
{currentDetail}
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>CreateApplication</Breadcrumb.Item>
</Breadcrumb>
</div>
</PageContainer>
<PageContainer>
<div className="create-container create-app">
<Steps current={current}>
<Step title="Step 1" description="Choose Workload" />
<Step title="Step 2" description="Attach Trait" />
<Step title="Step 3" description="Review and confirm" />
</Steps>
{currentDetail}
</div>
</PageContainer>
</div>
);
}
}

View File

@@ -27,7 +27,6 @@
}
.relativeBox {
position: relative;
// border: 1px solid #eee;
padding: 0 48px 0 16px;
.hasMore {
position: absolute;
@@ -53,7 +52,7 @@
}
.summaryBox1 {
color: #fff;
background: #0097a7;
background: rgb(24, 144, 255);
}
.summaryBox2 {
background: yellow;

View File

@@ -35,29 +35,35 @@ export default class CreateTraitItem extends React.PureComponent {
return this.formRefStep2.current.getFieldsValue();
};
validateFields = () => {
return this.formRefStep2.current.validateFields();
};
traitSelectChange = async (value, isType = 1) => {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
payload: {
traitName: value,
},
});
this.setState({
parameters: res.parameters,
});
if (isType === 2) {
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
} else {
// 进行默认值填写
const parameters = _.get(res, 'parameters', []);
if (parameters.length) {
const initialObj = {};
parameters.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
this.formRefStep2.current.setFieldsValue(initialObj);
if (value) {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
payload: {
traitName: value,
},
});
this.setState({
parameters: res.parameters,
});
if (isType === 2) {
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
} else {
// 进行默认值填写
const parameters = _.get(res, 'parameters', []);
if (parameters.length) {
const initialObj = {};
parameters.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
this.formRefStep2.current.setFieldsValue(initialObj);
}
}
}
};
@@ -78,8 +84,12 @@ export default class CreateTraitItem extends React.PureComponent {
>
<div style={{ border: '1px solid #eee', margin: '16px 0px 8px' }}>
<div style={{ padding: '16px 48px 0px 16px' }}>
<Form.Item name="name" label="Trait">
<Select placeholder="Select a Trait" allowClear onChange={this.traitSelectChange}>
<Form.Item
name="name"
label="Trait"
rules={[{ required: true, message: 'Please Select a Trait!' }]}
>
<Select placeholder="Select a Trait" onChange={this.traitSelectChange}>
{availableTraitList.map((item) => {
return (
<Option value={item.name} key={item.name}>
@@ -92,11 +102,36 @@ export default class CreateTraitItem extends React.PureComponent {
<Form.Item label="Properties" />
</div>
<div className="relativeBox">
{/* <p className="hasMore">?</p> */}
{this.state.parameters ? (
this.state.parameters.map((item) => {
return (
<Form.Item name={item.name} label={item.name} key={item.name}>
return item.type === 4 ? (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
]}
>
<Input />
</Form.Item>
) : (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
]}
>
<Input />
</Form.Item>
);

View File

@@ -1,19 +1,14 @@
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { SearchOutlined, BranchesOutlined, ApartmentOutlined } from '@ant-design/icons';
import { Button, Card, Row, Col, Form, Select, DatePicker, Spin, Empty } from 'antd';
import { BranchesOutlined, ApartmentOutlined } from '@ant-design/icons';
import { Button, Card, Row, Col, Form, Spin, Empty, Breadcrumb } from 'antd';
import { connect } from 'dva';
import moment from 'moment';
import './index.less';
import { Link } from 'umi';
const { Option } = Select;
@connect(({ loading, applist, globalData }) => ({
loadingAll: loading.models.applist,
// 当applist这个models有数据请求行为的时候loading为true没有请求的时候为false
// loadingList: loading.effects['applist/getList'],
// 当applist的effects中的getList有异步请求行为时为true没有请求行为时为false
returnObj: applist.returnObj,
currentEnv: globalData.currentEnv,
}))
@@ -27,7 +22,7 @@ class TableList extends React.Component {
const { currentEnv } = this.props;
if (currentEnv) {
this.props.dispatch({
type: 'applist/getList', // applist对应models层的命名空间namespace
type: 'applist/getList',
payload: {
url: `/api/envs/${currentEnv}/apps/`,
},
@@ -40,29 +35,24 @@ class TableList extends React.Component {
return true;
}
this.props.dispatch({
type: 'applist/getList', // applist对应models层的命名空间namespace
type: 'applist/getList',
payload: {
url: `/api/envs/${nextProps.currentEnv}/apps/`,
},
});
return true;
// return true;
}
onFinish = () => {
// const data = moment(values.createTime).format('YYYY-MM-DD')
};
onFinish = () => {};
handleChange = () => {};
handleAdd = () => {};
onSelect = () => {
// console.log("selected", selectedKeys, info);
};
onSelect = () => {};
getHeight = (num) => {
return `${num * 55}px`;
return `${num * 43}px`;
};
getFormatDate = (time) => {
@@ -80,98 +70,91 @@ class TableList extends React.Component {
UNKNOWN: 'first3',
};
return (
<PageContainer>
<Spin spinning={loadingAll}>
<div className="applist">
<Form name="horizontal_login" layout="inline" onFinish={this.onFinish}>
<Form.Item name="createTime">
<DatePicker placeholder="createTime" />
</Form.Item>
<Form.Item name="status">
<Select
placeholder="status"
style={{ width: 120 }}
onChange={this.handleChange}
allowClear
>
<Option value="True">True</Option>
<Option value="False">False</Option>
<Option value="UNKNOWN">UNKNOWN</Option>
</Select>
</Form.Item>
<Form.Item>
<Button icon={<SearchOutlined />} htmlType="submit">
Search
</Button>
</Form.Item>
<Form.Item>
<Link to="/ApplicationList/CreateApplication">
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
create
</Button>
</Link>
</Form.Item>
</Form>
</div>
<Row gutter={16}>
{Array.isArray(returnObj) && returnObj.length ? (
returnObj.map((item, index) => {
const { traits = [] } = item;
return (
<Col span={6} onClick={this.gotoDetail} key={index.toString()}>
<Link
to={{
pathname: '/ApplicationList/ApplicationListDetail',
state: { appName: item.name, envName: currentEnv },
}}
>
<Card
title={item.name}
bordered={false}
extra={this.getFormatDate(item.created)}
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Applications</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Spin spinning={loadingAll}>
<div className="applist">
<Form name="horizontal_login" layout="inline" onFinish={this.onFinish}>
<Form.Item>
<Link to="/ApplicationList/CreateApplication">
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
create
</Button>
</Link>
</Form.Item>
</Form>
</div>
<Row gutter={16}>
{Array.isArray(returnObj) && returnObj.length ? (
returnObj.map((item, index) => {
const { traits = [] } = item;
return (
<Col span={6} onClick={this.gotoDetail} key={index.toString()}>
<Link
to={{
pathname: '/ApplicationList/ApplicationListDetail',
state: { appName: item.name, envName: currentEnv },
}}
>
<div className="cardContent">
<div className="box2" style={{ height: this.getHeight(traits.length) }} />
<div className="box1">
{traits.length ? (
<div className="box3" style={{ width: '40px' }} />
) : (
''
)}
<Card
title={item.name}
bordered={false}
extra={this.getFormatDate(item.created)}
>
<div className="cardContent">
<div
className={['hasPadding', colorObj[item.status] || 'first3'].join(
' ',
className="box2"
style={{ height: this.getHeight(traits.length) }}
/>
<div className="box1">
{traits.length ? (
<div className="box3" style={{ width: '30px' }} />
) : (
''
)}
>
<ApartmentOutlined style={{ marginRight: '10px' }} />
{item.workload}
</div>
</div>
{traits.map((item1, index1) => {
return (
<div className="box1" key={index1.toString()}>
<div className="box3" style={{ width: '80px' }} />
<div className="other hasPadding">
<BranchesOutlined style={{ marginRight: '10px' }} />
{item1}
</div>
<div
className={['hasPadding', colorObj[item.status] || 'first3'].join(
' ',
)}
>
<ApartmentOutlined style={{ marginRight: '4px' }} />
{item.workload}
</div>
);
})}
</div>
</Card>
</Link>
</Col>
);
})
) : (
<div style={{ width: '100%', height: '80%' }}>
<Empty />
</div>
)}
</Row>
</Spin>
</PageContainer>
</div>
{traits.map((item1, index1) => {
return (
<div className="box1" key={index1.toString()}>
<div className="box3" style={{ width: '50px' }} />
<div className="other hasPadding">
<BranchesOutlined style={{ marginRight: '4px' }} />
{item1}
</div>
</div>
);
})}
</div>
</Card>
</Link>
</Col>
);
})
) : (
<div style={{ width: '100%', height: '80%' }}>
<Empty />
</div>
)}
</Row>
</Spin>
</PageContainer>
</div>
);
}
}

View File

@@ -15,38 +15,38 @@
}
.box2 {
position: absolute;
top: 21px;
top: 15px;
width: 0;
padding: 0;
border-left: 1px solid black;
}
.box3 {
position: absolute;
top: 21px;
top: 15px;
height: 0;
border-bottom: 1px solid black;
}
.hasPadding {
padding: 10px;
padding: 4px;
color: #fff;
}
div {
margin-bottom: 10px;
}
.first1 {
margin-left: 40px;
background: #1890ff;
margin-left: 30px;
background: #4caf51;
}
.first2 {
margin-left: 40px;
background: red;
margin-left: 30px;
background: #f44337;
}
.first3 {
margin-left: 40px;
background: #92c47c;
margin-left: 30px;
background: #1890ff;
}
.other {
margin-left: 80px;
background: #ee9611;
margin-left: 50px;
background: #ffc105;
}
}

View File

@@ -1,14 +1,11 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Space, Button, Row, Col, message, Spin } from 'antd';
// import { Space, Modal, Button, Row, Col, message, Spin } from 'antd';
// import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Space, Button, Row, Col, message, Spin, Breadcrumb } from 'antd';
import { Link } from 'umi';
import './index.less';
import { connect } from 'dva';
import _ from 'lodash';
// const { confirm } = Modal;
@connect(({ loading, globalData }) => ({
loadingAll: loading.models.capability,
currentEnv: globalData.currentEnv,
@@ -53,8 +50,7 @@ class TableList extends React.PureComponent {
};
gotoOtherPage = () => {
// window.location.href = 'https://github.com/oam-dev/catalog/blob/master/workloads/cloneset/README.md';
window.open('https://github.com/oam-dev/catalog/blob/master/workloads/cloneset/README.md');
// window.open('https://github.com/oam-dev/catalog/blob/master/workloads/cloneset/README.md');
};
installSignle = async (e, name) => {
@@ -69,25 +65,21 @@ class TableList extends React.PureComponent {
});
if (res) {
message.success(res);
// this.getInitialData();
window.location.reload();
}
};
uninstallSignle = async (e, name) => {
e.stopPropagation();
// const capabilityCenterName = _.get(this.props, 'location.state.name', '');
if (name) {
const res = await this.props.dispatch({
type: 'capability/deleteOneCapability',
payload: {
// capabilityCenterName,
capabilityName: name,
},
});
if (res) {
message.success(res);
// this.getInitialData();
window.location.reload();
}
}
@@ -104,7 +96,6 @@ class TableList extends React.PureComponent {
});
if (res) {
message.success(res);
// this.getInitialData();
window.location.reload();
}
}
@@ -112,48 +103,6 @@ class TableList extends React.PureComponent {
showDeleteConfirm = () => {
message.info('正在开发中...');
// // eslint-disable-next-line
// const _this = this;
// const capabilityCenterName = _.get(this.props, 'location.state.name', '');
// if (capabilityCenterName) {
// confirm({
// title: `Are you sure delete ${capabilityCenterName}?`,
// icon: <ExclamationCircleOutlined />,
// width: 500,
// content: (
// <div>
// <p style={{ margin: '0px' }}>您本次移除 {capabilityCenterName},将会删除的应用列表:</p>
// <Space>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// </Space>
// <p style={{ margin: '0px' }}>
// 确认后,移除 {capabilityCenterName},并且删除相应的应用?
// </p>
// </div>
// ),
// okText: 'Yes',
// okType: 'danger',
// cancelText: 'No',
// async onOk() {
// const res = await _this.props.dispatch({
// type: 'capability/deleteCapability',
// payload: {
// capabilityName: capabilityCenterName,
// },
// });
// if (res) {
// message.success(res);
// _this.props.history.push({ pathname: '/Capability' });
// }
// },
// onCancel() {
// // console.log('Cancel');
// },
// });
// }
};
render() {
@@ -161,82 +110,101 @@ class TableList extends React.PureComponent {
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
return (
<PageContainer>
<Spin spinning={loadingAll}>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.syncAllSignle}>
Install all
</Button>
<Button type="default" onClick={this.showDeleteConfirm}>
Remove
</Button>
</Space>
</div>
<div>
<h3>Workloads</h3>
<Row>
{workloadList.length ? (
workloadList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<img
src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1109866916,1852667152&fm=26&gp=0.jpg"
alt="workload"
/>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
uninstall
</Button>
) : (
<Button onClick={(e) => this.installSignle(e, item.name)}>install</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment>
<div>暂无可用的workload</div>
</Fragment>
)}
</Row>
</div>
<div>
<h3>Traits</h3>
<Row>
{traitList.length ? (
traitList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<img
src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1109866916,1852667152&fm=26&gp=0.jpg"
alt="workload"
/>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
uninstall
</Button>
) : (
<Button onClick={(e) => this.installSignle(e, item.name)}>install</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment>
<div>暂无可用的trait</div>
</Fragment>
)}
</Row>
</div>
</Spin>
</PageContainer>
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/Capability">Capability</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Detail</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Spin spinning={loadingAll}>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.syncAllSignle}>
Install all
</Button>
<Button type="default" onClick={this.showDeleteConfirm}>
Remove
</Button>
</Space>
</div>
<div>
<h3>Workloads</h3>
<Row>
{workloadList.length ? (
workloadList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<div className="title">{item.name.substr(0, 3).toUpperCase()}</div>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
uninstall
</Button>
) : (
<Button
onClick={(e) => this.installSignle(e, item.name)}
type="primary"
ghost
>
install
</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment>
<div>暂无可用的workload</div>
</Fragment>
)}
</Row>
</div>
<div>
<h3>Traits</h3>
<Row>
{traitList.length ? (
traitList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<div className="title">{item.name.substr(0, 3).toUpperCase()}</div>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
uninstall
</Button>
) : (
<Button
onClick={(e) => this.installSignle(e, item.name)}
type="primary"
ghost
>
install
</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment>
<div>暂无可用的trait</div>
</Fragment>
)}
</Row>
</div>
</Spin>
</PageContainer>
</div>
);
}
}

View File

@@ -3,7 +3,7 @@
flex-direction: column;
align-items: center;
justify-content: center;
margin: 10px;
margin: 10px 16px 10px 0;
padding: 10px 0;
background-color: #fff;
cursor: pointer;
@@ -11,6 +11,12 @@
width: 60%;
margin: 10px auto;
}
.title {
width: 60%;
margin: 10px auto;
font-size: 30px;
text-align: center;
}
p {
margin: 0;
margin-bottom: 10px;

View File

@@ -1,13 +1,12 @@
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Table, Space, Modal, Form, Input, message, Spin } from 'antd';
// import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Button, Table, Space, Modal, Form, Input, message, Spin, Breadcrumb } from 'antd';
import { CopyOutlined } from '@ant-design/icons';
import { Link } from 'umi';
import './index.less';
import { connect } from 'dva';
import _ from 'lodash';
// const { confirm } = Modal;
const { Column } = Table;
const layout = {
@@ -85,13 +84,6 @@ class TableList extends React.PureComponent {
}
};
// handleTest = async () => {
// await this.formRef.current.validateFields();
// this.setState({
// visible: false,
// });
// };
handleCancel = () => {
this.setState({
visible: false,
@@ -113,7 +105,6 @@ class TableList extends React.PureComponent {
});
if (res) {
message.success(res);
// this.getInitialData();
}
const newList1 = _.cloneDeep(this.state.capabilityList);
newList1[index].btnSyncLoading = false;
@@ -124,47 +115,19 @@ class TableList extends React.PureComponent {
}
};
copyURL = (text) => {
const oInput = document.createElement('input');
oInput.value = text;
document.body.appendChild(oInput);
oInput.select();
document.execCommand('Copy');
oInput.className = 'oInput';
oInput.style.display = 'none';
message.success('copy success');
};
showDeleteConfirm = () => {
message.info('正在开发中...');
// if (record) {
// // eslint-disable-next-line
// const _this = this;
// confirm({
// title: `Are you sure delete ${record}?`,
// icon: <ExclamationCircleOutlined />,
// width: 500,
// content: (
// <div>
// <p>您本次移除 {record},将会删除的应用列表:</p>
// <Space>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// </Space>
// <p>确认后,移除{record},并且删除相应的应用?</p>
// </div>
// ),
// okText: 'Yes',
// okType: 'danger',
// cancelText: 'No',
// async onOk() {
// const res = await _this.props.dispatch({
// type: 'capability/deleteCapability',
// payload: {
// capabilityName: record,
// },
// });
// if (res) {
// message.success(res);
// _this.getInitialData();
// }
// },
// onCancel() {
// // console.log('Cancel');
// },
// });
// }
};
render() {
@@ -173,106 +136,123 @@ class TableList extends React.PureComponent {
loadingList = loadingList || false;
capabilityList = Array.isArray(capabilityList) ? capabilityList : [];
return (
<PageContainer>
<Spin spinning={loadingList}>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.showModal}>
Create
</Button>
{/* <Button type="default">Sync All</Button> */}
</Space>
</div>
<Modal
title="Create Capability Center"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
// <Button key="test" onClick={this.handleTest}>
// Test
// </Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Create
</Button>,
]}
>
<Form {...layout} ref={this.formRef} name="control-ref" labelAlign="left">
<Form.Item
name="Name"
label="Name"
rules={[
{
required: true,
message: 'Please input name!',
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="Address"
label="URL"
rules={[
// { pattern: '/^((https|http|ftp|rtsp|mms){0,1}(:\/\/){0,1})\.(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+$/',
// message: 'please input correct URL'
// },
{
required: true,
message: 'Please input URL!',
},
]}
>
<Input />
</Form.Item>
</Form>
</Modal>
<Table dataSource={capabilityList} pagination={false} rowKey={(record) => record.name}>
<Column
title="Name"
dataIndex="name"
key="name"
render={(text, record) => {
return (
<Link to={{ pathname: '/Capability/Detail', state: { name: record.name } }}>
{text}
</Link>
);
}}
/>
<Column
title="URL"
dataIndex="url"
key="url"
render={(text) => {
return (
<a href={text} target="_blank" rel="noreferrer">
{text}
</a>
);
}}
/>
<Column
title="Operations"
dataIndex="name"
key="name"
render={(text, record, index) => {
return (
<Space>
<Button
loading={record.btnSyncLoading}
onClick={() => this.syncSignle(text, index)}
>
sync
</Button>
<Button onClick={() => this.showDeleteConfirm(text)}>remove</Button>
</Space>
);
}}
/>
</Table>
</Spin>
</PageContainer>
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Capability</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Spin spinning={loadingList}>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.showModal}>
Create
</Button>
</Space>
</div>
<Modal
title="Create Capability Center"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="submit" type="primary" onClick={this.handleOk}>
Create
</Button>,
]}
>
<Form {...layout} ref={this.formRef} name="control-ref" labelAlign="left">
<Form.Item
name="Name"
label="Name"
rules={[
{
required: true,
message: 'Please input name!',
},
{
pattern: new RegExp('^[0-9a-zA-Z_]{1,60}$', 'g'),
message:
'The maximum length is 60,should be combination of numbers,alphabets,underline!',
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="Address"
label="URL"
rules={[
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
{
required: true,
message: 'Please input URL!',
},
]}
>
<Input />
</Form.Item>
</Form>
</Modal>
<Table dataSource={capabilityList} pagination={false} rowKey={(record) => record.name}>
<Column
title="Name"
dataIndex="name"
key="name"
render={(text, record) => {
return (
<Link to={{ pathname: '/Capability/Detail', state: { name: record.name } }}>
{text}
</Link>
);
}}
/>
<Column
title="URL"
dataIndex="url"
key="url"
width="60%"
render={(text) => {
return (
<div className="hoverItem">
<a href={text} target="_blank" rel="noreferrer">
{text}
</a>
<div className="copyIcon" onClick={() => this.copyURL(text)}>
<CopyOutlined />
</div>
</div>
);
}}
/>
<Column
title="Operations"
dataIndex="name"
key="name"
render={(text, record, index) => {
return (
<Space>
<Button
loading={record.btnSyncLoading}
onClick={() => this.syncSignle(text, index)}
type="primary"
ghost
>
sync
</Button>
<Button onClick={() => this.showDeleteConfirm(text)}>remove</Button>
</Space>
);
}}
/>
</Table>
</Spin>
</PageContainer>
</div>
);
}
}

View File

@@ -1,3 +1,14 @@
p {
margin: 0;
}
.hoverItem {
.copyIcon {
display: none;
margin-left: 10px;
font-size: 16px;
cursor: pointer;
}
}
.hoverItem:hover .copyIcon {
display: inline-block;
}

View File

@@ -178,7 +178,8 @@ const TableList = (props) => {
},
{
pattern: new RegExp('^[0-9a-zA-Z_]{1,32}$', 'g'),
message: 'The maximum length is 63,should be combination of numbers,alphabets,underline!',
message:
'The maximum length is 63,should be combination of numbers,alphabets,underline!',
},
]}
>

View File

@@ -1,9 +1,11 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import './index.less';
import { Form, Input, Button, Row, Col, Tabs, Table } from 'antd';
import { Form, Input, Button, Row, Col, Tabs, Table, Breadcrumb } from 'antd';
import { CheckCircleOutlined } from '@ant-design/icons';
import _ from 'lodash';
import { connect } from 'dva';
import { Link } from 'umi';
const { TabPane } = Tabs;
const layout = {
@@ -27,7 +29,12 @@ const columns = [
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text) => <a>{text}</a>,
render: (text) => (
<div>
<CheckCircleOutlined style={{ fontSize: '20px', color: '#4CAF51' }} />
<a style={{ marginLeft: '6px' }}>{text}</a>
</div>
),
},
{
title: 'Ready',
@@ -120,13 +127,6 @@ const data1 = [
LastTransition: '2d',
},
];
// const demoText = `H4sIAAAAAAAA/6xUwW7bOhD8lYc9U4lsWfazgB4C5Na0DZK0lyKHNbmyWVMkQ66UGob/vSBdt3GCxEXRmwjujmZnZrmFtbYKGrgkb9ymI8sgAL3+QiFqZ6EB9D6eDyMQ0BGjQkZotmCxI2hAOmcKDBtcrJCxGMrWfn+Ygsj30aNMRYpa7E0CloGQtbN3uqPI2HlobG+MAIMLMjEBo/cvcEGAW3wjyZH4LGh3JpHZ0Jl25yuMK2hgPkPV1lRJbKfjWVlNpqps69mkrNu2qv5X5UzV9ULVIOC4P1IYtHxzlOGXFEMJOwForeM8Rib8GjOdZD3Avz6Ae7QUiuWwhuYZtWEk/nuvrXp3+4cgpzw53f3SsePKjrLaHHpKSuTGG2opkJUUofm6Pc7O84FAHPL2e6ZTrPssZDWq5+O6GhVqPquLybyaF9gqKsbVTMpyXFZ1m8yVznJwxlCAJrEUsDBOrj8lopdkiDOvFk2k3f1OQPQkk4mRDEl2IX13yHJ1dSqQx6nYCWDqvEGmDPFkU/4+8/86qUbbNQWVw2lTFKABsrgwpN52+olQSWDUlsLe7VPm6Q6XlPdABspPS1imTihWcC8gUHR9yNHZQqCHniLnb+l7aKAuu/zsdC5soIHp5IPOZDLqdW/MtTNapqsL84ibCLt7cVi5Cyldb/njCYLYs+tS4e1R251bkz1EaK/Rz4IrbdfxEKEkDAdkWm4Sa9749LMbZ4y2y89epTgICEfnZrvb9yH3MZ9+BAAA//+bxjCThQUAAA
// objectset.rio.cattle.io/id service
// objectset.rio.cattle.io/owner-gvk rio.cattle.io/v1, Kind=Service
// objectset.rio.cattle.io/owner-name cool-aryabhata-v0fnxq6
// objectset.rio.cattle.io/owner-namespace default
// rio.cattle.io/mesh true
// Controlled By cool-aryabhata-v0fnxq6`;
@connect(({ loading, globalData }) => ({
loadingAll: loading.models.applist,
@@ -204,16 +204,6 @@ class TableList extends React.Component {
render() {
const { hasShowEdit, hasShowEdit2 } = this.state;
// let finallyText;
// if (demoText && demoText.length > 50) {
// finallyText = (
// <Tooltip placement="topRight" title={demoText}>
// <Button>{`${demoText.substring(0, 50)}......`}</Button>
// </Tooltip>
// );
// } else {
// finallyText = <p>{demoText}</p>;
// }
const status = _.get(this.state.appDetailData, 'Status', '');
const { traitItem, appName } = this.state;
const metadata = _.get(traitItem, 'metadata', '');
@@ -224,341 +214,269 @@ class TableList extends React.Component {
if (traitItem.kind === 'Ingress') {
traitType = 2;
}
const envName = _.get(this.props, 'location.state.envName', '');
return (
<PageContainer>
<div className="card-container trait-detail">
<h2>{traitName}</h2>
<p style={{ marginBottom: '20px' }}>
<i>
{traitItem.apiVersion}1,Name={appName}
</i>
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<div>
<Row>
<Col span="12">
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Configuration</p>
<Row>
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>domain</p>
</Col>
<Col span="16">
<p>{_.get(spec, 'rules[0].host', '')}</p>
</Col>
<Col span="8">
<p>service</p>
</Col>
<Col span="16">
<p>
{_.get(spec, 'rules[0].http.paths[0].backend.serviceName', '')}
</p>
</Col>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(spec, 'rules[0].http.paths[0].backend.servicePort', '')}
</p>
</Col>
</Fragment>
) : (
Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
</Col>
</Fragment>
);
})
)}
{/* {Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link
to={{
pathname: '/ApplicationList/ApplicationListDetail',
state: { appName, envName },
}}
>
ApplicationListDetail
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>TraitDetail</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<div className="card-container trait-detail">
<h2>{traitName}</h2>
<p style={{ marginBottom: '20px' }}>
<i>
{traitItem.apiVersion}1,Name={appName}
</i>
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<div>
<Row>
<Col span="12">
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Configuration</p>
<Row>
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>{currentKey}</p>
<p>domain</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
<p>{_.get(spec, 'rules[0].host', '')}</p>
</Col>
<Col span="8">
<p>service</p>
</Col>
<Col span="16">
<p>
{_.get(spec, 'rules[0].http.paths[0].backend.serviceName', '')}
</p>
</Col>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(spec, 'rules[0].http.paths[0].backend.servicePort', '')}
</p>
</Col>
</Fragment>
);
})} */}
</Row>
{/* <Row>
<Col span="10">
<div style={{ color: 'black' }}>
<p>Deployment Strategy</p>
<p>Rolling Update Strategy</p>
<p>Selectors</p>
<p>Min Ready Seconds</p>
<p>Revision History Limit</p>
<p>Replicas</p>
</div>
</Col>
<Col>
<p>RollingUpdate</p>
<p>Max Surge 25%, Max Unavailable 25%</p>
<p>
<Tag color="orange">aryabhataapp:cool</Tag>
<Tag color="orange">version:v0</Tag>
</p>
<p>0</p>
<p>10</p>
<p>1</p>
</Col>
</Row> */}
</div>
<div
className="hasPadding"
style={{ display: hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
labelAlign="left"
{...layout}
ef={this.formRefStep1}
name="control-ref"
onFinish={this.onFinishStep1}
>
<div className="relativeBox">
<Form.Item name="Replicas" label="Replicas">
<Input type="number" />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit}>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
) : (
Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
</Col>
</Fragment>
);
})
)}
</Row>
</div>
<div
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
/>
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit}
className="hasPadding"
style={{ display: hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
labelAlign="left"
{...layout}
ef={this.formRefStep1}
name="control-ref"
onFinish={this.onFinishStep1}
>
Edit
<div className="relativeBox">
<Form.Item name="Replicas" label="Replicas">
<Input type="number" />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit}>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
<div
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
/>
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit}
>
Edit
</Button>
</div>
</div>
</div>
</Col>
<Col span="1" />
<Col span="10">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Status</p>
<p>{status}</p>
</div>
</div>
</Col>
</Row>
<p className="title hasBG">Pods</p>
<Table columns={columns} dataSource={data} pagination={false} />
<p className="title hasBG">Conditions</p>
<Table columns={columns1} dataSource={data1} pagination={false} />
<p className="title hasBG">Pod Template</p>
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Container cool-aryabhata-v0fnxq6</p>
<Row>
<Col span="2">
<div style={{ color: 'black' }}>
<p>Image</p>
<p>Args</p>
</div>
</Col>
<Col>
<p>secret</p>
<p>[&apos;-h&apos;]</p>
</Col>
</Row>
</div>
<div
className="hasPadding"
style={{ display: hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
style={{ width: '50%' }}
{...layout1}
labelAlign="left"
ef={this.formRefStep2}
name="control-ref"
onFinish={this.onFinishStep2}
>
<div className="relativeBox">
<Form.Item name="Image" label="Image">
<Input />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
Cancle
</Button>
</div>
</div>
</Form>
</div>
</Col>
<Col span="1" />
<Col span="10">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Status</p>
<p>{status}</p>
{/* <Row>
<Col span="10">
<div style={{ color: 'black' }}>
<p>Avaliable Replicas</p>
<p>Ready Replicas</p>
<p>Total Replicas</p>
<p>Unavaliable Replicas</p>
<p>Updated Replicas</p>
</div>
</Col>
<Col>
<p>0</p>
<p>0</p>
<p>1</p>
<p>1</p>
<p>1</p>
</Col>
</Row> */}
</div>
</div>
</Col>
</Row>
<p className="title" style={{ marginTop: '16px' }}>
Pods
</p>
<Table columns={columns} dataSource={data} />
<p className="title">Conditions</p>
<Table columns={columns1} dataSource={data1} />
<p className="title">Pod Template</p>
<div className="hasBorder">
<div className="hasPadding" style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
<p className="title">Container cool-aryabhata-v0fnxq6</p>
<Row>
<Col span="2">
<div style={{ color: 'black' }}>
<p>Image</p>
<p>Args</p>
</div>
</Col>
<Col>
<p>secret</p>
<p>[&apos;-h&apos;]</p>
</Col>
</Row>
</div>
<div className="hasPadding" style={{ display: hasShowEdit2 ? 'block' : 'none' }}>
<p className="title">Deployment Editor</p>
<Form
style={{ width: '50%' }}
{...layout1}
labelAlign="left"
ef={this.formRefStep2}
name="control-ref"
onFinish={this.onFinishStep2}
>
<div className="relativeBox">
<Form.Item name="Image" label="Image">
<Input />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
Cancle
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
<div style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }} />
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit2}
>
Edit
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
<div style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }} />
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit2}
>
Edit
</Button>
</div>
</div>
</div>
</div>
</TabPane>
<TabPane tab="Metadata" key="2">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Metadata</p>
{Object.keys(metadata).map((currentKey8) => {
if (currentKey8 === 'annotations') {
</TabPane>
<TabPane tab="Metadata" key="2">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Metadata</p>
{Object.keys(metadata).map((currentKey8) => {
if (currentKey8 === 'annotations') {
return (
<Row key={currentKey8}>
<Col span="4">
<div style={{ color: 'black' }}>
<p>{currentKey8}</p>
</div>
</Col>
<Col span="20">
{Object.keys(metadata[currentKey8]).map((currentKey9) => {
return (
<Row key={currentKey9}>
<Col span="8">
<div style={{ color: 'black' }}>
<p>{currentKey9}</p>
</div>
</Col>
<Col>
<p>{metadata[currentKey8][currentKey9]}</p>
</Col>
</Row>
);
})}
</Col>
</Row>
);
}
return (
<Row key={currentKey8}>
<Col span="4">
<div style={{ color: 'black' }}>
<p>{currentKey8}</p>
</div>
<p>{currentKey8}</p>
</Col>
<Col span="20">
{Object.keys(metadata[currentKey8]).map((currentKey9) => {
return (
<Row key={currentKey9}>
<Col span="8">
<div style={{ color: 'black' }}>
<p>{currentKey9}</p>
</div>
</Col>
<Col>
<p>{metadata[currentKey8][currentKey9]}</p>
</Col>
</Row>
);
})}
<Col>
<p>{metadata[currentKey8]}</p>
</Col>
</Row>
);
}
return (
<Row key={currentKey8}>
<Col span="4">
<p>{currentKey8}</p>
</Col>
<Col>
<p>{metadata[currentKey8]}</p>
</Col>
</Row>
);
})}
{/* <Row>
<Col span="4">
<div style={{ color: 'black' }}>
<p>Annotations</p>
</div>
</Col>
<Col span="20">
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>deployment.kubernetes.io/revision</p>
</div>
</Col>
<Col>
<p>1</p>
</Col>
</Row>
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>objectset.rio.cattle.io/applied</p>
</div>
</Col>
<Col span="16">{finallyText}</Col>
</Row>
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>objectset.rio.cattle.io/id</p>
</div>
</Col>
<Col span="16">
<p>service</p>
</Col>
</Row>
</Col>
</Row>
<Row>
<Col span="4">
<div style={{ color: 'black' }}>
<p>Controlled By</p>
</div>
</Col>
<Col>
<p>cool-aryabhata-v0fnxq6</p>
</Col>
</Row> */}
})}
</div>
</div>
</div>
</TabPane>
<TabPane tab="Resource Viewer" key="3">
<p>Resource Viewer</p>
</TabPane>
<TabPane tab="YAML" key="4">
<p>YAML</p>
</TabPane>
</Tabs>
</div>
</PageContainer>
</TabPane>
<TabPane tab="Resource Viewer" key="3">
<p>Resource Viewer</p>
</TabPane>
<TabPane tab="YAML" key="4">
<p>YAML</p>
</TabPane>
</Tabs>
</div>
</PageContainer>
</div>
);
}
}

View File

@@ -9,13 +9,19 @@
.hasBorder {
border: 1px solid #eee;
.hasPadding {
padding: 16px 16px 0;
padding: 16px;
}
}
.title {
color: black;
font-size: 18px;
}
.hasBG {
margin-top: 50px;
margin-bottom: 8px;
padding-left: 16px;
background: rgba(0, 0, 0, 0.04);
}
.textAlignLeft {
padding-left: 16px;
text-align: left;

View File

@@ -1,8 +1,10 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import './index.less';
import { Form, Input, Button, Row, Col, Tabs, Table, Spin } from 'antd';
import { Form, Input, Button, Row, Col, Tabs, Table, Spin, Breadcrumb } from 'antd';
import { CheckCircleOutlined } from '@ant-design/icons';
import { connect } from 'dva';
import { Link } from 'umi';
import _ from 'lodash';
const { TabPane } = Tabs;
@@ -27,7 +29,12 @@ const columns = [
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text) => <a>{text}</a>,
render: (text) => (
<div>
<CheckCircleOutlined style={{ fontSize: '20px', color: '#4CAF51' }} />
<a style={{ marginLeft: '6px' }}>{text}</a>
</div>
),
},
{
title: 'Ready',
@@ -120,13 +127,6 @@ const data1 = [
LastTransition: '2d',
},
];
// const demoText = `H4sIAAAAAAAA/6xUwW7bOhD8lYc9U4lsWfazgB4C5Na0DZK0lyKHNbmyWVMkQ66UGob/vSBdt3GCxEXRmwjujmZnZrmFtbYKGrgkb9ymI8sgAL3+QiFqZ6EB9D6eDyMQ0BGjQkZotmCxI2hAOmcKDBtcrJCxGMrWfn+Ygsj30aNMRYpa7E0CloGQtbN3uqPI2HlobG+MAIMLMjEBo/cvcEGAW3wjyZH4LGh3JpHZ0Jl25yuMK2hgPkPV1lRJbKfjWVlNpqps69mkrNu2qv5X5UzV9ULVIOC4P1IYtHxzlOGXFEMJOwForeM8Rib8GjOdZD3Avz6Ae7QUiuWwhuYZtWEk/nuvrXp3+4cgpzw53f3SsePKjrLaHHpKSuTGG2opkJUUofm6Pc7O84FAHPL2e6ZTrPssZDWq5+O6GhVqPquLybyaF9gqKsbVTMpyXFZ1m8yVznJwxlCAJrEUsDBOrj8lopdkiDOvFk2k3f1OQPQkk4mRDEl2IX13yHJ1dSqQx6nYCWDqvEGmDPFkU/4+8/86qUbbNQWVw2lTFKABsrgwpN52+olQSWDUlsLe7VPm6Q6XlPdABspPS1imTihWcC8gUHR9yNHZQqCHniLnb+l7aKAuu/zsdC5soIHp5IPOZDLqdW/MtTNapqsL84ibCLt7cVi5Cyldb/njCYLYs+tS4e1R251bkz1EaK/Rz4IrbdfxEKEkDAdkWm4Sa9749LMbZ4y2y89epTgICEfnZrvb9yH3MZ9+BAAA//+bxjCThQUAAA
// objectset.rio.cattle.io/id service
// objectset.rio.cattle.io/owner-gvk rio.cattle.io/v1, Kind=Service
// objectset.rio.cattle.io/owner-name cool-aryabhata-v0fnxq6
// objectset.rio.cattle.io/owner-namespace default
// rio.cattle.io/mesh true
// Controlled By cool-aryabhata-v0fnxq6`;
@connect(({ loading, globalData }) => ({
loadingAll: loading.models.applist,
@@ -195,337 +195,247 @@ class TableList extends React.PureComponent {
render() {
const { hasShowEdit, hasShowEdit2 } = this.state;
// let finallyText;
// if (demoText && demoText.length > 50) {
// finallyText = (
// <Tooltip placement="topRight" title={demoText}>
// <Button>{`${demoText.substring(0, 50)}......`}</Button>
// </Tooltip>
// );
// } else {
// finallyText = <p>{demoText}</p>;
// }
const status = _.get(this.state.appDetailData, 'Status', '');
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
const metadata = _.get(Workload, 'metadata', {});
let containers = {};
// if (Workload.kind === 'ContainerizedWorkload') {
// containers = _.get(Workload, 'spec.containers[0]', {});
// } else if (Workload.kind === 'Deployment') {
// containers = _.get(Workload, 'spec.template.spec.containers[0]', {});
// }
containers = _.get(Workload, 'spec.containers[0]', {});
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
const appName = _.get(this.props, 'location.state.appName', '');
const envName = _.get(this.props, 'location.state.envName', '');
return (
<PageContainer>
<Spin spinning={loadingAll}>
<div className="card-container workload-detail">
<h2>{Workload.kind}</h2>
<p style={{ marginBottom: '20px' }}>
<i>
{Workload.apiVersion},Name={_.get(Workload, 'metadata.name', '')}
</i>
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<div>
<Row>
<Col span="12">
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Configuration</p>
<Row>
{Object.keys(containers).map((currentKey) => {
if (currentKey === 'ports') {
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link
to={{
pathname: '/ApplicationList/ApplicationListDetail',
state: { appName, envName },
}}
>
ApplicationListDetail
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>WorkloadDetail</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Spin spinning={loadingAll}>
<div className="card-container workload-detail">
<h2>{Workload.kind}</h2>
<p style={{ marginBottom: '20px' }}>
<i>
{Workload.apiVersion},Name={_.get(Workload, 'metadata.name', '')}
</i>
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<div>
<Row>
<Col span="12">
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Configuration</p>
<Row>
{Object.keys(containers).map((currentKey) => {
if (currentKey === 'ports') {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(containers[currentKey], '[0].containerPort', '')}
</p>
</Col>
</Fragment>
);
// eslint-disable-next-line no-else-return
} else if (currentKey === 'name') {
return <Fragment key={currentKey} />;
}
return (
<Fragment key={currentKey}>
<Col span="8">
<p>port</p>
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>
{_.get(containers[currentKey], '[0].containerPort', '')}
</p>
<p>{containers[currentKey]}</p>
</Col>
</Fragment>
);
// eslint-disable-next-line no-else-return
} else if (currentKey === 'name') {
return <Fragment key={currentKey} />;
}
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{containers[currentKey]}</p>
</Col>
</Fragment>
);
})}
</Row>
{/* <Row>
<Col span="10">
<div style={{ color: 'black' }}>
<p>Deployment Strategy</p>
<p>Rolling Update Strategy</p>
<p>Selectors</p>
<p>Min Ready Seconds</p>
<p>Revision History Limit</p>
<p>Replicas</p>
</div>
</Col>
<Col>
<p>RollingUpdate</p>
<p>Max Surge 25%, Max Unavailable 25%</p>
<p>
<Tag color="orange">aryabhataapp:cool</Tag>
<Tag color="orange">version:v0</Tag>
</p>
<p>0</p>
<p>10</p>
<p>1</p>
</Col>
</Row> */}
</div>
<div
className="hasPadding"
style={{ display: hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
labelAlign="left"
{...layout}
ef={this.formRefStep1}
name="control-ref"
onFinish={this.onFinishStep1}
>
<div className="relativeBox">
<Form.Item name="Replicas" label="Replicas">
<Input type="number" />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit}>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
})}
</Row>
</div>
<div
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
/>
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit}
className="hasPadding"
style={{ display: hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
labelAlign="left"
{...layout}
ef={this.formRefStep1}
name="control-ref"
onFinish={this.onFinishStep1}
>
Edit
<div className="relativeBox">
<Form.Item name="Replicas" label="Replicas">
<Input type="number" />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button
style={{ marginLeft: '16px' }}
onClick={this.changeShowEdit}
>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
<div
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
/>
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit}
>
Edit
</Button>
</div>
</div>
</div>
</Col>
<Col span="1" />
<Col span="10">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Status</p>
<p>{status}</p>
</div>
</div>
</Col>
</Row>
<p className="title hasBG">Pods</p>
<Table columns={columns} dataSource={data} pagination={false} />
<p className="title hasBG">Conditions</p>
<Table columns={columns1} dataSource={data1} pagination={false} />
<p className="title hasBG">Pod Template</p>
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Container cool-aryabhata-v0fnxq6</p>
<Row>
<Col span="2">
<div style={{ color: 'black' }}>
<p>Image</p>
<p>Args</p>
</div>
</Col>
<Col>
<p>secret</p>
<p>[&apos;-h&apos;]</p>
</Col>
</Row>
</div>
<div
className="hasPadding"
style={{ display: hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
style={{ width: '50%' }}
{...layout1}
labelAlign="left"
ef={this.formRefStep2}
name="control-ref"
onFinish={this.onFinishStep2}
>
<div className="relativeBox">
<Form.Item name="Image" label="Image">
<Input />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
<div
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
/>
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit2}
>
Edit
</Button>
</div>
</div>
</Col>
<Col span="1" />
<Col span="10">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Status</p>
<p>{status}</p>
{/* <Row>
<Col span="10">
<div style={{ color: 'black' }}>
<p>Avaliable Replicas</p>
<p>Ready Replicas</p>
<p>Total Replicas</p>
<p>Unavaliable Replicas</p>
<p>Updated Replicas</p>
</div>
</div>
</div>
</TabPane>
<TabPane tab="Metadata" key="2">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Metadata</p>
{Object.keys(metadata).map((currentKey6) => {
return (
<Row key={currentKey6}>
<Col span="4">
<p>{currentKey6}</p>
</Col>
<Col>
<p>0</p>
<p>0</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>{metadata[currentKey6]}</p>
</Col>
</Row> */}
</div>
</div>
</Col>
</Row>
<p className="title" style={{ marginTop: '16px' }}>
Pods
</p>
<Table columns={columns} dataSource={data} />
<p className="title">Conditions</p>
<Table columns={columns1} dataSource={data1} />
<p className="title">Pod Template</p>
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Container cool-aryabhata-v0fnxq6</p>
<Row>
<Col span="2">
<div style={{ color: 'black' }}>
<p>Image</p>
<p>Args</p>
</div>
</Col>
<Col>
<p>secret</p>
<p>[&apos;-h&apos;]</p>
</Col>
</Row>
</div>
<div
className="hasPadding"
style={{ display: hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
style={{ width: '50%' }}
{...layout1}
labelAlign="left"
ef={this.formRefStep2}
name="control-ref"
onFinish={this.onFinishStep2}
>
<div className="relativeBox">
<Form.Item name="Image" label="Image">
<Input />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
<div style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }} />
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit2}
>
Edit
</Button>
</div>
</Row>
);
})}
</div>
</div>
</div>
</TabPane>
<TabPane tab="Metadata" key="2">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Metadata</p>
{Object.keys(metadata).map((currentKey6) => {
return (
<Row key={currentKey6}>
<Col span="4">
<p>{currentKey6}</p>
</Col>
<Col>
<p>{metadata[currentKey6]}</p>
</Col>
</Row>
);
})}
{/* <Row>
<Col span="4">
<div style={{ color: 'black' }}>
<p>Age</p>
<p>Labels</p>
</div>
</Col>
<Col>
<p>2d</p>
<p>
<Tag color="orange">aryabhataapp:cool</Tag>
<Tag color="orange">version:v0</Tag>
</p>
</Col>
</Row>
<Row>
<Col span="4">
<div style={{ color: 'black' }}>
<p>Annotations</p>
</div>
</Col>
<Col span="20">
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>deployment.kubernetes.io/revision</p>
</div>
</Col>
<Col>
<p>1</p>
</Col>
</Row>
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>objectset.rio.cattle.io/applied</p>
</div>
</Col>
<Col span="16">{finallyText}</Col>
</Row>
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>objectset.rio.cattle.io/id</p>
</div>
</Col>
<Col span="16">
<p>service</p>
</Col>
</Row>
</Col>
</Row>
<Row>
<Col span="4">
<div style={{ color: 'black' }}>
<p>Controlled By</p>
</div>
</Col>
<Col>
<p>cool-aryabhata-v0fnxq6</p>
</Col>
</Row> */}
</div>
</div>
</TabPane>
<TabPane tab="Resource Viewer" key="3">
<p>Resource Viewer</p>
</TabPane>
<TabPane tab="YAML" key="4">
<p>YAML</p>
</TabPane>
</Tabs>
</div>
</Spin>
</PageContainer>
</TabPane>
<TabPane tab="Resource Viewer" key="3">
<p>Resource Viewer</p>
</TabPane>
<TabPane tab="YAML" key="4">
<p>YAML</p>
</TabPane>
</Tabs>
</div>
</Spin>
</PageContainer>
</div>
);
}
}

View File

@@ -9,13 +9,19 @@
.hasBorder {
border: 1px solid #eee;
.hasPadding {
padding: 16px 16px 0;
padding: 16px;
}
}
.title {
color: black;
font-size: 18px;
}
.hasBG {
margin-top: 50px;
margin-bottom: 8px;
padding-left: 16px;
background: rgba(0, 0, 0, 0.04);
}
.textAlignLeft {
padding-left: 16px;
text-align: left;

View File

@@ -11,7 +11,6 @@ export async function getapplist({ url }) {
export async function createApp({ params, url }) {
return request(url, {
method: 'POST',
// body:JSON.stringify(params),
data: {
...params,
},

View File

@@ -11,7 +11,6 @@ export async function getCapabilityCenterlist() {
export async function createCapabilityCenter({ params }) {
return request('/api/capability-centers/', {
method: 'put',
// body:JSON.stringify(params),
data: {
...params,
},

View File

@@ -35,11 +35,6 @@ const errorHandler = async (error) => {
} else if (!response) {
message.error('网络异常: 您的网络发生异常,无法连接服务器');
}
// throw error; // 如果throw. 错误将继续抛出.
// 如果return, 则将值作为返回. 'return;' 相当于return undefined, 在处理结果时判断response是否有值即可.
// return {some: 'data'};
// return response;
};
/**
@@ -47,9 +42,7 @@ const errorHandler = async (error) => {
*/
const request = extend({
errorHandler, // 默认错误处理
credentials: 'include', // 默认请求是否带上cookie,
// prefix: '/api/v1',
// timeout: 1000,
credentials: 'include', // 默认请求是否带上cookie
});
/*
@@ -75,6 +68,5 @@ request.interceptors.response.use(async (response) => {
}
// eslint-disable-next-line consistent-return
return data.data;
// return response;
});
export default request;

View File

@@ -1 +0,0 @@
kubevela.io

View File

@@ -1,2 +0,0 @@
theme: jekyll-theme-cayman
source: website

View File

@@ -1,25 +0,0 @@
## Overview
For developers and operators, KubeVela, as a out-of-box Cloud Native Application Management Platform, provides numerous
workloads and operation toolings for application defining, deployment, scaling, traffic, rollout, routing, monitoring,
logging, alerting, CICD and so on.
For platform builders, KubeVela, as a highly extensible PaaS/Serverless Core, provides pluginable capabilities, an elegant
way to integrate any Workloads and Traits.
![](./resource/3-minutes-demo.jpg)
## Get Started
Check out [Get Started](https://github.com/oam-dev/kubevela) to try KubeVela.
Explore [Cli docs](https://github.com/oam-dev/kubevela/tree/master/documentation/cli) to get a quick glance
of the power of KubeVela.
## Community
## Support or Contact
Having trouble with KubeVela? File an [issue](https://github.com/oam-dev/kubevela/issues), or contact us directly [Twitter](https://twitter.com/oam_dev) or [Gitter](https://gitter.im/oam-dev/)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 859 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Some files were not shown because too many files have changed in this diff Show More