@ -15,44 +15,26 @@
package main
package main
import (
import (
"context"
"errors"
"errors"
"flag"
"flag"
"fmt"
"fmt"
"net"
"net/url"
"os"
"os"
"strings"
"strings"
"time"
"time"
"github.com/ IBM/sarama "
"github.com/ OpenIMSDK/tools/component "
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/errs"
"github.com/go-zookeeper/zk"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/redis/go-redis/v9"
"gopkg.in/yaml.v3"
"gopkg.in/yaml.v3"
)
)
const (
const (
// defaultCfgPath is the default path of the configuration file.
// defaultCfgPath is the default path of the configuration file.
defaultCfgPath = "../../../../../config/config.yaml"
defaultCfgPath = "../../../../../config/config.yaml"
minioHealthCheckDuration = 1
maxRetry = 300
maxRetry = 300
componentStartErrCode = 6000
componentStartErrCode = 6000
configErrCode = 6001
configErrCode = 6001
mongoConnTimeout = 30 * time . Second
)
const (
colorRed = 31
colorGreen = 32
colorYellow = 33
)
)
var (
var (
@ -103,16 +85,16 @@ func main() {
for _ , check := range checks {
for _ , check := range checks {
str , err := check . function ( )
str , err := check . function ( )
if err != nil {
if err != nil {
e rrorPrint( fmt . Sprintf ( "Starting %s failed, %v" , check . name , err ) )
component . E rrorPrint( fmt . Sprintf ( "Starting %s failed, %v" , check . name , err ) )
allSuccess = false
allSuccess = false
break
break
} else {
} else {
s uccessPrint( fmt . Sprintf ( "%s connected successfully, %s" , check . name , str ) )
component . S uccessPrint( fmt . Sprintf ( "%s connected successfully, %s" , check . name , str ) )
}
}
}
}
if allSuccess {
if allSuccess {
s uccessPrint( "All components started successfully!" )
component . S uccessPrint( "All components started successfully!" )
return
return
}
}
@ -120,19 +102,6 @@ func main() {
os . Exit ( 1 )
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
}
// Helper function to get environment variable or default value
// Helper function to get environment variable or default value
func getEnv ( key , fallback string ) string {
func getEnv ( key , fallback string ) string {
if value , exists := os . LookupEnv ( key ) ; exists {
if value , exists := os . LookupEnv ( key ) ; exists {
@ -143,45 +112,23 @@ func getEnv(key, fallback string) string {
// checkMongo checks the MongoDB connection without retries
// checkMongo checks the MongoDB connection without retries
func checkMongo ( ) ( string , error ) {
func checkMongo ( ) ( string , error ) {
uri := getEnv ( "MONGO_URI" , buildMongoURI ( ) )
mongo := & component . Mongo {
Address : config . Config . Mongo . Address ,
ctx , cancel := context . WithTimeout ( context . Background ( ) , mongoConnTimeout )
Database : config . Config . Mongo . Database ,
defer cancel ( )
Username : config . Config . Mongo . Username ,
Password : config . Config . Mongo . Password ,
str := "ths addr is:" + strings . Join ( config . Config . Mongo . Address , "," )
MaxPoolSize : config . Config . Mongo . MaxPoolSize ,
client , err := mongo . Connect ( ctx , options . Client ( ) . ApplyURI ( uri ) )
if err != nil {
return "" , errs . Wrap ( errStr ( err , str ) )
}
}
defer client . Disconnect ( context . Background ( ) )
uri , uriExist := os . LookupEnv ( "MONGO_URI" )
if uriExist {
ctx , cancel = context . WithTimeout ( context . Background ( ) , mongoConnTimeout )
mongo . URL = uri
defer cancel ( )
if err = client . Ping ( ctx , nil ) ; err != nil {
return "" , errs . Wrap ( errStr ( err , str ) )
}
}
return str , nil
str , err := component . CheckMongo ( mongo )
}
if err != nil {
return "" , err
// buildMongoURI constructs the MongoDB URI using configuration settings
func buildMongoURI ( ) string {
// Fallback to config if environment variables are not set
username := config . Config . Mongo . Username
password := config . Config . Mongo . Password
database := config . Config . Mongo . Database
maxPoolSize := config . Config . Mongo . MaxPoolSize
mongodbHosts := strings . Join ( config . Config . Mongo . Address , "," )
if username != "" && password != "" {
return fmt . Sprintf ( "mongodb://%s:%s@%s/%s?maxPoolSize=%d" ,
username , password , mongodbHosts , database , maxPoolSize )
}
}
return fmt . Sprintf ( "mongodb://%s/%s?maxPoolSize=%d" ,
return str , nil
mongodbHosts , database , maxPoolSize )
}
}
// checkMinio checks the MinIO connection
// checkMinio checks the MinIO connection
@ -191,52 +138,24 @@ func checkMinio() (string, error) {
return "" , nil
return "" , nil
}
}
// Prioritize environment variables
endpoint , err := getMinioAddr ( "MINIO_ENDPOINT" , "MINIO_ADDRESS" , "MINIO_PORT" , config . Config . Object . Minio . Endpoint )
endpoint := getEnv ( "MINIO_ENDPOINT" , config . Config . Object . Minio . Endpoint )
accessKeyID := getEnv ( "MINIO_ACCESS_KEY_ID" , config . Config . Object . Minio . AccessKeyID )
secretAccessKey := getEnv ( "MINIO_SECRET_ACCESS_KEY" , config . Config . Object . Minio . SecretAccessKey )
useSSL := getEnv ( "MINIO_USE_SSL" , "false" ) // Assuming SSL is not used by default
if endpoint == "" || accessKeyID == "" || secretAccessKey == "" {
return "" , ErrConfig . Wrap ( "MinIO configuration missing" )
}
// Parse endpoint URL to determine if SSL is enabled
u , err := url . Parse ( endpoint )
if err != nil {
if err != nil {
str := "the endpoint is:" + endpoint
return "" , err
return "" , errs . Wrap ( errStr ( err , str ) )
}
}
secure := u . Scheme == "https" || useSSL == "true"
// Initialize MinIO client
minio := & component . Minio {
minioClient , err := minio . New ( u . Host , & minio . Options {
ApiURL : config . Config . Object . ApiURL ,
Creds : credentials . NewStaticV4 ( accessKeyID , secretAccessKey , "" ) ,
Endpoint : endpoint ,
Secure : secure ,
AccessKeyID : getEnv ( "MINIO_ACCESS_KEY_ID" , config . Config . Object . Minio . AccessKeyID ) ,
} )
SecretAccessKey : getEnv ( "MINIO_SECRET_ACCESS_KEY" , config . Config . Object . Minio . SecretAccessKey ) ,
str := "ths addr is:" + u . Host
SignEndpoint : config . Config . Object . Minio . SignEndpoint ,
if err != nil {
UseSSL : getEnv ( "MINIO_USE_SSL" , "false" ) ,
strs := fmt . Sprintf ( "%v;host:%s,accessKeyID:%s,secretAccessKey:%s,Secure:%v" , err , u . Host , accessKeyID , secretAccessKey , secure )
return "" , errs . Wrap ( err , strs )
}
}
// Perform health check
str , err := component . CheckMinio ( minio )
cancel , err := minioClient . HealthCheck ( time . Duration ( minioHealthCheckDuration ) * time . Second )
if err != nil {
if err != nil {
return "" , errs . Wrap ( errStr ( err , str ) )
return "" , err
}
defer cancel ( )
if minioClient . IsOffline ( ) {
str := fmt . Sprintf ( "Minio server is offline;%s" , str )
return "" , ErrComponentStart . Wrap ( str )
}
// Check for localhost in API URL and Minio SignEndpoint
if exactIP ( config . Config . Object . ApiURL ) == "127.0.0.1" || exactIP ( config . Config . Object . Minio . SignEndpoint ) == "127.0.0.1" {
return "" , ErrConfig . Wrap ( "apiURL or Minio SignEndpoint endpoint contain 127.0.0.1" )
}
}
return str , nil
return str , nil
}
}
@ -247,76 +166,48 @@ func checkRedis() (string, error) {
username := getEnv ( "REDIS_USERNAME" , config . Config . Redis . Username )
username := getEnv ( "REDIS_USERNAME" , config . Config . Redis . Username )
password := getEnv ( "REDIS_PASSWORD" , config . Config . Redis . Password )
password := getEnv ( "REDIS_PASSWORD" , config . Config . Redis . Password )
// Split address to handle multiple addresses for cluster setup
redis := & component . Redis {
redisAddresses := strings . Split ( address , "," )
Address : strings . Split ( address , "," ) ,
var redisClient redis . UniversalClient
if len ( redisAddresses ) > 1 {
// Use cluster client for multiple addresses
redisClient = redis . NewClusterClient ( & redis . ClusterOptions {
Addrs : redisAddresses ,
Username : username ,
Password : password ,
} )
} else {
// Use regular client for single address
redisClient = redis . NewClient ( & redis . Options {
Addr : redisAddresses [ 0 ] ,
Username : username ,
Username : username ,
Password : password ,
Password : password ,
} )
}
}
defer redisClient . Close ( )
// Ping Redis to check connectivity
addresses , err := getAddress ( "REDIS_ADDRESS" , "REDIS_PORT" , config . Config . Redis . Address )
_ , err := redisClient . Ping ( context . Background ( ) ) . Result ( )
str := "the addr is:" + strings . Join ( redisAddresses , "," )
if err != nil {
if err != nil {
return "" , err s. Wrap ( errStr ( err , str ) )
return "" , err
}
}
redis . Address = addresses
str , err := component . CheckRedis ( redis )
if err != nil {
return "" , err
}
return str , nil
return str , nil
}
}
// checkZookeeper checks the Zookeeper connection
// checkZookeeper checks the Zookeeper connection
func checkZookeeper ( ) ( string , error ) {
func checkZookeeper ( ) ( string , error ) {
// Prioritize environment variables
// Prioritize environment variables
schema := getEnv ( "ZOOKEEPER_SCHEMA" , "digest" )
address := getEnv ( "ZOOKEEPER_ADDRESS" , strings . Join ( config . Config . Zookeeper . ZkAddr , "," ) )
address := getEnv ( "ZOOKEEPER_ADDRESS" , strings . Join ( config . Config . Zookeeper . ZkAddr , "," ) )
username := getEnv ( "ZOOKEEPER_USERNAME" , config . Config . Zookeeper . Username )
password := getEnv ( "ZOOKEEPER_PASSWORD" , config . Config . Zookeeper . Password )
// Split addresses to handle multiple Zookeeper nodes
zk := & component . Zookeeper {
zookeeperAddresses := strings . Split ( address , "," )
Schema : getEnv ( "ZOOKEEPER_SCHEMA" , "digest" ) ,
ZkAddr : strings . Split ( address , "," ) ,
Username : getEnv ( "ZOOKEEPER_USERNAME" , config . Config . Zookeeper . Username ) ,
Password : getEnv ( "ZOOKEEPER_PASSWORD" , config . Config . Zookeeper . Password ) ,
}
// Connect to Zookeeper
addresses , err := getAddress ( "ZOOKEEPER_ADDRESS" , "ZOOKEEPER_PORT" , config . Config . Zookeeper . ZkAddr )
str := "the addr is:" + address
c , eventChan , err := zk . Connect ( zookeeperAddresses , time . Second ) // Adjust the timeout as necessary
if err != nil {
if err != nil {
return "" , errs . Wrap ( errStr ( err , str ) )
return "" , nil
}
timeout := time . After ( 5 * time . Second )
for {
select {
case event := <- eventChan :
if event . State == zk . StateConnected {
fmt . Println ( "Connected to Zookeeper" )
goto Connected
}
case <- timeout :
return "" , errs . Wrap ( errors . New ( "timeout waiting for Zookeeper connection" ) , "Zookeeper Addr: " + strings . Join ( config . Config . Zookeeper . ZkAddr , " " ) )
}
}
}
Connected :
zk . ZkAddr = addresses
defer c . Close ( )
// Set authentication if username and password are provided
str , err := component . CheckZookeeper ( zk )
if username != "" && password != "" {
if err != nil {
if err := c . AddAuth ( schema , [ ] byte ( username + ":" + password ) ) ; err != nil {
return "" , err
return "" , errs . Wrap ( errStr ( err , str ) )
}
}
}
return str , nil
return str , nil
}
}
@ -327,24 +218,21 @@ func checkKafka() (string, error) {
password := getEnv ( "KAFKA_PASSWORD" , config . Config . Kafka . Password )
password := getEnv ( "KAFKA_PASSWORD" , config . Config . Kafka . Password )
address := getEnv ( "KAFKA_ADDRESS" , strings . Join ( config . Config . Kafka . Addr , "," ) )
address := getEnv ( "KAFKA_ADDRESS" , strings . Join ( config . Config . Kafka . Addr , "," ) )
// Split addresses to handle multiple Kafka brokers
kafka := & component . Kafka {
kafkaAddresses := strings . Split ( address , "," )
Username : username ,
Password : password ,
Addr : strings . Split ( address , "," ) ,
}
// Configure Kafka client
addresses , err := getAddress ( "KAFKA_ADDRESS" , "KAFKA_PORT" , config . Config . Kafka . Addr )
cfg := sarama . NewConfig ( )
if err != nil {
if username != "" && password != "" {
return "" , nil
cfg . Net . SASL . Enable = true
cfg . Net . SASL . User = username
cfg . Net . SASL . Password = password
}
}
// Additional Kafka setup (e.g., TLS configuration) can be added here
kafka . Addr = addresses
// kafka.SetupTLSConfig(cfg)
// Create Kafka client
str , kafkaClient , err := component . CheckKafka ( kafka )
str := "the addr is:" + address
kafkaClient , err := sarama . NewClient ( kafkaAddresses , cfg )
if err != nil {
if err != nil {
return "" , err s. Wrap ( errStr ( err , str ) )
return "" , err
}
}
defer kafkaClient . Close ( )
defer kafkaClient . Close ( )
@ -379,22 +267,42 @@ func isTopicPresent(topic string, topics []string) bool {
return false
return false
}
}
func colorPrint( colorCode int , format string , a ... interface { } ) {
func getAddress( key1 , key2 string , fallback [ ] string ) ( [ ] string , error ) {
fmt. Printf ( "\x1b[%dm%s\x1b[0m\n" , colorCode , fmt . Sprintf ( format , a ... ) )
address, addrExist := os . LookupEnv ( key1 )
}
port , portExist := os . LookupEnv ( key2 )
func errorPrint ( s string ) {
if addrExist && portExist {
colorPrint ( colorRed , "%v" , s )
addresses := strings . Split ( address , "," )
}
for i , addr := range addresses {
addresses [ i ] = addr + ":" + port
func successPrint ( s string ) {
}
colorPrint ( colorGreen , "%v" , s )
return addresses , nil
}
} else if ! addrExist && portExist {
result := make ( [ ] string , len ( config . Config . Redis . Address ) )
func warningPrint ( s string ) {
for i , addr := range config . Config . Redis . Address {
colorPrint ( colorYellow , "Warning: But %v" , s )
add := strings . Split ( addr , ":" )
result [ i ] = add [ 0 ] + ":" + port
}
return result , nil
} else if addrExist && ! portExist {
return nil , errs . Wrap ( errors . New ( "the ZOOKEEPER_PORT of minio is empty" ) )
}
return fallback , nil
}
}
func errStr ( err error , str string ) error {
func getMinioAddr ( key1 , key2 , key3 , fallback string ) ( string , error ) {
return fmt . Errorf ( "%v;%s" , err , str )
// Prioritize environment variables
endpoint := getEnv ( key1 , fallback )
address , addressExist := os . LookupEnv ( key2 )
port , portExist := os . LookupEnv ( key3 )
if portExist && addressExist {
endpoint = "http://" + address + ":" + port
} else if ! portExist && addressExist {
return "" , errs . Wrap ( errors . New ( "the MINIO_PORT of minio is empty" ) )
} else if portExist && ! addressExist {
arr := strings . Split ( config . Config . Object . Minio . Endpoint , ":" )
arr [ 2 ] = port
endpoint = strings . Join ( arr , ":" )
}
return endpoint , nil
}
}