ಕುಬರ್ನೆಟ್ಸ್ ಒಳಗೆ ಮತ್ತು ಹೊರಗೆ ಪ್ರಾಜೆಕ್ಟ್ ಕಾನ್ಫಿಗರೇಶನ್

ನಾನು ಇತ್ತೀಚೆಗೆ ಬರೆದಿದ್ದೇನೆ ಡಾಕರ್‌ನಲ್ಲಿ ಪ್ರಾಜೆಕ್ಟ್ ಲೈಫ್ ಮತ್ತು ಅದರ ಹೊರಗೆ ಡೀಬಗ್ ಮಾಡುವ ಕೋಡ್ ಬಗ್ಗೆ ಉತ್ತರಿಸಿ, ಅಲ್ಲಿ ನೀವು ನಿಮ್ಮದೇ ಆದ ಕಾನ್ಫಿಗರೇಶನ್ ಸಿಸ್ಟಮ್ ಅನ್ನು ಮಾಡಬಹುದು ಎಂದು ಅವರು ಸಂಕ್ಷಿಪ್ತವಾಗಿ ಪ್ರಸ್ತಾಪಿಸಿದ್ದಾರೆ, ಇದರಿಂದ ಸೇವೆಯು ಕುಬರ್‌ನಲ್ಲಿ ಉತ್ತಮವಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ, ರಹಸ್ಯಗಳನ್ನು ಎಳೆಯುತ್ತದೆ ಮತ್ತು ಡಾಕರ್‌ನ ಹೊರಗೆ ಸಂಪೂರ್ಣವಾಗಿ ಸ್ಥಳೀಯವಾಗಿ ಅನುಕೂಲಕರವಾಗಿ ಚಲಿಸುತ್ತದೆ. ಏನೂ ಸಂಕೀರ್ಣವಾಗಿಲ್ಲ, ಆದರೆ ವಿವರಿಸಿದ "ಪಾಕವಿಧಾನ" ಯಾರಿಗಾದರೂ ಉಪಯುಕ್ತವಾಗಬಹುದು :) ಕೋಡ್ ಪೈಥಾನ್‌ನಲ್ಲಿದೆ, ಆದರೆ ತರ್ಕವು ಭಾಷೆಗೆ ಸಂಬಂಧಿಸಿಲ್ಲ.

ಕುಬರ್ನೆಟ್ಸ್ ಒಳಗೆ ಮತ್ತು ಹೊರಗೆ ಪ್ರಾಜೆಕ್ಟ್ ಕಾನ್ಫಿಗರೇಶನ್

ಪ್ರಶ್ನೆಯ ಹಿನ್ನೆಲೆ ಹೀಗಿದೆ: ಒಂದು ಕಾಲದಲ್ಲಿ ಒಂದು ಯೋಜನೆ ಇತ್ತು, ಮೊದಲಿಗೆ ಇದು ಉಪಯುಕ್ತತೆಗಳು ಮತ್ತು ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳನ್ನು ಹೊಂದಿರುವ ಸಣ್ಣ ಏಕಶಿಲೆಯಾಗಿತ್ತು, ಆದರೆ ಕಾಲಾನಂತರದಲ್ಲಿ ಅದು ಬೆಳೆದು, ಸೇವೆಗಳಾಗಿ ವಿಂಗಡಿಸಲ್ಪಟ್ಟಿತು, ಅದು ಪ್ರತಿಯಾಗಿ ಮೈಕ್ರೋ ಸರ್ವೀಸ್‌ಗಳಾಗಿ ವಿಂಗಡಿಸಲು ಪ್ರಾರಂಭಿಸಿತು, ಮತ್ತು ನಂತರ ಅಳೆಯಲಾಗುತ್ತದೆ. ಮೊದಲಿಗೆ, ಇದೆಲ್ಲವನ್ನೂ ಬೇರ್ ವಿಪಿಎಸ್‌ನಲ್ಲಿ ಮಾಡಲಾಯಿತು, ಅನ್ಸಿಬಲ್ ಬಳಸಿ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕೋಡ್ ಅನ್ನು ಹೊಂದಿಸುವ ಮತ್ತು ನಿಯೋಜಿಸುವ ಪ್ರಕ್ರಿಯೆಗಳು, ಮತ್ತು ಪ್ರತಿ ಸೇವೆಯನ್ನು ಅಗತ್ಯ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಕೀಗಳೊಂದಿಗೆ YAML ಸಂರಚನೆಯೊಂದಿಗೆ ಸಂಕಲಿಸಲಾಗಿದೆ ಮತ್ತು ಇದೇ ರೀತಿಯ ಸಂರಚನಾ ಫೈಲ್ ಅನ್ನು ಬಳಸಲಾಗುತ್ತದೆ. ಸ್ಥಳೀಯ ಉಡಾವಣೆಗಳು, ಇದು ತುಂಬಾ ಅನುಕೂಲಕರವಾಗಿದೆ, ಏಕೆಂದರೆ .k ಈ ಸಂರಚನೆಯನ್ನು ಜಾಗತಿಕ ವಸ್ತುವಿಗೆ ಲೋಡ್ ಮಾಡಲಾಗಿದೆ, ಯೋಜನೆಯಲ್ಲಿ ಎಲ್ಲಿಂದಲಾದರೂ ಪ್ರವೇಶಿಸಬಹುದು.

