diff --git a/api/src/gmsa_service.cpp b/api/src/gmsa_service.cpp index bf1f20a4..733a75de 100644 --- a/api/src/gmsa_service.cpp +++ b/api/src/gmsa_service.cpp @@ -84,6 +84,149 @@ class CredentialsFetcherImpl final } private: + // Class encompasing the state and logic needed to serve a request. + class CallDataHealthCheck + { + public: + std::string cookie; + +#define CLASS_NAME_CallDataHealthCheck "CallDataHealthCheck" + // Take in the "service" instance (in this case representing an asynchronous + // server) and the completion queue "cq" used for asynchronous communication + // with the gRPC runtime. + CallDataHealthCheck( + credentialsfetcher::CredentialsFetcherService::AsyncService* service, + grpc::ServerCompletionQueue* cq ) + : service_( service ) + , cq_( cq ) + , health_check_responder_( &health_check_ctx_ ) + , status_( CREATE ) + { + cookie = CLASS_NAME_CallDataHealthCheck; + // Invoke the serving logic right away. + Proceed(); + } + + void Proceed(creds_fetcher::CF_logger& cf_logger) + { + if ( cookie.compare( CLASS_NAME_CallDataHealthCheck ) != 0 ) + { + return; + } + printf( "CallDataHealthCheck %p status: %d\n", this, status_ ); + if ( status_ == CREATE ) + { + // Make this instance progress to the PROCESS state. + status_ = PROCESS; + + // As part of the initial CREATE state, we *request* that the system + // start processing RequestHealthCheck requests. In this request, "this" acts + // are the tag uniquely identifying the request (so that different CallData + // instances can serve different requests concurrently), in this case + // the memory address of this CallData instance. + + service_->RequestHealthCheck( &health_check_ctx_, &health_check_request_, + &health_check_responder_, cq_, cq_, this ); + } + else if ( status_ == PROCESS ) + { + // Spawn a new CallData instance to serve new clients while we process + // the one for this CallData. The instance will deallocate itself as + // part of its FINISH state. + new CallDataHealthCheck( service_, cq_ ); + + // The actual processing. + health_check_reply_.set_status( "OK" ); + status_ = FINISH; + health_check_responder_.Finish( health_check_reply_, grpc::Status::OK, this ); + + } + else + { + GPR_ASSERT( status_ == FINISH ); + // Once in the FINISH state, deallocate ourselves (CallData). + delete this; + } + + return; + } + void Proceed() + { + if ( cookie.compare( CLASS_NAME_CallDataHealthCheck ) != 0 ) + { + return; + } + printf( "CallHealthCheck %p status: %d\n", this, status_ ); + if ( status_ == CREATE ) + { + // Make this instance progress to the PROCESS state. + status_ = PROCESS; + + // As part of the initial CREATE state, we *request* that the system + // start processing RequestHealthCheck requests. In this request, "this" acts + // are the tag uniquely identifying the request (so that different CallData + // instances can serve different requests concurrently), in this case + // the memory address of this CallData instance. + + service_->RequestHealthCheck( &health_check_ctx_, &health_check_request_, + &health_check_responder_, cq_, cq_, this ); + } + else if ( status_ == PROCESS ) + { + // Spawn a new CallData instance to serve new clients while we process + // the one for this CallData. The instance will deallocate itself as + // part of its FINISH state. + new CallDataHealthCheck( service_, cq_ ); + + // The actual processing. + health_check_reply_.set_status( "OK" ); + + // And we are done! Let the gRPC runtime know we've finished, using the + // memory address of this instance as the uniquely identifying tag for + // the event. + status_ = FINISH; + health_check_responder_.Finish( health_check_reply_, grpc::Status::OK, this ); + } + else + { + GPR_ASSERT( status_ == FINISH ); + // Once in the FINISH state, deallocate ourselves (CallData). + delete this; + } + + return; + } + + private: + // The means of communication with the gRPC runtime for an asynchronous + // server. + credentialsfetcher::CredentialsFetcherService::AsyncService* service_; + // The producer-consumer queue where for asynchronous server notifications. + grpc::ServerCompletionQueue* cq_; + // Context for the rpc, allowing to tweak aspects of it such as the use + // of compression, authentication, as well as to send metadata back to the + // client. + grpc::ServerContext health_check_ctx_; + + // What we get from the client. + credentialsfetcher::HealthCheckRequest health_check_request_; + // What we send back to the client. + credentialsfetcher::HealthCheckResponse health_check_reply_; + + // The means to get back to the client. + grpc::ServerAsyncResponseWriter + health_check_responder_; + + // Let's implement a tiny state machine with the following states. + enum CallStatus + { + CREATE, + PROCESS, + FINISH + }; + CallStatus status_; // The current serving state. + }; + // Class encompasing the state and logic needed to serve a request. class CallDataCreateKerberosLease { @@ -1024,6 +1167,7 @@ class CredentialsFetcherImpl final new CallDataAddNonDomainJoinedKerberosLease ( &service_, cq_.get() ); new CallDataRenewNonDomainJoinedKerberosLease ( &service_, cq_.get() ); new CallDataDeleteKerberosLease( &service_, cq_.get() ); + new CallDataHealthCheck( &service_, cq_.get() ); while ( pthread_shutdown_signal != nullptr && !( *pthread_shutdown_signal ) ) { @@ -1046,6 +1190,7 @@ class CredentialsFetcherImpl final aws_sm_secret_name ); static_cast( got_tag )->Proceed( krb_files_dir, cf_logger, aws_sm_secret_name ); + static_cast( got_tag )->Proceed( cf_logger); } } diff --git a/api/tests/gmsa_test_client.cpp b/api/tests/gmsa_test_client.cpp index d5aac3b1..e9786e7c 100644 --- a/api/tests/gmsa_test_client.cpp +++ b/api/tests/gmsa_test_client.cpp @@ -36,6 +36,38 @@ class CredentialsFetcherClient { } + /** + * Health check method + * @return + */ + std::string HealthCheckMethod( std::string service_name ) + { + std::string result; + // Prepare request + credentialsfetcher::HealthCheckRequest request; + request.set_service( service_name ); + + credentialsfetcher::HealthCheckResponse response; + grpc::ClientContext context; + grpc::Status status; + + // Send request + status = _stub->HealthCheck( &context, request, &response ); + + // Handle response + if ( status.ok() ) + { + return response.status(); + } + else + { + std::cerr << status.error_code() << ": " << status.error_message() << std::endl; + return response.status(); + } + } + + + /** * Test method to create kerberos tickets * @param credspec_contents - information of service account @@ -235,6 +267,7 @@ static void show_usage( std::string name ) std::cout << "Usage: " << name << " SOURCES" << "Options:\n" << "\t-h,--help\t\tShow this help message\n" + << "\t --check \t\thealth check of daemon\n" << "\t-no option\t\tcreate & delete kerberos tickets\n" << "\t --create \t\tcreate krb tickets for service account\n" << "\t --delete \t\tdelete krb tickets for a given lease_id\tprovide lease_id to be " @@ -249,6 +282,17 @@ static void show_usage( std::string name ) << std::endl; } +// health check daemon +std::string health_check( + CredentialsFetcherClient& client) +{ + std::string health_check_response = + client.HealthCheckMethod("cfservice"); + std::cout << "Client received output for health check: " + << health_check_response << std::endl; + return health_check_response; +} + // create kerberos tickets std::pair> create_krb_ticket( CredentialsFetcherClient& client, std::list credspec_contents ) @@ -438,6 +482,11 @@ int main( int argc, char** argv ) show_usage( argv[0] ); return 0; } + else if ( arg == "--check" ) + { + health_check(client); + return 0; + } else if ( arg == "--delete" ) { if ( i + 1 < argc ) diff --git a/protos/credentialsfetcher.proto b/protos/credentialsfetcher.proto index e0a694c1..627bda95 100644 --- a/protos/credentialsfetcher.proto +++ b/protos/credentialsfetcher.proto @@ -10,6 +10,15 @@ service CredentialsFetcherService { rpc RenewNonDomainJoinedKerberosLease (RenewNonDomainJoinedKerberosLeaseRequest) returns (RenewNonDomainJoinedKerberosLeaseResponse); rpc DeleteKerberosLease (DeleteKerberosLeaseRequest) returns (DeleteKerberosLeaseResponse); + rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse); +} + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + string status = 1; } message CreateKerberosLeaseRequest {