未验证 提交 be0f57ae 编写于 作者: W wankai123 提交者: GitHub

Support k8s configmap grouped dynamic configurations (#7570)

* Support k8s configmap grouped dynamic configurations

* Support single config read from multiple configmaps
上级 944b47b5
......@@ -48,6 +48,7 @@ Release Notes.
* Support collection type in dynamic configuration core.
* Support zookeeper grouped dynamic configurations.
* Fix NPE when OAP nodes synchronize events with each other in cluster mode.
* Support k8s configmap grouped dynamic configurations.
#### UI
......
......@@ -18,7 +18,7 @@
package org.apache.skywalking.oap.server.configuration.configmap;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
......@@ -40,9 +40,9 @@ public class ConfigmapConfigurationWatcherRegister extends ConfigWatcherRegister
@Override
public Optional<ConfigTable> readConfig(Set<String> keys) {
final ConfigTable configTable = new ConfigTable();
Optional<V1ConfigMap> v1ConfigMap = informer.configMap();
Map<String, String> configMapData = informer.configMapData();
for (final String name : keys) {
final String value = v1ConfigMap.map(V1ConfigMap::getData).map(data -> data.get(name)).orElse(null);
final String value = configMapData.get(name);
if (log.isDebugEnabled()) {
log.debug("read config: name:{} ,value:{}", name, value);
}
......@@ -53,8 +53,19 @@ public class ConfigmapConfigurationWatcherRegister extends ConfigWatcherRegister
@Override
public Optional<GroupConfigTable> readGroupConfig(final Set<String> keys) {
// TODO: implement readGroupConfig
return Optional.empty();
}
GroupConfigTable groupConfigTable = new GroupConfigTable();
Map<String, String> configMapData = informer.configMapData();
keys.forEach(key -> {
GroupConfigTable.GroupConfigItems groupConfigItems = new GroupConfigTable.GroupConfigItems(key);
groupConfigTable.addGroupConfigItems(groupConfigItems);
configMapData.forEach((groupItemKey, itemValue) -> {
if (groupItemKey.startsWith(key)) {
String itemName = groupItemKey.replaceFirst(key + ".", "");
groupConfigItems.add(new ConfigTable.ConfigItem(itemName, itemValue));
}
});
});
return Optional.of(groupConfigTable);
}
}
......@@ -28,8 +28,9 @@ import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.util.Config;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
......@@ -37,7 +38,6 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ConfigurationConfigmapInformer {
private Lister<V1ConfigMap> configMapLister;
private SharedInformerFactory factory;
......@@ -84,8 +84,18 @@ public class ConfigurationConfigmapInformer {
configMapLister = new Lister<>(configMapSharedIndexInformer.getIndexer());
}
public Optional<V1ConfigMap> configMap() {
return Optional.ofNullable(configMapLister.list().size() == 1 ? configMapLister.list().get(0) : null);
}
public Map<String, String> configMapData() {
Map<String, String> configMapData = new HashMap<>();
if (configMapLister != null && configMapLister.list() != null) {
configMapLister.list().forEach(cf -> {
Map<String, String> data = cf.getData();
if (data == null) {
return;
}
configMapData.putAll(data);
});
}
return configMapData;
}
}
......@@ -18,8 +18,9 @@
package org.apache.skywalking.oap.server.configuration.configmap;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import java.io.FileNotFoundException;
import java.io.Reader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
......@@ -27,6 +28,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
import org.apache.skywalking.oap.server.configuration.api.GroupConfigTable;
import org.apache.skywalking.oap.server.library.util.ResourceUtils;
import org.junit.Assert;
import org.junit.Before;
......@@ -59,8 +61,8 @@ public class ConfigmapConfigWatcherRegisterTest {
@Test
public void readConfigWhenConfigMapDataIsNull() throws Exception {
V1ConfigMap v1ConfigMap = new V1ConfigMap();
PowerMockito.doReturn(Optional.of(v1ConfigMap)).when(informer).configMap();
Map<String, String> configMapData = new HashMap<>();
PowerMockito.doReturn(configMapData).when(informer).configMapData();
Optional<ConfigTable> optionalConfigTable = register.readConfig(new HashSet<String>() {{
add("key1");
}});
......@@ -74,7 +76,7 @@ public class ConfigmapConfigWatcherRegisterTest {
@Test
public void readConfigWhenInformerNotwork() throws Exception {
PowerMockito.doReturn(Optional.empty()).when(informer).configMap();
PowerMockito.doReturn(new HashMap<>()).when(informer).configMapData();
Optional<ConfigTable> optionalConfigTable = register.readConfig(new HashSet<String>() {{
add("key1");
}});
......@@ -88,17 +90,15 @@ public class ConfigmapConfigWatcherRegisterTest {
@Test
public void readConfigWhenInformerWork() throws Exception {
Reader configmapReader = ResourceUtils.read("skywalking-dynamic-configmap.example.yaml");
Map<String, Map<String, String>> configmapMap = yaml.loadAs(configmapReader, Map.class);
V1ConfigMap v1ConfigMap = new V1ConfigMap();
v1ConfigMap.data(configmapMap.get("data"));
PowerMockito.doReturn(Optional.of(v1ConfigMap)).when(informer).configMap();
Map<String, String> configMapData = this.readMockConfigMapData();
PowerMockito.doReturn(configMapData).when(informer).configMapData();
Optional<ConfigTable> optionalConfigTable = register.readConfig(new HashSet<String>() {{
add("receiver-trace.default.slowDBAccessThreshold");
add("alarm.default.alarm-settings");
add("core.default.apdexThreshold");
add("receiver-trace.default.uninstrumentedGateways");
}});
Assert.assertTrue(optionalConfigTable.isPresent());
ConfigTable configTable = optionalConfigTable.get();
......@@ -108,4 +108,65 @@ public class ConfigmapConfigWatcherRegisterTest {
.collect(Collectors.toList());
Assert.assertEquals(list.size(), 4);
}
@Test
public void readGroupConfigWhenConfigMapDataIsNull() throws Exception {
Map<String, String> configMapData = new HashMap<>();
PowerMockito.doReturn(configMapData).when(informer).configMapData();
Optional<GroupConfigTable> optionalGroupConfigTable = register.readGroupConfig(new HashSet<String>() {{
add("key1");
}});
Assert.assertTrue(optionalGroupConfigTable.isPresent());
GroupConfigTable groupConfigTable = optionalGroupConfigTable.get();
Assert.assertEquals(groupConfigTable.getGroupItems().size(), 1);
Assert.assertEquals(groupConfigTable.getGroupItems().get(0).getName(), "key1");
Assert.assertEquals(groupConfigTable.getGroupItems().get(0).getItems().size(), 0);
}
@Test
public void readGroupConfigWhenInformerNotwork() throws Exception {
PowerMockito.doReturn(new HashMap<>()).when(informer).configMapData();
Optional<GroupConfigTable> optionalGroupConfigTable = register.readGroupConfig(new HashSet<String>() {{
add("key1");
}});
Assert.assertTrue(optionalGroupConfigTable.isPresent());
GroupConfigTable groupConfigTable = optionalGroupConfigTable.get();
Assert.assertEquals(groupConfigTable.getGroupItems().size(), 1);
Assert.assertEquals(groupConfigTable.getGroupItems().get(0).getName(), "key1");
Assert.assertEquals(groupConfigTable.getGroupItems().get(0).getItems().size(), 0);
}
@Test
public void readGroupConfigWhenInformerWork() throws Exception {
Map<String, String> configMapData = this.readMockConfigMapData();
PowerMockito.doReturn(configMapData).when(informer).configMapData();
Optional<GroupConfigTable> optionalGroupConfigTable = register.readGroupConfig(new HashSet<String>() {{
add("core.default.endpoint-name-grouping-openapi");
}});
Assert.assertTrue(optionalGroupConfigTable.isPresent());
GroupConfigTable groupConfigTable = optionalGroupConfigTable.get();
Assert.assertEquals(groupConfigTable.getGroupItems().size(), 1);
Assert.assertEquals(groupConfigTable.getGroupItems().get(0).getName(), "core.default.endpoint-name-grouping-openapi");
Assert.assertEquals(groupConfigTable.getGroupItems().get(0).getItems().size(), 3);
}
private Map<String, String> readMockConfigMapData() throws FileNotFoundException {
Reader configmapReader1 = ResourceUtils.read("skywalking-dynamic-configmap.example.yaml");
Reader configmapReader2 = ResourceUtils.read("skywalking-group-dynamic-configmap.example-serviceA.yaml");
Reader configmapReader3 = ResourceUtils.read("skywalking-group-dynamic-configmap.example-serviceB.yaml");
Map<String, Map<String, String>> configmapMap1 = yaml.loadAs(configmapReader1, Map.class);
Map<String, Map<String, String>> configmapMap2 = yaml.loadAs(configmapReader2, Map.class);
Map<String, Map<String, String>> configmapMap3 = yaml.loadAs(configmapReader3, Map.class);
Map<String, String> configMapData = new HashMap<>();
configMapData.putAll(configmapMap1.get("data"));
configMapData.putAll(configmapMap2.get("data"));
configMapData.putAll(configmapMap3.get("data"));
return configMapData;
}
}
......@@ -21,7 +21,7 @@ kind: ConfigMap
metadata:
name: skywalking-dynamic-config
labels:
app: skywalking-alarm
app: collector
release: skywalking
data:
receiver-trace.default.slowDBAccessThreshold: default:200,mongodb:50
......
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
#
#
apiVersion: v1
kind: ConfigMap
metadata:
name: skywalking-dynamic-config2
labels:
app: collector
release: skywalking
data:
core.default.endpoint-name-grouping-openapi.customerAPI-v1.yaml: |-
openapi: 3.0.0
x-sw-service-name: serviceA
info:
description: OpenAPI definition for SkyWalking test.
version: v1
title: Customer API
tags:
- name: customer
description: customer
paths:
/customers:
get:
tags:
- customer
summary: Get all customers list
description: Get all customers list.
operationId: getCustomers
responses:
"200":
description: Success
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Customer"
/customers/{id}:
get:
tags:
- customer
summary: Get customer details
description: Get customer details with the given id.
operationId: getCustomer
parameters:
- name: id
in: path
description: Customer id
required: true
schema:
type: integer
format: int64
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/CustomerDetails"
"400":
description: Invalid customer id
post:
tags:
- customer
summary: Update customer details
description: Update customer details with the given id.
operationId: updateCustomer
parameters:
- name: id
in: path
description: Customer id
required: true
schema:
type: integer
format: int64
- name: name
in: query
description: Customer name
required: true
schema:
type: string
responses:
"200":
description: successful operation
delete:
tags:
- customer
summary: Delete customer details
description: Delete customer details with the given id.
operationId: deleteCustomer
parameters:
- name: id
in: path
description: Customer id
required: true
schema:
type: integer
format: int64
responses:
"200":
description: successful operation
/customer/{region}/{country}:
get:
tags:
- customer
summary: Get customers regional
description: Get customers regional with the given id.
operationId: getCustomersRegional
parameters:
- name: region
in: path
description: Customers region
required: true
schema:
type: string
- name: country
in: path
description: Customers country
required: true
schema:
type: string
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/Customer"
"400":
description: Invalid parameters supplied
components:
schemas:
Customer:
type: object
description: Customer id and name
properties:
id:
type: integer
format: int64
description: Customer id
name:
type: string
description: Customer name
required:
- id
- name
CustomerDetails:
type: object
description: Customer details
properties:
id:
type: integer
format: int64
description: Customer id
name:
type: string
description: Customer name
description:
type: string
description: Customer description
required:
- id
- name
core.default.endpoint-name-grouping-openapi.productAPI-v1.yaml: |-
openapi: 3.0.0
x-sw-service-name: serviceA
info:
description: OpenAPI definition for SkyWalking test.
version: v1
title: Product API
tags:
- name: product
description: product
- name: relatedProducts
description: Related Products
paths:
/products:
get:
tags:
- product
summary: Get all products list
description: Get all products list.
operationId: getProducts
responses:
"200":
description: Success
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Product"
/products/{id}:
get:
tags:
- product
summary: Get product details
description: Get product details with the given id.
operationId: getProduct
parameters:
- name: id
in: path
description: Product id
required: true
schema:
type: integer
format: int64
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/ProductDetails"
"400":
description: Invalid product id
post:
tags:
- product
summary: Update product details
description: Update product details with the given id.
operationId: updateProduct
parameters:
- name: id
in: path
description: Product id
required: true
schema:
type: integer
format: int64
- name: name
in: query
description: Product name
required: true
schema:
type: string
responses:
"200":
description: successful operation
delete:
tags:
- product
summary: Delete product details
description: Delete product details with the given id.
operationId: deleteProduct
parameters:
- name: id
in: path
description: Product id
required: true
schema:
type: integer
format: int64
responses:
"200":
description: successful operation
/products/{id}/relatedProducts:
get:
tags:
- relatedProducts
summary: Get related products
description: Get related products with the given product id.
operationId: getRelatedProducts
parameters:
- name: id
in: path
description: Product id
required: true
schema:
type: integer
format: int64
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/RelatedProducts"
"400":
description: Invalid product id
components:
schemas:
Product:
type: object
description: Product id and name
properties:
id:
type: integer
format: int64
description: Product id
name:
type: string
description: Product name
required:
- id
- name
ProductDetails:
type: object
description: Product details
properties:
id:
type: integer
format: int64
description: Product id
name:
type: string
description: Product name
description:
type: string
description: Product description
required:
- id
- name
RelatedProducts:
type: object
description: Related Products
properties:
id:
type: integer
format: int32
description: Product id
relatedProducts:
type: array
description: List of related products
items:
$ref: "#/components/schemas/Product"
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
#
#
apiVersion: v1
kind: ConfigMap
metadata:
name: skywalking-dynamic-config3
labels:
app: collector
release: skywalking
data:
core.default.endpoint-name-grouping-openapi.productAPI-v2.yaml: |-
openapi: 3.0.0
x-sw-service-name: serviceB
info:
description: OpenAPI definition for SkyWalking test.
version: v2
title: Product API
tags:
- name: product
description: product
- name: relatedProducts
description: Related Products
paths:
/products:
get:
tags:
- product
summary: Get all products list
description: Get all products list.
operationId: getProducts
responses:
"200":
description: Success
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Product"
/products/{region}/{country}:
get:
tags:
- product
summary: Get products regional
description: Get products regional with the given id.
operationId: getProductRegional
parameters:
- name: region
in: path
description: Products region
required: true
schema:
type: string
- name: country
in: path
description: Products country
required: true
schema:
type: string
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/Product"
"400":
description: Invalid parameters supplied
/products/{id}:
get:
tags:
- product
summary: Get product details
description: Get product details with the given id.
operationId: getProduct
parameters:
- name: id
in: path
description: Product id
required: true
schema:
type: integer
format: int64
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/ProductDetails"
"400":
description: Invalid product id
post:
tags:
- product
summary: Update product details
description: Update product details with the given id.
operationId: updateProduct
parameters:
- name: id
in: path
description: Product id
required: true
schema:
type: integer
format: int64
- name: name
in: query
description: Product name
required: true
schema:
type: string
responses:
"200":
description: successful operation
delete:
tags:
- product
summary: Delete product details
description: Delete product details with the given id.
operationId: deleteProduct
parameters:
- name: id
in: path
description: Product id
required: true
schema:
type: integer
format: int64
responses:
"200":
description: successful operation
/products/{id}/relatedProducts:
get:
tags:
- relatedProducts
summary: Get related products
description: Get related products with the given product id.
operationId: getRelatedProducts
parameters:
- name: id
in: path
description: Product id
required: true
schema:
type: integer
format: int64
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/RelatedProducts"
"400":
description: Invalid product id
components:
schemas:
Product:
type: object
description: Product id and name
properties:
id:
type: integer
format: int64
description: Product id
name:
type: string
description: Product name
required:
- id
- name
ProductDetails:
type: object
description: Product details
properties:
id:
type: integer
format: int64
description: Product id
name:
type: string
description: Product name
description:
type: string
description: Product description
required:
- id
- name
RelatedProducts:
type: object
description: Related Products
properties:
id:
type: integer
format: int32
description: Product id
relatedProducts:
type: array
description: List of related products
items:
$ref: "#/components/schemas/Product"
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册