ಆದಾಗ್ಯೂ, ಮೈಕ್ರೊ ಸರ್ವೀಸ್‌ಗಳ ಸಂಖ್ಯೆಯಲ್ಲಿ ಬೆಳವಣಿಗೆ, ಅವುಗಳ ಸಂಪರ್ಕಗಳು ಮತ್ತು ಕೇಂದ್ರೀಕೃತ ಲಾಗಿಂಗ್ ಮತ್ತು ಮೇಲ್ವಿಚಾರಣೆಯ ಅಗತ್ಯವಿದೆ, ಕುಬೇರ್‌ಗೆ ಹೋಗುವುದನ್ನು ಮುನ್ಸೂಚಿಸಿದೆ, ಅದು ಇನ್ನೂ ಪ್ರಗತಿಯಲ್ಲಿದೆ. ಪ್ರಸ್ತಾಪಿಸಲಾದ ಸಮಸ್ಯೆಗಳನ್ನು ಪರಿಹರಿಸುವಲ್ಲಿ ಸಹಾಯದೊಂದಿಗೆ, ಕುಬರ್ನೆಟ್ಸ್ ಮೂಲಸೌಕರ್ಯ ನಿರ್ವಹಣೆಗೆ ತನ್ನ ವಿಧಾನಗಳನ್ನು ನೀಡುತ್ತದೆ, ಸೇರಿದಂತೆ ಎಂದು ಕರೆಯಲ್ಪಡುವ ರಹಸ್ಯಗಳು и ಅವರೊಂದಿಗೆ ಕೆಲಸ ಮಾಡುವ ಮಾರ್ಗಗಳು. ಕಾರ್ಯವಿಧಾನವು ಪ್ರಮಾಣಿತ ಮತ್ತು ವಿಶ್ವಾಸಾರ್ಹವಾಗಿದೆ, ಆದ್ದರಿಂದ ಅದನ್ನು ಬಳಸದಿರುವುದು ಅಕ್ಷರಶಃ ಪಾಪವಾಗಿದೆ! ಆದರೆ ಅದೇ ಸಮಯದಲ್ಲಿ, ಸಂರಚನೆಯೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ನನ್ನ ಪ್ರಸ್ತುತ ಸ್ವರೂಪವನ್ನು ನಿರ್ವಹಿಸಲು ನಾನು ಬಯಸುತ್ತೇನೆ: ಮೊದಲನೆಯದಾಗಿ, ಯೋಜನೆಯ ವಿವಿಧ ಮೈಕ್ರೋಸರ್ವಿಸ್‌ಗಳಲ್ಲಿ ಏಕರೂಪವಾಗಿ ಬಳಸಲು ಮತ್ತು ಎರಡನೆಯದಾಗಿ, ಒಂದು ಸರಳವನ್ನು ಬಳಸಿಕೊಂಡು ಸ್ಥಳೀಯ ಗಣಕದಲ್ಲಿ ಕೋಡ್ ಅನ್ನು ಚಲಾಯಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ. config ಫೈಲ್.

