From 9df16eaf258cba80269d00d7acbf14b540723323 Mon Sep 17 00:00:00 2001 From: hanzhixiao <709674996@qq.com> Date: Thu, 10 Aug 2023 10:50:05 +0800 Subject: [PATCH] new_815 Signed-off-by: hanzhixiao <709674996@qq.com> --- Dockerfile | 7 +- docker-compose.yaml | 3 +- go.work | 3 +- scripts/docker_start_all.sh | 9 + scripts/start_all.sh | 13 ++ scripts/start_component_check.sh | 42 +++++ tools/component/go.mod | 3 + tools/component/main.go | 281 +++++++++++++++++++++++++++++++ 8 files changed, 356 insertions(+), 5 deletions(-) create mode 100644 scripts/start_component_check.sh create mode 100644 tools/component/go.mod create mode 100644 tools/component/main.go diff --git a/Dockerfile b/Dockerfile index 6504f15aec..df564e5987 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,11 +10,11 @@ ENV GOPROXY=$GOPROXY # Set up the working directory WORKDIR /openim/openim-server -COPY go.mod go.sum ./ -RUN go mod download +COPY go.mod go.sum go.work go.work.sum ./ +#RUN go mod download # Copy all files to the container -ADD . . +ADD ../../Desktop . RUN make clean RUN make build @@ -27,5 +27,6 @@ WORKDIR ${SERVER_WORKDIR} COPY --from=builder ${OPENIM_SERVER_CMDDIR} /openim/openim-server/scripts COPY --from=builder ${SERVER_WORKDIR}/config /openim/openim-server/config COPY --from=builder ${SERVER_WORKDIR}/_output/bin/platforms /openim/openim-server/_output/bin/platforms +COPY --from=builder ${SERVER_WORKDIR}/_output/bin-tools/platforms /openim/openim-server/_output/bin-tools/platforms CMD ["bash","-c","${OPENIM_SERVER_CMDDIR}/docker_start_all.sh"] diff --git a/docker-compose.yaml b/docker-compose.yaml index 2c0c98bd72..d6e8dfded4 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -97,7 +97,8 @@ services: command: minio server /data --console-address ':9090' openim-server: - image: ghcr.io/openimsdk/openim-server:latest + #image: ghcr.io/openimsdk/openim-server:latest + build: ../../Desktop container_name: openim-server volumes: - ./logs:/openim/openim-server/logs diff --git a/go.work b/go.work index 09e86f0320..757933536e 100644 --- a/go.work +++ b/go.work @@ -1,7 +1,8 @@ go 1.20 use ( - . + . + ./tools/component ./tools/infra ./tools/ncpu ) diff --git a/scripts/docker_start_all.sh b/scripts/docker_start_all.sh index f617c50575..24847e21e2 100755 --- a/scripts/docker_start_all.sh +++ b/scripts/docker_start_all.sh @@ -30,6 +30,15 @@ need_to_start_server_shell=( ${SCRIPTS_ROOT}/start_cron.sh ) +component_check=start_component_check.sh +chmod +x $SCRIPTS_ROOT/$component_check +$SCRIPTS_ROOT/$component_check +if [ $? -ne 0 ]; then + # Print error message and exit + echo "${BOLD_PREFIX}${RED_PREFIX}Error executing ${component_check}. Exiting...${COLOR_SUFFIX}" + exit -1 +fi + #fixme The 10 second delay to start the project is for the docker-compose one-click to start openIM when the infrastructure dependencies are not started sleep 10 diff --git a/scripts/start_all.sh b/scripts/start_all.sh index b6b4e34288..bb68546c76 100755 --- a/scripts/start_all.sh +++ b/scripts/start_all.sh @@ -77,6 +77,19 @@ need_to_start_server_shell=( start_cron.sh ) +component_check=start_component_check.sh +echo -e "" +chmod +x $component_check +echo -e "=========> ${BACKGROUND_GREEN}Executing ${component_check}...${COLOR_SUFFIX}" +echo -e "" +./$component_check +if [ $? -ne 0 ]; then + # Print error message and exit + echo -e "${BOLD_PREFIX}${RED_PREFIX}Error executing ${component_check}. Exiting...${COLOR_SUFFIX}" + exit -1 +fi + + # Loop through the script names and execute them for i in ${need_to_start_server_shell[*]}; do chmod +x $i diff --git a/scripts/start_component_check.sh b/scripts/start_component_check.sh new file mode 100644 index 0000000000..6d9fa227b3 --- /dev/null +++ b/scripts/start_component_check.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# 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. + +#Include shell font styles and some basic information +SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + +#Include shell font styles and some basic information +source $SCRIPTS_ROOT/style_info.sh +source $SCRIPTS_ROOT/path_info.sh +source $SCRIPTS_ROOT/function.sh + +echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}" +echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}" + +bin_dir="$BIN_DIR" +logs_dir="$OPENIM_ROOT/logs" + +cd ${component_check_binary_root} +echo -e "${YELLOW_PREFIX}=======>$PWD${COLOR_SUFFIX}" +cmd="./${component_check}" +echo "==========================start components checking===========================">>$OPENIM_ROOT/logs/openIM.log +$cmd + +if [ $? -ne 0 ]; then + exit 1 +fi + + diff --git a/tools/component/go.mod b/tools/component/go.mod new file mode 100644 index 0000000000..bc7abc133e --- /dev/null +++ b/tools/component/go.mod @@ -0,0 +1,3 @@ +module github.com/OpenIMSDK/Open-IM-Server/tools/component + +go 1.19 diff --git a/tools/component/main.go b/tools/component/main.go new file mode 100644 index 0000000000..eb7def9fc4 --- /dev/null +++ b/tools/component/main.go @@ -0,0 +1,281 @@ +package main + +import ( + "context" + "fmt" + "github.com/OpenIMSDK/Open-IM-Server/pkg/common/config" + "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/utils" + "github.com/Shopify/sarama" + "github.com/go-zookeeper/zk" + "github.com/minio/minio-go/v7" + "github.com/redis/go-redis/v9" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" + "gopkg.in/yaml.v3" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "net" + "net/url" + "os" + "strings" + "time" + + "github.com/minio/minio-go/v7/pkg/credentials" +) + +const ( + cfgPath = "../../../../../config/config.yaml" + minioHealthCheckDuration = 1 + maxRetry = 100 + componentStartErrCode = 6000 + configErrCode = 6001 +) + +var ( + ErrComponentStart = errs.NewCodeError(componentStartErrCode, "ComponentStartErr") + ErrConfig = errs.NewCodeError(configErrCode, "Config file is incorrect") +) + +func initCfg() error { + data, err := os.ReadFile(cfgPath) + if err != nil { + return err + } + if err = yaml.Unmarshal(data, &config.Config); err != nil { + return err + } + return nil +} + +func main() { + err := initCfg() + if err != nil { + fmt.Printf("Read config failed: %v", err.Error()) + } + for i := 0; i < maxRetry; i++ { + if i != 0 { + time.Sleep(3 * time.Second) + } + fmt.Printf("Checking components Round %v......\n", i+1) + // Check MySQL + if err := checkMysql(); err != nil { + errorPrint(fmt.Sprintf("Starting Mysql failed: %v. Please make sure your mysql service has started", err.Error())) + continue + } else { + successPrint(fmt.Sprint("Mysql starts successfully")) + } + + // Check MongoDB + if err := checkMongo(); err != nil { + errorPrint(fmt.Sprintf("Starting Mongo failed: %v. Please make sure your monngo service has started", err.Error())) + continue + } else { + successPrint(fmt.Sprint("Mongo starts successfully")) + } + + // Check Minio + if err := checkMinio(); err != nil { + if index := strings.Index(err.Error(), utils.IntToString(configErrCode)); index != -1 { + successPrint(fmt.Sprint("Minio starts successfully")) + warningPrint(fmt.Sprintf("%v. Please modify your config file", err.Error())) + } else { + errorPrint(fmt.Sprintf("Starting Minio failed: %v. Please make sure your Minio service has started", err.Error())) + continue + } + } else { + successPrint(fmt.Sprint("Minio starts successfully")) + } + // Check Redis + if err := checkRedis(); err != nil { + errorPrint(fmt.Sprintf("Starting Redis failed: %v.Please make sure your Redis service has started", err.Error())) + continue + } else { + successPrint(fmt.Sprint("Redis starts successfully")) + } + + // Check Zookeeper + if err := checkZookeeper(); err != nil { + errorPrint(fmt.Sprintf("Starting Zookeeper failed: %v.Please make sure your Zookeeper service has started", err.Error())) + continue + } else { + successPrint(fmt.Sprint("Zookeeper starts successfully")) + } + + // Check Kafka + if err := checkKafka(); err != nil { + errorPrint(fmt.Sprintf("Starting Kafka failed: %v.Please make sure your Kafka service has started", err.Error())) + continue + } else { + successPrint(fmt.Sprint("Kafka starts successfully")) + } + successPrint(fmt.Sprint("All components starts successfully")) + os.Exit(0) + } + os.Exit(1) +} + +func exactIP(urll string) string { + u, _ := url.Parse(urll) + host, _, err := net.SplitHostPort(u.Host) + if err != nil { + host = u.Host + } + if strings.HasSuffix(host, ":") { + host = host[0 : len(host)-1] + } + return host +} + +func checkMysql() error { + dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true&loc=Local", + config.Config.Mysql.Username, config.Config.Mysql.Password, config.Config.Mysql.Address[0], "mysql") + db, err := gorm.Open(mysql.Open(dsn), nil) + if err != nil { + return err + } else { + sqlDB, err := db.DB() + err = sqlDB.Ping() + sqlDB.Close() + if err != nil { + return err + } + } + return nil +} + +func checkMongo() error { + mongodbHosts := "" + for i, v := range config.Config.Mongo.Address { + if i == len(config.Config.Mongo.Address)-1 { + mongodbHosts += v + } else { + mongodbHosts += v + "," + } + } + client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI( + fmt.Sprintf("mongodb://%v:%v@%v/?authSource=admin", + config.Config.Mongo.Username, config.Config.Mongo.Password, mongodbHosts))) + if err != nil { + return err + } else { + err = client.Ping(context.TODO(), &readpref.ReadPref{}) + client.Disconnect(context.TODO()) + if err != nil { + return err + } + } + return nil +} + +func checkMinio() error { + if config.Config.Object.Enable == "minio" { + if exactIP(config.Config.Object.ApiURL) == "127.0.0.1" || exactIP(config.Config.Object.Minio.Endpoint) == "127.0.0.1" { + return ErrConfig.Wrap("apiURL or Minio endpoint contain 127.0.0.1.") + } + conf := config.Config.Object.Minio + u, _ := url.Parse(conf.Endpoint) + minioClient, err := minio.New(u.Host, &minio.Options{ + Creds: credentials.NewStaticV4(conf.AccessKeyID, conf.SecretAccessKey, ""), + Secure: u.Scheme == "https", + }) + if err != nil { + return err + } + + cancel, err := minioClient.HealthCheck(time.Duration(minioHealthCheckDuration) * time.Second) + if err != nil { + return err + } else { + if minioClient.IsOffline() { + return ErrComponentStart.Wrap("Minio server is offline") + } + cancel() + } + } + return nil +} + +func checkRedis() error { + var redisClient redis.UniversalClient + if len(config.Config.Redis.Address) > 1 { + redisClient = redis.NewClusterClient(&redis.ClusterOptions{ + Addrs: config.Config.Redis.Address, + Username: config.Config.Redis.Username, + Password: config.Config.Redis.Password, + }) + } else { + redisClient = redis.NewClient(&redis.Options{ + Addr: config.Config.Redis.Address[0], + Username: config.Config.Redis.Username, + Password: config.Config.Redis.Password, + }) + } + _, err := redisClient.Ping(context.Background()).Result() + if err != nil { + return err + } + return nil +} + +func checkZookeeper() error { + c, _, err := zk.Connect(config.Config.Zookeeper.ZkAddr, time.Second) + if err != nil { + return err + } else { + if config.Config.Zookeeper.Username != "" && config.Config.Zookeeper.Password != "" { + if err := c.AddAuth("digest", []byte(config.Config.Zookeeper.Username+":"+config.Config.Zookeeper.Password)); err != nil { + c.Close() + return err + } + } + _, _, err = c.Get("/") + if err != nil { + c.Close() + return err + } + } + return nil +} + +func checkKafka() error { + cfg := sarama.NewConfig() + if config.Config.Kafka.Username != "" && config.Config.Kafka.Password != "" { + cfg.Net.SASL.Enable = true + cfg.Net.SASL.User = config.Config.Kafka.Username + cfg.Net.SASL.Password = config.Config.Kafka.Password + } + kafkaClient, err := sarama.NewClient(config.Config.Kafka.Addr, cfg) + if err != nil { + return err + } else { + topics, err := kafkaClient.Topics() + kafkaClient.Close() + if err != nil { + return err + } + if !utils.IsContain(config.Config.Kafka.MsgToMongo.Topic, topics) { + return ErrComponentStart.Wrap(fmt.Sprintf("kafka doesn't contain topic:%v", config.Config.Kafka.MsgToMongo.Topic)) + } + if !utils.IsContain(config.Config.Kafka.MsgToPush.Topic, topics) { + return ErrComponentStart.Wrap(fmt.Sprintf("kafka doesn't contain topic:%v", config.Config.Kafka.MsgToPush.Topic)) + } + if !utils.IsContain(config.Config.Kafka.LatestMsgToRedis.Topic, topics) { + return ErrComponentStart.Wrap(fmt.Sprintf("kafka doesn't contain topic:%v", config.Config.Kafka.LatestMsgToRedis.Topic)) + } + } + return nil +} + +func errorPrint(s string) { + fmt.Printf("\x1b[%dm%v\x1b[0m\n", 31, s) +} + +func successPrint(s string) { + fmt.Printf("\x1b[%dm%v\x1b[0m\n", 32, s) +} + +func warningPrint(s string) { + fmt.Printf("\x1b[%dmWarning: But %v\x1b[0m\n", 33, s) +}