ಈ ನಿಟ್ಟಿನಲ್ಲಿ, ಕಾನ್ಫಿಗರೇಶನ್ ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ನಿರ್ಮಿಸುವ ಕಾರ್ಯವಿಧಾನವನ್ನು ನಮ್ಮ ಕ್ಲಾಸಿಕ್ ಕಾನ್ಫಿಗರ್ ಫೈಲ್‌ನೊಂದಿಗೆ ಮತ್ತು ಕುಬರ್‌ನಿಂದ ರಹಸ್ಯಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ಸಾಧ್ಯವಾಗುವಂತೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ. ಮೂರನೇ ಪೈಥಾನ್‌ನ ಭಾಷೆಯಲ್ಲಿ ಹೆಚ್ಚು ಕಟ್ಟುನಿಟ್ಟಾದ ಸಂರಚನಾ ರಚನೆಯನ್ನು ಈ ಕೆಳಗಿನಂತೆ ನಿರ್ದಿಷ್ಟಪಡಿಸಲಾಗಿದೆ:

ನಿರ್ದೇಶನ[str, Dict[str, Union[str, int, float]]]

ಅಂದರೆ, ಅಂತಿಮ ಕಾಗ್‌ಫಿಗ್ ಹೆಸರಿಸಲಾದ ವಿಭಾಗಗಳನ್ನು ಹೊಂದಿರುವ ನಿಘಂಟು, ಪ್ರತಿಯೊಂದೂ ಸರಳ ಪ್ರಕಾರಗಳಿಂದ ಮೌಲ್ಯಗಳನ್ನು ಹೊಂದಿರುವ ನಿಘಂಟು. ಮತ್ತು ವಿಭಾಗಗಳು ನಿರ್ದಿಷ್ಟ ಪ್ರಕಾರದ ಸಂಪನ್ಮೂಲಗಳಿಗೆ ಸಂರಚನೆ ಮತ್ತು ಪ್ರವೇಶವನ್ನು ವಿವರಿಸುತ್ತದೆ. ನಮ್ಮ ಸಂರಚನೆಯ ತುಣುಕಿನ ಉದಾಹರಣೆ:

adminka:
  django_secret: "ExtraLongAndHardCode"

db_main:
  engine: mysql
  host: 256.128.64.32
  user: cool_user
  password: "SuperHardPassword"

redis:
  host: 256.128.64.32
  pw: "SuperHardPassword"
  port: 26379

smtp:
  server: smtp.gmail.com
  port: 465
  email: [email protected]
  pw: "SuperHardPassword"

ಅದೇ ಸಮಯದಲ್ಲಿ, ಕ್ಷೇತ್ರ engine ಡೇಟಾಬೇಸ್‌ಗಳನ್ನು SQLite ನಲ್ಲಿ ಸ್ಥಾಪಿಸಬಹುದು, ಮತ್ತು redis ಗೆ ಹೊಂದಿಸಲಾಗಿದೆ mock, ಉಳಿಸಲು ಫೈಲ್‌ನ ಹೆಸರನ್ನು ಸಹ ನಿರ್ದಿಷ್ಟಪಡಿಸುವುದು - ಈ ನಿಯತಾಂಕಗಳನ್ನು ಸರಿಯಾಗಿ ಗುರುತಿಸಲಾಗಿದೆ ಮತ್ತು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲಾಗಿದೆ, ಇದು ಡೀಬಗ್ ಮಾಡುವಿಕೆ, ಘಟಕ ಪರೀಕ್ಷೆ ಮತ್ತು ಯಾವುದೇ ಇತರ ಅಗತ್ಯಗಳಿಗಾಗಿ ಸ್ಥಳೀಯವಾಗಿ ಕೋಡ್ ಅನ್ನು ಚಲಾಯಿಸಲು ಸುಲಭಗೊಳಿಸುತ್ತದೆ. ಇದು ನಮಗೆ ವಿಶೇಷವಾಗಿ ಮುಖ್ಯವಾಗಿದೆ ಏಕೆಂದರೆ ಹಲವಾರು ಇತರ ಅಗತ್ಯತೆಗಳಿವೆ - ನಮ್ಮ ಕೋಡ್‌ನ ಭಾಗವು ವಿವಿಧ ವಿಶ್ಲೇಷಣಾತ್ಮಕ ಲೆಕ್ಕಾಚಾರಗಳಿಗೆ ಉದ್ದೇಶಿಸಲಾಗಿದೆ, ಇದು ಆರ್ಕೆಸ್ಟ್ರೇಶನ್‌ನೊಂದಿಗೆ ಸರ್ವರ್‌ಗಳಲ್ಲಿ ಮಾತ್ರವಲ್ಲದೆ ವಿವಿಧ ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳೊಂದಿಗೆ ಮತ್ತು ಕೆಲಸ ಮಾಡಬೇಕಾದ ವಿಶ್ಲೇಷಕರ ಕಂಪ್ಯೂಟರ್‌ಗಳಲ್ಲಿಯೂ ಚಲಿಸುತ್ತದೆ. ಮತ್ತು ಬ್ಯಾಕೆಂಡ್ ಸಮಸ್ಯೆಗಳನ್ನು ಚಿಂತಿಸದೆ ಸಂಕೀರ್ಣ ಡೇಟಾ ಸಂಸ್ಕರಣಾ ಪೈಪ್‌ಲೈನ್‌ಗಳನ್ನು ಡೀಬಗ್ ಮಾಡಿ. ಅಂದಹಾಗೆ, ಕಾನ್ಫಿಗರ್ ಲೇಔಟ್ ಕೋಡ್ ಸೇರಿದಂತೆ ನಮ್ಮ ಮುಖ್ಯ ಪರಿಕರಗಳನ್ನು ಈ ಮೂಲಕ ಸ್ಥಾಪಿಸಲಾಗಿದೆ ಎಂದು ಹಂಚಿಕೊಳ್ಳಲು ತೊಂದರೆಯಾಗುವುದಿಲ್ಲ setup.py - ಒಟ್ಟಾಗಿ ಇದು ನಮ್ಮ ಕೋಡ್ ಅನ್ನು ಒಂದೇ ಪರಿಸರ ವ್ಯವಸ್ಥೆಗೆ ಒಂದುಗೂಡಿಸುತ್ತದೆ, ವೇದಿಕೆ ಮತ್ತು ಬಳಕೆಯ ವಿಧಾನದಿಂದ ಸ್ವತಂತ್ರವಾಗಿದೆ.

ಕುಬರ್ನೆಟ್ಸ್ ಪಾಡ್ನ ವಿವರಣೆಯು ಈ ರೀತಿ ಕಾಣುತ್ತದೆ:

containers:
  - name : enter-api
    image: enter-api:latest
    ports:
      - containerPort: 80
    volumeMounts:
      - name: db-main-secret-volume
        mountPath: /etc/secrets/db-main

volumes:
  - name: db-main-secret-volume
    secret:
      secretName: db-main-secret

ಅಂದರೆ, ಪ್ರತಿಯೊಂದು ರಹಸ್ಯವು ಒಂದು ವಿಭಾಗವನ್ನು ವಿವರಿಸುತ್ತದೆ. ರಹಸ್ಯಗಳನ್ನು ಈ ರೀತಿ ರಚಿಸಲಾಗಿದೆ:

apiVersion: v1
kind: Secret
metadata:
  name: db-main-secret
type: Opaque
stringData:
  db_main.yaml: |
    engine: sqlite
    filename: main.sqlite3

ಒಟ್ಟಿನಲ್ಲಿ ಇದು ಹಾದಿಯಲ್ಲಿ YAML ಫೈಲ್‌ಗಳ ರಚನೆಗೆ ಕಾರಣವಾಗುತ್ತದೆ /etc/secrets/db-main/section_name.yaml

ಮತ್ತು ಸ್ಥಳೀಯ ಉಡಾವಣೆಗಳಿಗಾಗಿ, ಸಂರಚನೆಯನ್ನು ಬಳಸಲಾಗುತ್ತದೆ, ಇದು ಯೋಜನೆಯ ಮೂಲ ಡೈರೆಕ್ಟರಿಯಲ್ಲಿ ಅಥವಾ ಪರಿಸರ ವೇರಿಯಬಲ್‌ನಲ್ಲಿ ನಿರ್ದಿಷ್ಟಪಡಿಸಿದ ಹಾದಿಯಲ್ಲಿದೆ. ಈ ಅನುಕೂಲಗಳಿಗೆ ಕಾರಣವಾದ ಕೋಡ್ ಅನ್ನು ಸ್ಪಾಯ್ಲರ್‌ನಲ್ಲಿ ಕಾಣಬಹುದು.

config.py

__author__ = 'AivanF'
__copyright__ = 'Copyright 2020, AivanF'

import os
import yaml

__all__ = ['config']
PROJECT_DIR = os.path.abspath(__file__ + 3 * '/..')
SECRETS_DIR = '/etc/secrets'
KEY_LOG = '_config_log'
KEY_DBG = 'debug'

def is_yes(value):
    if isinstance(value, str):
        value = value.lower()
        if value in ('1', 'on', 'yes', 'true'):
            return True
    else:
        if value in (1, True):
            return True
    return False

def update_config_part(config, key, data):
    if key not in config:
        config[key] = data
    else:
        config[key].update(data)

def parse_big_config(config, filename):
    '''
    Parse YAML config with multiple section
    '''
    if not os.path.isfile(filename):
        return False
    with open(filename) as f:
        config_new = yaml.safe_load(f.read())
        for key, data in config_new.items():
            update_config_part(config, key, data)
        config[KEY_LOG].append(filename)
        return True

def parse_tiny_config(config, key, filename):
    '''
    Parse YAML config with a single section
    '''
    with open(filename) as f:
        config_tiny = yaml.safe_load(f.read())
        update_config_part(config, key, config_tiny)
        config[KEY_LOG].append(filename)

def combine_config():
    config = {
        # To debug config load code
        KEY_LOG: [],
        # To debug other code
        KEY_DBG: is_yes(os.environ.get('DEBUG')),
    }
    # For simple local runs
    CONFIG_SIMPLE = os.path.join(PROJECT_DIR, 'config.yaml')
    parse_big_config(config, CONFIG_SIMPLE)
    # For container's tests
    CONFIG_ENVVAR = os.environ.get('CONFIG')
    if CONFIG_ENVVAR is not None:
        if not parse_big_config(config, CONFIG_ENVVAR):
            raise ValueError(
                f'No config file from EnvVar:n'
                f'{CONFIG_ENVVAR}'
            )
    # For K8s secrets
    for path, dirs, files in os.walk(SECRETS_DIR):
        depth = path[len(SECRETS_DIR):].count(os.sep)
        if depth > 1:
            continue
        for file in files:
            if file.endswith('.yaml'):
                filename = os.path.join(path, file)
                key = file.rsplit('.', 1)[0]
                parse_tiny_config(config, key, filename)
    return config

def build_config():
    config = combine_config()
    # Preprocess
    for key, data in config.items():
        if key.startswith('db_'):
            if data['engine'] == 'sqlite':
                data['filename'] = os.path.join(PROJECT_DIR, data['filename'])
    # To verify correctness
    if config[KEY_DBG]:
        print(f'** Loaded config:n{yaml.dump(config)}')
    else:
        print(f'** Loaded config from: {config[KEY_LOG]}')
    return config

config = build_config()

ಇಲ್ಲಿ ತರ್ಕವು ತುಂಬಾ ಸರಳವಾಗಿದೆ: ನಾವು ಪ್ರಾಜೆಕ್ಟ್ ಡೈರೆಕ್ಟರಿಯಿಂದ ದೊಡ್ಡ ಸಂರಚನೆಗಳನ್ನು ಮತ್ತು ಪರಿಸರ ವೇರಿಯಬಲ್ ಮೂಲಕ ಮಾರ್ಗಗಳನ್ನು ಮತ್ತು ಕುಬರ್ ರಹಸ್ಯಗಳಿಂದ ಸಣ್ಣ ಸಂರಚನಾ ವಿಭಾಗಗಳನ್ನು ಸಂಯೋಜಿಸುತ್ತೇವೆ ಮತ್ತು ನಂತರ ಅವುಗಳನ್ನು ಸ್ವಲ್ಪ ಪೂರ್ವಪ್ರಕ್ರಿಯೆ ಮಾಡುತ್ತೇವೆ. ಜೊತೆಗೆ ಕೆಲವು ಅಸ್ಥಿರ. ರಹಸ್ಯಗಳಿಂದ ಫೈಲ್‌ಗಳನ್ನು ಹುಡುಕುವಾಗ, ಆಳದ ಮಿತಿಯನ್ನು ಬಳಸಲಾಗುತ್ತದೆ ಎಂದು ನಾನು ಗಮನಿಸುತ್ತೇನೆ, ಏಕೆಂದರೆ K8 ಗಳು ಪ್ರತಿಯೊಂದು ರಹಸ್ಯದಲ್ಲಿ ಗುಪ್ತ ಫೋಲ್ಡರ್ ಅನ್ನು ರಚಿಸುತ್ತದೆ, ಅಲ್ಲಿ ರಹಸ್ಯಗಳನ್ನು ಸ್ವತಃ ಸಂಗ್ರಹಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಕೇವಲ ಒಂದು ಲಿಂಕ್ ಉನ್ನತ ಮಟ್ಟದಲ್ಲಿದೆ.

ವಿವರಿಸಿರುವುದು ಯಾರಿಗಾದರೂ ಉಪಯುಕ್ತವಾಗಿದೆ ಎಂದು ನಾನು ಭಾವಿಸುತ್ತೇನೆ :) ಭದ್ರತೆ ಅಥವಾ ಸುಧಾರಣೆಗಾಗಿ ಇತರ ಕ್ಷೇತ್ರಗಳಿಗೆ ಸಂಬಂಧಿಸಿದ ಯಾವುದೇ ಕಾಮೆಂಟ್‌ಗಳು ಮತ್ತು ಶಿಫಾರಸುಗಳನ್ನು ಸ್ವೀಕರಿಸಲಾಗುತ್ತದೆ. ಸಮುದಾಯದ ಅಭಿಪ್ರಾಯವೂ ಆಸಕ್ತಿದಾಯಕವಾಗಿದೆ, ಬಹುಶಃ ಕಾನ್ಫಿಗ್‌ಮ್ಯಾಪ್‌ಗಳಿಗೆ ಬೆಂಬಲವನ್ನು ಸೇರಿಸುವುದು ಯೋಗ್ಯವಾಗಿದೆ (ನಮ್ಮ ಯೋಜನೆಯು ಅವುಗಳನ್ನು ಇನ್ನೂ ಬಳಸುವುದಿಲ್ಲ) ಮತ್ತು GitHub / PyPI ನಲ್ಲಿ ಕೋಡ್ ಅನ್ನು ಪ್ರಕಟಿಸುವುದೇ? ವೈಯಕ್ತಿಕವಾಗಿ, ಯೋಜನೆಗಳು ಸಾರ್ವತ್ರಿಕವಾಗಲು ಅಂತಹ ವಿಷಯಗಳು ತುಂಬಾ ವೈಯಕ್ತಿಕವಾಗಿವೆ ಎಂದು ನಾನು ಭಾವಿಸುತ್ತೇನೆ ಮತ್ತು ಇಲ್ಲಿ ನೀಡಲಾದ ಇತರ ಜನರ ಅನುಷ್ಠಾನಗಳನ್ನು ಸ್ವಲ್ಪ ಇಣುಕಿ ನೋಡುವುದು ಮತ್ತು ಸೂಕ್ಷ್ಮ ವ್ಯತ್ಯಾಸಗಳು, ಸಲಹೆಗಳು ಮತ್ತು ಉತ್ತಮ ಅಭ್ಯಾಸಗಳ ಚರ್ಚೆ, ನಾನು ಕಾಮೆಂಟ್‌ಗಳಲ್ಲಿ ನೋಡಲು ಆಶಿಸುತ್ತೇನೆ. , ಸಾಕು 😉

ನೋಂದಾಯಿತ ಬಳಕೆದಾರರು ಮಾತ್ರ ಸಮೀಕ್ಷೆಯಲ್ಲಿ ಭಾಗವಹಿಸಬಹುದು. ಸೈನ್ ಇನ್ ಮಾಡಿ, ದಯವಿಟ್ಟು.

ನಾನು ಪ್ರಾಜೆಕ್ಟ್/ಲೈಬ್ರರಿಯಾಗಿ ಪ್ರಕಟಿಸಬೇಕೇ?

  • 0,0%ಹೌದು, ನಾನು /ಕೊಡುಗೆ0 ಅನ್ನು ಬಳಸುತ್ತೇನೆ

  • 33,3%ಹೌದು, ಅದು ಅದ್ಭುತವಾಗಿದೆ4

  • 41,7%ಇಲ್ಲ, ಯಾರು ಅದನ್ನು ತಮ್ಮ ಸ್ವಂತ ಸ್ವರೂಪದಲ್ಲಿ ಮತ್ತು ಅವರ ಅಗತ್ಯಗಳಿಗೆ ತಕ್ಕಂತೆ ಮಾಡಬೇಕು5

  • 25,0%ನಾನು ಉತ್ತರಿಸುವುದನ್ನು ತಡೆಯುತ್ತೇನೆ3

12 ಬಳಕೆದಾರರು ಮತ ಹಾಕಿದ್ದಾರೆ. 3 ಬಳಕೆದಾರರು ದೂರ ಉಳಿದಿದ್ದಾರೆ.

ಮೂಲ: www.habr.com

ಕಾಮೆಂಟ್ ಅನ್ನು ಸೇರಿಸಿ